adjust directory 2

This commit is contained in:
JamesonHuang 2015-06-28 14:39:56 +08:00
parent b09950bf62
commit 4039994946
1767 changed files with 503365 additions and 0 deletions

113
1_1.mi_Lua/Posix.lua Normal file
View File

@ -0,0 +1,113 @@
local base = _G
local string = require("string")
local M = require "posix"
function M.timeradd (x,y)
local sec, usec = 0, 0
if x.sec then sec = sec + x.sec end
if y.sec then sec = sec + y.sec end
if x.usec then usec = usec + x.usec end
if y.usec then usec = usec + y.usec end
if usec > 1000000 then
sec = sec + 1
usec = usec - 1000000
end
return { sec = sec, usec = usec }
end
function M.timercmp (x, y)
local x = { sec = x.sec or 0, usec = x.usec or 0 }
local y = { sec = y.sec or 0, usec = y.usec or 0 }
if x.sec ~= y.sec then
return x.sec - y.sec
else
return x.usec - y.usec
end
end
function M.timersub (x,y)
local sec, usec = 0, 0
if x.sec then sec = x.sec end
if y.sec then sec = sec - y.sec end
if x.usec then usec = x.usec end
if y.usec then usec = usec - y.usec end
if usec < 0 then
sec = sec - 1
usec = usec + 1000000
end
return { sec = sec, usec = usec }
end
function M.timesleep (x)
local sec, nsec = 0, 0
y = M.gettimeofday();
if( M.timercmp(x, y) > 0 ) then
sec = x.sec - y.sec
nsec = (x.usec - y.usec) * 1000
if nsec < 0 then
sec = sec - 1
nsec = nsec + 1000000000
end
M.nanosleep(sec, nsec)
end
end
function M.strsplit(str, delim, maxNb)
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
if maxNb == nil or maxNb < 1 then
maxNb = 0 -- No limit
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gfind(str, pat) do
nb = nb + 1
result[nb] = part
lastPos = pos
if nb == maxNb then break end
end
-- Handle the last field
if nb ~= maxNb then
result[nb + 1] = string.sub(str, lastPos)
end
return result
end
function M.var_dump(data, max_level, prefix)
if type(prefix) ~= "string" then
prefix = ""
end
if type(data) ~= "table" then
print(prefix .. tostring(data))
else
print(data)
if max_level ~= 0 then
local prefix_next = prefix .. " "
print(prefix .. "{")
for k,v in pairs(data) do
io.stdout:write(prefix_next .. k .. " = ")
if type(v) ~= "table" or (type(max_level) == "number" and max_level <= 1) then
print(v)
else
if max_level == nil then
M.var_dump(v, nil, prefix_next)
else
M.var_dump(v, max_level - 1, prefix_next)
end
end
end
print(prefix .. "}")
end
end
end
return M

15
1_1.mi_Lua/bit.lua Normal file
View File

@ -0,0 +1,15 @@
--[[
nixio - Linux I/O library for lua
Copyright 2009 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
return require "nixio".bit

BIN
1_1.mi_Lua/cjson.so Normal file

Binary file not shown.

BIN
1_1.mi_Lua/iproute.so Normal file

Binary file not shown.

BIN
1_1.mi_Lua/iwinfo.so Normal file

Binary file not shown.

376
1_1.mi_Lua/json.lua Normal file
View File

@ -0,0 +1,376 @@
-----------------------------------------------------------------------------
-- JSON4Lua: JSON encoding / decoding support for the Lua language.
-- json Module.
-- Author: Craig Mason-Jones
-- Homepage: http://json.luaforge.net/
-- Version: 0.9.40
-- This module is released under the MIT License (MIT).
-- Please see LICENCE.txt for details.
--
-- USAGE:
-- This module exposes two functions:
-- encode(o)
-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string.
-- decode(json_string)
-- Returns a Lua object populated with the data encoded in the JSON string json_string.
--
-- REQUIREMENTS:
-- compat-5.1 if using Lua 5.0
--
-- CHANGELOG
-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix).
-- Fixed Lua 5.1 compatibility issues.
-- Introduced json.null to have null values in associative arrays.
-- encode() performance improvement (more than 50%) through table.concat rather than ..
-- Introduced decode ability to ignore /**/ comments in the JSON string.
-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays.
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Imports and dependencies
-----------------------------------------------------------------------------
local math = require('math')
local string = require("string")
local table = require("table")
local base = _G
-----------------------------------------------------------------------------
-- Module declaration
-----------------------------------------------------------------------------
module("json")
-- Public functions
-- Private functions
local decode_scanArray
local decode_scanComment
local decode_scanConstant
local decode_scanNumber
local decode_scanObject
local decode_scanString
local decode_scanWhitespace
local encodeString
local isArray
local isEncodable
-----------------------------------------------------------------------------
-- PUBLIC FUNCTIONS
-----------------------------------------------------------------------------
--- Encodes an arbitrary Lua object / variable.
-- @param v The Lua object / variable to be JSON encoded.
-- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode)
function encode (v)
-- Handle nil values
if v==nil then
return "null"
end
local vtype = base.type(v)
-- Handle strings
if vtype=='string' then
return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string
end
-- Handle booleans
if vtype=='number' or vtype=='boolean' then
return base.tostring(v)
end
-- Handle tables
if vtype=='table' then
local rval = {}
-- Consider arrays separately
local bArray, maxCount = isArray(v)
if bArray then
for i = 1,maxCount do
table.insert(rval, encode(v[i]))
end
else -- An object, not an array
for i,j in base.pairs(v) do
if isEncodable(i) and isEncodable(j) then
table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j))
end
end
end
if bArray then
return '[' .. table.concat(rval,',') ..']'
else
return '{' .. table.concat(rval,',') .. '}'
end
end
-- Handle null values
if vtype=='function' and v==null then
return 'null'
end
base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
end
--- Decodes a JSON string and returns the decoded value as a Lua data structure / value.
-- @param s The string to scan.
-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1.
-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil,
-- and the position of the first character after
-- the scanned JSON object.
function decode(s, startPos)
startPos = startPos and startPos or 1
startPos = decode_scanWhitespace(s,startPos)
base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
local curChar = string.sub(s,startPos,startPos)
-- Object
if curChar=='{' then
return decode_scanObject(s,startPos)
end
-- Array
if curChar=='[' then
return decode_scanArray(s,startPos)
end
-- Number
if string.find("+-0123456789.e", curChar, 1, true) then
return decode_scanNumber(s,startPos)
end
-- String
if curChar==[["]] or curChar==[[']] then
return decode_scanString(s,startPos)
end
if string.sub(s,startPos,startPos+1)=='/*' then
return decode(s, decode_scanComment(s,startPos))
end
-- Otherwise, it must be a constant
return decode_scanConstant(s,startPos)
end
--- The null function allows one to specify a null value in an associative array (which is otherwise
-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }
function null()
return null -- so json.null() will also return null ;-)
end
-----------------------------------------------------------------------------
-- Internal, PRIVATE functions.
-- Following a Python-like convention, I have prefixed all these 'PRIVATE'
-- functions with an underscore.
-----------------------------------------------------------------------------
--- Scans an array from JSON into a Lua object
-- startPos begins at the start of the array.
-- Returns the array and the next starting position
-- @param s The string being scanned.
-- @param startPos The starting position for the scan.
-- @return table, int The scanned array as a table, and the position of the next character to scan.
function decode_scanArray(s,startPos)
local array = {} -- The return value
local stringLen = string.len(s)
base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
startPos = startPos + 1
-- Infinite loop for array elements
repeat
startPos = decode_scanWhitespace(s,startPos)
base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
local curChar = string.sub(s,startPos,startPos)
if (curChar==']') then
return array, startPos+1
end
if (curChar==',') then
startPos = decode_scanWhitespace(s,startPos+1)
end
base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
object, startPos = decode(s,startPos)
table.insert(array,object)
until false
end
--- Scans a comment and discards the comment.
-- Returns the position of the next character following the comment.
-- @param string s The JSON string to scan.
-- @param int startPos The starting position of the comment
function decode_scanComment(s, startPos)
base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos)
local endPos = string.find(s,'*/',startPos+2)
base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
return endPos+2
end
--- Scans for given constants: true, false or null
-- Returns the appropriate Lua type, and the position of the next character to read.
-- @param s The string being scanned.
-- @param startPos The position in the string at which to start scanning.
-- @return object, int The object (true, false or nil) and the position at which the next character should be
-- scanned.
function decode_scanConstant(s, startPos)
local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
local constNames = {"true","false","null"}
for i,k in base.pairs(constNames) do
--print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
if string.sub(s,startPos, startPos + string.len(k) -1 )==k then
return consts[k], startPos + string.len(k)
end
end
base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
end
--- Scans a number from the JSON encoded string.
-- (in fact, also is able to scan numeric +- eqns, which is not
-- in the JSON spec.)
-- Returns the number, and the position of the next character
-- after the number.
-- @param s The string being scanned.
-- @param startPos The position at which to start scanning.
-- @return number, int The extracted number and the position of the next character to scan.
function decode_scanNumber(s,startPos)
local endPos = startPos+1
local stringLen = string.len(s)
local acceptableChars = "+-0123456789.e"
while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
and endPos<=stringLen
) do
endPos = endPos + 1
end
local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
local stringEval = base.loadstring(stringValue)
base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
return stringEval(), endPos
end
--- Scans a JSON object into a Lua object.
-- startPos begins at the start of the object.
-- Returns the object and the next starting position.
-- @param s The string being scanned.
-- @param startPos The starting position of the scan.
-- @return table, int The scanned object as a table and the position of the next character to scan.
function decode_scanObject(s,startPos)
local object = {}
local stringLen = string.len(s)
local key, value
base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
startPos = startPos + 1
repeat
startPos = decode_scanWhitespace(s,startPos)
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
local curChar = string.sub(s,startPos,startPos)
if (curChar=='}') then
return object,startPos+1
end
if (curChar==',') then
startPos = decode_scanWhitespace(s,startPos+1)
end
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
-- Scan the key
key, startPos = decode(s,startPos)
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
startPos = decode_scanWhitespace(s,startPos)
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)
startPos = decode_scanWhitespace(s,startPos+1)
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
value, startPos = decode(s,startPos)
object[key]=value
until false -- infinite loop while key-value pairs are found
end
--- Scans a JSON string from the opening inverted comma or single quote to the
-- end of the string.
-- Returns the string extracted as a Lua string,
-- and the position of the next non-string character
-- (after the closing inverted comma or single quote).
-- @param s The string being scanned.
-- @param startPos The starting position of the scan.
-- @return string, int The extracted string as a Lua string, and the next character to parse.
function decode_scanString(s,startPos)
base.assert(startPos, 'decode_scanString(..) called without start position')
local startChar = string.sub(s,startPos,startPos)
base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
local escaped = false
local endPos = startPos + 1
local bEnded = false
local stringLen = string.len(s)
repeat
local curChar = string.sub(s,endPos,endPos)
-- Character escaping is only used to escape the string delimiters
if not escaped then
if curChar==[[\]] then
escaped = true
else
bEnded = curChar==startChar
end
else
-- If we're escaped, we accept the current character come what may
escaped = false
end
endPos = endPos + 1
base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos)
until bEnded
local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
local stringEval = base.loadstring(stringValue)
base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
return stringEval(), endPos
end
--- Scans a JSON string skipping all whitespace from the current start position.
-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached.
-- @param s The string being scanned
-- @param startPos The starting position where we should begin removing whitespace.
-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string
-- was reached.
function decode_scanWhitespace(s,startPos)
local whitespace=" \n\r\t"
local stringLen = string.len(s)
while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do
startPos = startPos + 1
end
return startPos
end
--- Encodes a string to be JSON-compatible.
-- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-)
-- @param s The string to return as a JSON encoded (i.e. backquoted string)
-- @return The string appropriately escaped.
function encodeString(s)
s = string.gsub(s,'\\','\\\\')
s = string.gsub(s,'"','\\"')
s = string.gsub(s,"'","\\'")
s = string.gsub(s,'\n','\\n')
s = string.gsub(s,'\t','\\t')
return s
end
-- Determines whether the given Lua type is an array or a table / dictionary.
-- We consider any table an array if it has indexes 1..n for its n items, and no
-- other data in the table.
-- I think this method is currently a little 'flaky', but can't think of a good way around it yet...
-- @param t The table to evaluate as an array
-- @return boolean, number True if the table can be represented as an array, false otherwise. If true,
-- the second returned value is the maximum
-- number of indexed elements in the array.
function isArray(t)
-- Next we count all the elements, ensuring that any non-indexed elements are not-encodable
-- (with the possible exception of 'n')
local maxIndex = 0
for k,v in base.pairs(t) do
if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair
if (not isEncodable(v)) then return false end -- All array elements must be encodable
maxIndex = math.max(maxIndex,k)
else
if (k=='n') then
if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements
else -- Else of (k=='n')
if isEncodable(v) then return false end
end -- End of (k~='n')
end -- End of k,v not an indexed pair
end -- End of loop across all pairs
return true, maxIndex
end
--- Determines whether the given Lua object / table / variable can be JSON encoded. The only
-- types that are JSON encodable are: string, boolean, number, nil, table and json.null.
-- In this implementation, all other types are ignored.
-- @param o The object to examine.
-- @return boolean True if the object should be JSON encoded, false if it should be ignored.
function isEncodable(o)
local t = base.type(o)
return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null)
end

201
1_1.mi_Lua/logging.lua Normal file
View File

@ -0,0 +1,201 @@
-------------------------------------------------------------------------------
-- includes a new tostring function that handles tables recursively
--
-- @author Danilo Tuler (tuler@ideais.com.br)
-- @author Andre Carregal (info@keplerproject.org)
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2013 Kepler Project
-------------------------------------------------------------------------------
local type, table, string, _tostring, tonumber = type, table, string, tostring, tonumber
local select = select
local error = error
local format = string.format
local pairs = pairs
local ipairs = ipairs
local logging = {
-- Meta information
_COPYRIGHT = "Copyright (C) 2004-2013 Kepler Project",
_DESCRIPTION = "A simple API to use logging features in Lua",
_VERSION = "LuaLogging 1.3.0",
-- The DEBUG Level designates fine-grained instring.formational events that are most
-- useful to debug an application
DEBUG = "DEBUG",
-- The INFO level designates instring.formational messages that highlight the
-- progress of the application at coarse-grained level
INFO = "INFO",
-- The WARN level designates potentially harmful situations
WARN = "WARN",
-- The ERROR level designates error events that might still allow the
-- application to continue running
ERROR = "ERROR",
-- The FATAL level designates very severe error events that will presumably
-- lead the application to abort
FATAL = "FATAL",
}
local LEVEL = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
local MAX_LEVELS = #LEVEL
-- make level names to order
for i=1,MAX_LEVELS do
LEVEL[LEVEL[i]] = i
end
-- private log function, with support for formating a complex log message.
local function LOG_MSG(self, level, fmt, ...)
local f_type = type(fmt)
if f_type == 'string' then
if select('#', ...) > 0 then
return self:append(level, format(fmt, ...))
else
-- only a single string, no formating needed.
return self:append(level, fmt)
end
elseif f_type == 'function' then
-- fmt should be a callable function which returns the message to log
return self:append(level, fmt(...))
end
-- fmt is not a string and not a function, just call tostring() on it.
return self:append(level, logging.tostring(fmt))
end
-- create the proxy functions for each log level.
local LEVEL_FUNCS = {}
for i=1,MAX_LEVELS do
local level = LEVEL[i]
LEVEL_FUNCS[i] = function(self, ...)
-- no level checking needed here, this function will only be called if it's level is active.
return LOG_MSG(self, level, ...)
end
end
-- do nothing function for disabled levels.
local function disable_level() end
-- improved assertion function.
local function assert(exp, ...)
-- if exp is true, we are finished so don't do any processing of the parameters
if exp then return exp, ... end
-- assertion failed, raise error
error(format(...), 2)
end
-------------------------------------------------------------------------------
-- Creates a new logger object
-- @param append Function used by the logger to append a message with a
-- log-level to the log stream.
-- @return Table representing the new logger object.
-------------------------------------------------------------------------------
function logging.new(append)
if type(append) ~= "function" then
return nil, "Appender must be a function."
end
local logger = {}
logger.append = append
logger.setLevel = function (self, level)
local order = LEVEL[level]
assert(order, "undefined level `%s'", _tostring(level))
if self.level then
self:log(logging.WARN, "Logger: changing loglevel from %s to %s", self.level, level)
end
self.level = level
self.level_order = order
-- enable/disable levels
for i=1,MAX_LEVELS do
local name = LEVEL[i]:lower()
if i >= order then
self[name] = LEVEL_FUNCS[i]
else
self[name] = disable_level
end
end
end
-- generic log function.
logger.log = function (self, level, ...)
local order = LEVEL[level]
assert(order, "undefined level `%s'", _tostring(level))
if order < self.level_order then
return
end
return LOG_MSG(self, level, ...)
end
-- initialize log level.
logger:setLevel(logging.DEBUG)
return logger
end
-------------------------------------------------------------------------------
-- Prepares the log message
-------------------------------------------------------------------------------
function logging.prepareLogMsg(pattern, dt, level, message)
local logMsg = pattern or "%date %level %message\n"
message = string.gsub(message, "%%", "%%%%")
logMsg = string.gsub(logMsg, "%%date", dt)
logMsg = string.gsub(logMsg, "%%level", level)
logMsg = string.gsub(logMsg, "%%message", message)
return logMsg
end
-------------------------------------------------------------------------------
-- Converts a Lua value to a string
--
-- Converts Table fields in alphabetical order
-------------------------------------------------------------------------------
local function tostring(value)
local str = ''
if (type(value) ~= 'table') then
if (type(value) == 'string') then
str = string.format("%q", value)
else
str = _tostring(value)
end
else
local auxTable = {}
for key in pairs(value) do
if (tonumber(key) ~= key) then
table.insert(auxTable, key)
else
table.insert(auxTable, tostring(key))
end
end
table.sort(auxTable)
str = str..'{'
local separator = ""
local entry = ""
for _, fieldName in ipairs(auxTable) do
if ((tonumber(fieldName)) and (tonumber(fieldName) > 0)) then
entry = tostring(value[tonumber(fieldName)])
else
entry = fieldName.." = "..tostring(value[fieldName])
end
str = str..separator..entry
separator = ", "
end
str = str..'}'
end
return str
end
logging.tostring = tostring
if _VERSION ~= 'Lua 5.2' then
-- still create 'logging' global for Lua versions < 5.2
_G.logging = logging
end
return logging

View File

@ -0,0 +1,20 @@
-------------------------------------------------------------------------------
-- Prints logging information to console
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2013 Kepler Project
--
-------------------------------------------------------------------------------
local logging = require"logging"
function logging.console(logPattern)
return logging.new( function(self, level, message)
io.stdout:write(logging.prepareLogMsg(logPattern, os.date(), level, message))
return true
end)
end
return logging.console

View File

@ -0,0 +1,43 @@
-------------------------------------------------------------------------------
-- Emails logging information to the given recipient
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2013 Kepler Project
--
-------------------------------------------------------------------------------
local logging = require"logging"
local smtp = require"socket.smtp"
function logging.email(params)
params = params or {}
params.headers = params.headers or {}
if params.from == nil then
return nil, "'from' parameter is required"
end
if params.rcpt == nil then
return nil, "'rcpt' parameter is required"
end
return logging.new( function(self, level, message)
local s = logging.prepareLogMsg(params.logPattern, os.date(), level, message)
if params.headers.subject then
params.headers.subject =
logging.prepareLogMsg(params.headers.subject, os.date(), level, message)
end
local msg = { headers = params.headers, body = s }
params.source = smtp.message(msg)
local r, e = smtp.send(params)
if not r then
return nil, e
end
return true
end)
end
return logging.email

View File

@ -0,0 +1,49 @@
-------------------------------------------------------------------------------
-- Saves logging information in a file
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2013 Kepler Project
--
-------------------------------------------------------------------------------
local logging = require"logging"
local lastFileNameDatePattern
local lastFileHandler
local openFileLogger = function (filename, datePattern)
local filename = string.format(filename, os.date(datePattern))
if (lastFileNameDatePattern ~= filename) then
local f = io.open(filename, "a")
if (f) then
f:setvbuf ("line")
lastFileNameDatePattern = filename
lastFileHandler = f
return f
else
return nil, string.format("file `%s' could not be opened for writing", filename)
end
else
return lastFileHandler
end
end
function logging.file(filename, datePattern, logPattern)
if type(filename) ~= "string" then
filename = "lualogging.log"
end
return logging.new( function(self, level, message)
local f, msg = openFileLogger(filename, datePattern)
if not f then
return nil, msg
end
local s = logging.prepareLogMsg(logPattern, os.date(), level, message)
f:write(s)
return true
end)
end
return logging.file

View File

@ -0,0 +1,79 @@
---------------------------------------------------------------------------
-- RollingFileAppender is a FileAppender that rolls over the logfile
-- once it has reached a certain size limit. It also mantains a
-- maximum number of log files.
--
-- @author Tiago Cesar Katcipis (tiagokatcipis@gmail.com)
--
-- @copyright 2004-2013 Kepler Project
---------------------------------------------------------------------------
local logging = require"logging"
local function openFile(self)
self.file = io.open(self.filename, "a")
if not self.file then
return nil, string.format("file `%s' could not be opened for writing", self.filename)
end
self.file:setvbuf ("line")
return self.file
end
local rollOver = function (self)
for i = self.maxIndex - 1, 1, -1 do
-- files may not exist yet, lets ignore the possible errors.
os.rename(self.filename.."."..i, self.filename.."."..i+1)
end
self.file:close()
self.file = nil
local _, msg = os.rename(self.filename, self.filename..".".."1")
if msg then
return nil, string.format("error %s on log rollover", msg)
end
return openFile(self)
end
local openRollingFileLogger = function (self)
if not self.file then
return openFile(self)
end
local filesize = self.file:seek("end", 0)
if (filesize < self.maxSize) then
return self.file
end
return rollOver(self)
end
function logging.rolling_file(filename, maxFileSize, maxBackupIndex, logPattern)
if type(filename) ~= "string" then
filename = "lualogging.log"
end
local obj = {
filename = filename,
maxSize = maxFileSize,
maxIndex = maxBackupIndex or 1
}
return logging.new( function(self, level, message)
local f, msg = openRollingFileLogger(obj)
if not f then
return nil, msg
end
local s = logging.prepareLogMsg(logPattern, os.date(), level, message)
f:write(s)
return true
end)
end
return logging.rolling_file

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- Sends the logging information through a socket using luasocket
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2013 Kepler Project
--
-------------------------------------------------------------------------------
local logging = require"logging"
local socket = require"socket"
function logging.socket(address, port, logPattern)
return logging.new( function(self, level, message)
local s = logging.prepareLogMsg(logPattern, os.date(), level, message)
local socket, err = socket.connect(address, port)
if not socket then
return nil, err
end
local cond, err = socket:send(s)
if not cond then
return nil, err
end
socket:close()
return true
end)
end
return logging.socket

View File

@ -0,0 +1,62 @@
-------------------------------------------------------------------------------
-- Saves the logging information in a table using luasql
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2013 Kepler Project
--
-------------------------------------------------------------------------------
local logging = require"logging"
function logging.sql(params)
params = params or {}
params.tablename = params.tablename or "LogTable"
params.logdatefield = params.logdatefield or "LogDate"
params.loglevelfield = params.loglevelfield or "LogLevel"
params.logmessagefield = params.logmessagefield or "LogMessage"
if params.connectionfactory == nil or type(params.connectionfactory) ~= "function" then
return nil, "No specified connection factory function"
end
local con, err
if params.keepalive then
con, err = params.connectionfactory()
end
return logging.new( function(self, level, message)
if (not params.keepalive) or (con == nil) then
con, err = params.connectionfactory()
if not con then
return nil, err
end
end
local logDate = os.date("%Y-%m-%d %H:%M:%S")
local insert = string.format("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
params.tablename, params.logdatefield, params.loglevelfield,
params.logmessagefield, logDate, level, string.gsub(message, "'", "''"))
local ret, err = pcall(con.execute, con, insert)
if not ret then
con, err = params.connectionfactory()
if not con then
return nil, err
end
ret, err = con:execute(insert)
if not ret then
return nil, err
end
end
if not params.keepalive then
con:close()
end
return true
end)
end
return logging.sql

BIN
1_1.mi_Lua/lsqlite3.so Normal file

Binary file not shown.

292
1_1.mi_Lua/ltn12.lua Normal file
View File

@ -0,0 +1,292 @@
-----------------------------------------------------------------------------
-- LTN12 - Filters, sources, sinks and pumps.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local base = _G
module("ltn12")
filter = {}
source = {}
sink = {}
pump = {}
-- 2048 seems to be better in windows...
BLOCKSIZE = 2048
_VERSION = "LTN12 1.0.1"
-----------------------------------------------------------------------------
-- Filter stuff
-----------------------------------------------------------------------------
-- returns a high level filter that cycles a low-level filter
function filter.cycle(low, ctx, extra)
base.assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
-- chains a bunch of filters together
-- (thanks to Wim Couwenberg)
function filter.chain(...)
local n = table.getn(arg)
local top, index = 1, 1
local retry = ""
return function(chunk)
retry = chunk and retry
while true do
if index == top then
chunk = arg[index](chunk)
if chunk == "" or top == n then return chunk
elseif chunk then index = index + 1
else
top = top+1
index = top
end
else
chunk = arg[index](chunk or "")
if chunk == "" then
index = index - 1
chunk = retry
elseif chunk then
if index == n then return chunk
else index = index + 1 end
else base.error("filter returned inappropriate nil") end
end
end
end
end
-----------------------------------------------------------------------------
-- Source stuff
-----------------------------------------------------------------------------
-- create an empty source
local function empty()
return nil
end
function source.empty()
return empty
end
-- returns a source that just outputs an error
function source.error(err)
return function()
return nil, err
end
end
-- creates a file source
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(BLOCKSIZE)
if not chunk then handle:close() end
return chunk
end
else return source.error(io_err or "unable to open file") end
end
-- turns a fancy source into a simple source
function source.simplify(src)
base.assert(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
-- creates string source
function source.string(s)
if s then
local i = 1
return function()
local chunk = string.sub(s, i, i+BLOCKSIZE-1)
i = i + BLOCKSIZE
if chunk ~= "" then return chunk
else return nil end
end
else return source.empty() end
end
-- creates rewindable source
function source.rewind(src)
base.assert(src)
local t = {}
return function(chunk)
if not chunk then
chunk = table.remove(t)
if not chunk then return src()
else return chunk end
else
table.insert(t, chunk)
end
end
end
function source.chain(src, f)
base.assert(src and f)
local last_in, last_out = "", ""
local state = "feeding"
local err
return function()
if not last_out then
base.error('source is empty!', 2)
end
while true do
if state == "feeding" then
last_in, err = src()
if err then return nil, err end
last_out = f(last_in)
if not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
elseif last_out ~= "" then
state = "eating"
if last_in then last_in = "" end
return last_out
end
else
last_out = f(last_in)
if last_out == "" then
if last_in == "" then
state = "feeding"
else
base.error('filter returned ""')
end
elseif not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
else
return last_out
end
end
end
end
end
-- creates a source that produces contents of several sources, one after the
-- other, as if they were concatenated
-- (thanks to Wim Couwenberg)
function source.cat(...)
local src = table.remove(arg, 1)
return function()
while src do
local chunk, err = src()
if chunk then return chunk end
if err then return nil, err end
src = table.remove(arg, 1)
end
end
end
-----------------------------------------------------------------------------
-- Sink stuff
-----------------------------------------------------------------------------
-- creates a sink that stores into a table
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
-- turns a fancy sink into a simple sink
function sink.simplify(snk)
base.assert(snk)
return function(chunk, err)
local ret, err_or_new = snk(chunk, err)
if not ret then return nil, err_or_new end
snk = err_or_new or snk
return 1
end
end
-- creates a file sink
function sink.file(handle, io_err)
if handle then
return function(chunk, err)
if not chunk then
handle:close()
return 1
else return handle:write(chunk) end
end
else return sink.error(io_err or "unable to open file") end
end
-- creates a sink that discards data
local function null()
return 1
end
function sink.null()
return null
end
-- creates a sink that just returns an error
function sink.error(err)
return function()
return nil, err
end
end
-- chains a sink with a filter
function sink.chain(f, snk)
base.assert(f and snk)
return function(chunk, err)
if chunk ~= "" then
local filtered = f(chunk)
local done = chunk and ""
while true do
local ret, snkerr = snk(filtered, err)
if not ret then return nil, snkerr end
if filtered == done then return 1 end
filtered = f(done)
end
else return 1 end
end
end
-----------------------------------------------------------------------------
-- Pump stuff
-----------------------------------------------------------------------------
-- pumps one chunk from the source to the sink
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1
else return nil, src_err or snk_err end
end
-- pumps all data from a source to a sink, using a step function
function pump.all(src, snk, step)
base.assert(src and snk)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then
if err then return nil, err
else return 1 end
end
end
end

View File

@ -0,0 +1,23 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local config = require "luci.config"
local ccache = require "luci.ccache"
module "luci.cacheloader"
if config.ccache and config.ccache.enable == "1" then
ccache.cache_ondemand()
end

1850
1_1.mi_Lua/luci/cbi.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,345 @@
--[[
LuCI - Configuration Bind Interface - Datatype Tests
(c) 2010 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: datatypes.lua 9352 2012-10-06 23:50:52Z jow $
]]--
local fs = require "nixio.fs"
local ip = require "luci.ip"
local math = require "math"
local util = require "luci.util"
local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
module "luci.cbi.datatypes"
_M['or'] = function(v, ...)
local i
for i = 1, select('#', ...), 2 do
local f = select(i, ...)
local a = select(i+1, ...)
if type(f) ~= "function" then
if f == v then
return true
end
i = i - 1
elseif f(v, unpack(a)) then
return true
end
end
return false
end
_M['and'] = function(v, ...)
local i
for i = 1, select('#', ...), 2 do
local f = select(i, ...)
local a = select(i+1, ...)
if type(f) ~= "function" then
if f ~= v then
return false
end
i = i - 1
elseif not f(v, unpack(a)) then
return false
end
end
return true
end
function neg(v, ...)
return _M['or'](v:gsub("^%s*!%s*", ""), ...)
end
function list(v, subvalidator, subargs)
if type(subvalidator) ~= "function" then
return false
end
local token
for token in v:gmatch("%S+") do
if not subvalidator(token, unpack(subargs)) then
return false
end
end
return true
end
function bool(val)
if val == "1" or val == "yes" or val == "on" or val == "true" then
return true
elseif val == "0" or val == "no" or val == "off" or val == "false" then
return true
elseif val == "" or val == nil then
return true
end
return false
end
function uinteger(val)
local n = tonumber(val)
if n ~= nil and math.floor(n) == n and n >= 0 then
return true
end
return false
end
function integer(val)
local n = tonumber(val)
if n ~= nil and math.floor(n) == n then
return true
end
return false
end
function ufloat(val)
local n = tonumber(val)
return ( n ~= nil and n >= 0 )
end
function float(val)
return ( tonumber(val) ~= nil )
end
function ipaddr(val)
return ip4addr(val) or ip6addr(val)
end
function ip4addr(val)
if val then
return ip.IPv4(val) and true or false
end
return false
end
function ip4prefix(val)
val = tonumber(val)
return ( val and val >= 0 and val <= 32 )
end
function ip6addr(val)
if val then
return ip.IPv6(val) and true or false
end
return false
end
function ip6prefix(val)
val = tonumber(val)
return ( val and val >= 0 and val <= 128 )
end
function port(val)
val = tonumber(val)
return ( val and val >= 0 and val <= 65535 )
end
function portrange(val)
local p1, p2 = val:match("^(%d+)%-(%d+)$")
if p1 and p2 and port(p1) and port(p2) then
return true
else
return port(val)
end
end
function macaddr(val)
if val and val:match(
"^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
"[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
) then
local parts = util.split( val, ":" )
for i = 1,6 do
parts[i] = tonumber( parts[i], 16 )
if parts[i] < 0 or parts[i] > 255 then
return false
end
end
return true
end
return false
end
function hostname(val)
if val and (#val < 254) and (
val:match("^[a-zA-Z_]+$") or
(val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
val:match("[^0-9%.]"))
) then
return true
end
return false
end
function host(val)
return hostname(val) or ipaddr(val)
end
function network(val)
return uciname(val) or host(val)
end
function wpakey(val)
if #val == 64 then
return (val:match("^[a-fA-F0-9]+$") ~= nil)
else
return (#val >= 8) and (#val <= 63)
end
end
function wepkey(val)
if val:sub(1, 2) == "s:" then
val = val:sub(3)
end
if (#val == 10) or (#val == 26) then
return (val:match("^[a-fA-F0-9]+$") ~= nil)
else
return (#val == 5) or (#val == 13)
end
end
function string(val)
return true -- Everything qualifies as valid string
end
function directory( val, seen )
local s = fs.stat(val)
seen = seen or { }
if s and not seen[s.ino] then
seen[s.ino] = true
if s.type == "dir" then
return true
elseif s.type == "lnk" then
return directory( fs.readlink(val), seen )
end
end
return false
end
function file( val, seen )
local s = fs.stat(val)
seen = seen or { }
if s and not seen[s.ino] then
seen[s.ino] = true
if s.type == "reg" then
return true
elseif s.type == "lnk" then
return file( fs.readlink(val), seen )
end
end
return false
end
function device( val, seen )
local s = fs.stat(val)
seen = seen or { }
if s and not seen[s.ino] then
seen[s.ino] = true
if s.type == "chr" or s.type == "blk" then
return true
elseif s.type == "lnk" then
return device( fs.readlink(val), seen )
end
end
return false
end
function uciname(val)
return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
end
function range(val, min, max)
val = tonumber(val)
min = tonumber(min)
max = tonumber(max)
if val ~= nil and min ~= nil and max ~= nil then
return ((val >= min) and (val <= max))
end
return false
end
function min(val, min)
val = tonumber(val)
min = tonumber(min)
if val ~= nil and min ~= nil then
return (val >= min)
end
return false
end
function max(val, max)
val = tonumber(val)
max = tonumber(max)
if val ~= nil and max ~= nil then
return (val <= max)
end
return false
end
function rangelength(val, min, max)
val = tostring(val)
min = tonumber(min)
max = tonumber(max)
if val ~= nil and min ~= nil and max ~= nil then
return ((#val >= min) and (#val <= max))
end
return false
end
function minlength(val, min)
val = tostring(val)
min = tonumber(min)
if val ~= nil and min ~= nil then
return (#val >= min)
end
return false
end
function maxlength(val, max)
val = tostring(val)
max = tonumber(max)
if val ~= nil and max ~= nil then
return (#val <= max)
end
return false
end
function phonedigit(val)
return (val:match("^[0-9\*#]+$") ~= nil)
end

View File

@ -0,0 +1,87 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local io = require "io"
local fs = require "nixio.fs"
local util = require "luci.util"
local nixio = require "nixio"
local debug = require "debug"
local string = require "string"
local package = require "package"
local type, loadfile = type, loadfile
module "luci.ccache"
function cache_ondemand(...)
if debug.getinfo(1, 'S').source ~= "=?" then
cache_enable(...)
end
end
function cache_enable(cachepath, mode)
cachepath = cachepath or "/tmp/luci-modulecache"
mode = mode or "r--r--r--"
local loader = package.loaders[2]
local uid = nixio.getuid()
if not fs.stat(cachepath) then
fs.mkdir(cachepath)
end
local function _encode_filename(name)
local encoded = ""
for i=1, #name do
encoded = encoded .. ("%2X" % string.byte(name, i))
end
return encoded
end
local function _load_sane(file)
local stat = fs.stat(file)
if stat and stat.uid == uid and stat.modestr == mode then
return loadfile(file)
end
end
local function _write_sane(file, func)
if nixio.getuid() == uid then
local fp = io.open(file, "w")
if fp then
fp:write(util.get_bytecode(func))
fp:close()
fs.chmod(file, mode)
end
end
end
package.loaders[2] = function(mod)
local encoded = cachepath .. "/" .. _encode_filename(mod)
local modcons = _load_sane(encoded)
if modcons then
return modcons
end
-- No cachefile
modcons = loader(mod)
if type(modcons) == "function" then
_write_sane(encoded, modcons)
end
return modcons
end
end

View File

@ -0,0 +1,42 @@
--[[
LuCI - Configuration
Description:
Some LuCI configuration values read from uci file "luci"
FileId:
$Id: config.lua 3856 2008-12-05 15:36:44Z Cyrus $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local util = require "luci.util"
module("luci.config",
function(m)
if pcall(require, "luci.model.uci") then
local config = util.threadlocal()
setmetatable(m, {
__index = function(tbl, key)
if not config[key] then
config[key] = luci.model.uci.cursor():get_all("luci", key)
end
return config[key]
end
})
end
end)

View File

@ -0,0 +1,10 @@
module("luci.controller.api.index", package.seeall)
function index()
local page = node("api")
page.target = firstchild()
page.title = _("")
page.order = 10
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
end

View File

@ -0,0 +1,336 @@
module("luci.controller.api.xqdatacenter", package.seeall)
function index()
local page = node("api","xqdatacenter")
page.target = firstchild()
page.title = ("")
page.order = 300
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
entry({"api", "xqdatacenter"}, firstchild(), _(""), 300)
entry({"api", "xqdatacenter", "request"}, call("tunnelRequest"), _(""), 301)
entry({"api", "xqdatacenter", "identify_device"}, call("identifyDevice"), _(""), 302, 0x08)
entry({"api", "xqdatacenter", "download"}, call("download"), _(""), 303)
entry({"api", "xqdatacenter", "upload"}, call("upload"), _(""), 304)
entry({"api", "xqdatacenter", "thumb"}, call("getThumb"), _(""), 305)
entry({"api", "xqdatacenter", "device_id"}, call("getDeviceId"), _(""), 306)
entry({"api", "xqdatacenter", "check_file_exist"}, call("checkFileExist"), _(""), 307)
entry({"api", "xqdatacenter", "plugin_ssh"}, call("pluginSSH"), _(""), 308)
entry({"api", "xqdatacenter", "plugin_ssh_status"}, call("pluginSSHStatus"), _(""), 309)
end
local LuciHttp = require("luci.http")
local LuciJson = require("json")
local XQConfigs = require("xiaoqiang.common.XQConfigs")
local XQFunction = require("xiaoqiang.common.XQFunction")
local XQErrorUtil = require("xiaoqiang.util.XQErrorUtil")
function tunnelRequest()
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
local cmd = XQConfigs.THRIFT_TUNNEL_TO_DATACENTER % payload
local LuciUtil = require("luci.util")
LuciHttp.write(LuciUtil.exec(cmd))
end
function identifyDevice()
local cmd = XQConfigs.THRIFT_TO_MQTT_IDENTIFY_DEVICE
local LuciUtil = require("luci.util")
local result = {}
result["code"] = 0
result["info"] = LuciUtil.exec(cmd)
LuciHttp.write_json(result)
end
function getDeviceId()
local cmd = XQConfigs.THRIFT_TO_MQTT_GET_DEVICEID
local LuciUtil = require("luci.util")
local result = {}
result["code"] = 0
result["deviceId"] = LuciUtil.exec(cmd)
LuciHttp.write_json(result)
end
function download()
local fs = require("nixio.fs")
local mime = require("luci.http.protocol.mime")
local ltn12 = require("luci.ltn12")
local log = require("xiaoqiang.XQLog")
local path = LuciHttp.formvalue("path")
if XQFunction.isStrNil(path) then
LuciHttp.status(404, _("no Such file"))
return
end
local constPrefix1 = "/userdisk/data/"
local constPrefix2 = "/extdisks/"
local constPrefix3 = "/userdisk/privacyData/"
if (string.sub(path, 1, string.len(constPrefix1)) ~= constPrefix1) and (string.sub(path, 1, string.len(constPrefix2)) ~= constPrefix2) and (string.sub(path, 1, string.len(constPrefix3)) ~= constPrefix3) then
LuciHttp.status(403, _("no permission"))
return
end
-- check privacy disk permission by md5 device mac address
--[[if string.sub(path, 1, string.len(constPrefix3)) == constPrefix3 then
local secret = LuciHttp.formvalue("secret")
if XQFunction.isStrNil(secret) then
LuciHttp.status(403, _("no permission"))
return
end
log.log(3, "=============secret = " .. secret)
local access = false
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local XQDeviceUtil = require("xiaoqiang.util.XQDeviceUtil")
local macfilterInfoList = XQDeviceUtil.getMacfilterInfoList()
for _,value in ipairs(macfilterInfoList) do
if XQFunction.isStrNil(value.mac) == false then
log.log(3, "=============mac = " .. value.mac)
if string.lower(XQCryptoUtil.md5Str(string.lower(value.mac))) == string.lower(secret) then
log.log(3, "=============device found")
if value.pridisk then
access = true
end
break
end
end
end
if access == false then
LuciHttp.status(403, _("no permission"))
return
end
end]]
log.log(3, "=============path = " .. path)
local stat = fs.stat(path)
if not stat then
LuciHttp.status(404, _("no Such file"))
return
end
LuciHttp.header("Accept-Ranges", "bytes")
LuciHttp.header("Content-Type", mime.to_mime(path))
local range = LuciHttp.getenv("HTTP_RANGE")
-- format: bytes=123-
if range then
LuciHttp.status(206)
range = string.gsub(range, "bytes=", "")
range = string.gsub(range, "-", "")
else
range = 0
end
log.log(3, "=============range = " .. range)
-- format: bytes 123-456/457
local contentRange = "bytes " .. range .. "-" .. (stat.size - 1) .. "/" .. stat.size
log.log(3, "=============contentRange = " .. contentRange)
LuciHttp.header("Content-Length", stat.size - range)
LuciHttp.header("Content-Range", contentRange)
LuciHttp.header("Content-Disposition", "attachment; filename=" .. fs.basename(path))
if string.sub(path, 1, string.len(constPrefix1)) == constPrefix1 then
LuciHttp.header("X-Accel-Redirect", "/download-userdisk/" .. string.sub(path, string.len(constPrefix1) + 1, string.len(path)))
elseif string.sub(path, 1, string.len(constPrefix2)) == constPrefix2 then
LuciHttp.header("X-Accel-Redirect", "/download-extdisks/" .. string.sub(path, string.len(constPrefix2) + 1, string.len(path)))
elseif string.sub(path, 1, string.len(constPrefix3)) == constPrefix3 then
LuciHttp.header("X-Accel-Redirect", "/download-pridisk/" .. string.sub(path, string.len(constPrefix3) + 1, string.len(path)))
end
--local file = io.open(path, "r")
--local position = file:seek("set", range)
--log.log(3, "=============position = " .. position)
--ltn12.pump.all(ltn12.source.file(file), LuciHttp.write)
end
function upload()
local fp
local log = require("xiaoqiang.XQLog")
local fs = require("luci.fs")
local tmpfile = "/userdisk/upload.tmp"
if fs.isfile(tmpfile) then
fs.unlink(tmpfile)
end
local filename
LuciHttp.setfilehandler(
function(meta, chunk, eof)
if not fp then
if meta and meta.name == "file" then
fp = io.open(tmpfile, "w")
filename = meta.file
filename = string.gsub(filename, "+", " ")
filename = string.gsub(filename, "%%(%x%x)",
function(h)
return string.char(tonumber(h, 16))
end)
filename = filename.gsub(filename, "\r\n", "\n")
end
end
if chunk then
fp:write(chunk)
end
if eof then
fp:close()
end
end
)
local path = LuciHttp.formvalue("target")
if string.match(path, "\/$") == nil then
path = path .. "/"
end
fs.mkdir(path, true)
local savename = filename
if fs.isfile(path .. savename) then
local basename = savename
local index = basename:match(".+()%.%w+$")
if index then
basename = basename:sub(1, index - 1)
end
local extension = savename:match(".+%.(%w+)$")
for i = 1, 100, 1 do
local tmpname = basename .. "(" .. i .. ")"
if extension then
tmpname = tmpname .. "." .. extension
end
if not fs.isfile(path .. tmpname) then
savename = tmpname
break
end
end
end
local dest = path .. savename
log.log(3, "dest=" .. dest)
fs.rename(tmpfile, dest)
local result = {}
result["code"] = 0
LuciHttp.write_json(result)
end
function getThumb()
local LuciUtil = require("luci.util")
local fs = require("nixio.fs")
local mime = require("luci.http.protocol.mime")
local ltn12 = require("luci.ltn12")
local log = require("xiaoqiang.XQLog")
local realPath = LuciHttp.formvalue("filePath")
log.log(3, "realPath = ", realPath)
if (realPath == nil) then
LuciHttp.status(404, _("no Such file"))
return
end
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local payload = "{\"api\":10, \"files\":[\"" ..realPath.. "\"]}"
local thumbResponse = XQFunction.thrift_tunnel_to_datacenter(payload)
if thumbResponse and thumbResponse.code == 0 then
local thumbPath = thumbResponse.thumbnails[1]
local stat = fs.stat(thumbPath)
LuciHttp.header("Content-Type", mime.to_mime(thumbPath))
LuciHttp.header("Content-Length", stat.size)
ltn12.pump.all(ltn12.source.file(io.open(thumbPath, "r")), LuciHttp.write)
else
LuciHttp.status(404, _("no Such thumb file"))
end
end
function checkFileExist()
local fs = require("nixio.fs")
local exist = true
local path = LuciHttp.formvalue("filePath")
if XQFunction.isStrNil(path) then
exist = false
else
local stat = fs.stat(path)
if not stat then
exist = false
end
end
local result = {}
result["code"] = 0
result['exist'] = exist
LuciHttp.write_json(result)
end
function pluginSSH()
local LuciUtil = require("luci.util")
local XQLog = require("xiaoqiang.XQLog")
local code = 0
local result = {}
local pluginID = LuciHttp.formvalue("pluginID")
local capabilitystr = LuciHttp.formvalue("capability")
local open = tonumber(LuciHttp.formvalue("open") or 0)
XQLog.check(0, XQLog.KEY_FUNC_PLUGIN, 1)
if open and open == 1 then
if pluginID and capabilitystr then
local payload = {
["api"] = 611,
["pluginID"] = pluginID,
["capability"] = LuciUtil.split(capabilitystr, ",")
}
local datacenter = XQFunction.thrift_tunnel_to_datacenter(LuciJson.encode(payload))
if datacenter and datacenter.code ~= 0 then
code = 1595
end
else
code = 1537
end
else
local payload = {
["api"] = 613
}
local datacenter = XQFunction.thrift_tunnel_to_datacenter(LuciJson.encode(payload))
if datacenter and datacenter.code == 0 then
code = 0
else
code = 1601
end
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end
function pluginSSHStatus()
local code = 0
local result = {}
local datacenter = XQFunction.thrift_tunnel_to_datacenter([[{"api":612}]])
local capability = XQFunction.thrift_tunnel_to_datacenter([[{"api":621}]])
if datacenter and datacenter.code == 0 and capability and datacenter.code == 0 then
local capabilitylist = {}
result["enable"] = datacenter.status == 1 and 1 or 0
local encapability = {}
if result.enable == 1 then
local pluginSSH = datacenter.plugin_ssh_status
result["pluginID"] = pluginSSH.pluginID
encapability = pluginSSH.capability
end
for _, item in ipairs(capability.list) do
item.enable = 0
for _, capa in ipairs(encapability) do
if item.key == capa then
item.enable = 1
break
end
end
table.insert(capabilitylist, item)
end
result["capability"] = capabilitylist
else
code = 1600
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end

View File

@ -0,0 +1,293 @@
module("luci.controller.api.xqnetdetect", package.seeall)
function index()
local page = node("api","xqnetdetect")
page.target = firstchild()
page.title = ("")
page.order = 350
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
entry({"api", "xqnetdetect"}, firstchild(), _(""), 350)
entry({"api", "xqnetdetect", "wan_status"}, call("getWanStatus"), _(""), 351, 0x01)
entry({"api", "xqnetdetect", "sys_info"}, call("getSysInfo"), (""), 352, 0x01)
entry({"api", "xqnetdetect", "ping_test"}, call("pingTest"), (""), 353, 0x01)
entry({"api", "xqnetdetect", "detect"}, call("systemDiagnostics"), (""), 354, 0x01)
entry({"api", "xqnetdetect", "sys_status"}, call("systemStatus"), (""), 355, 0x01)
entry({"api", "xqnetdetect", "netspeed"}, call("netspeed"), (""), 356)
entry({"api", "xqnetdetect", "uploadspeed"}, call("uploadSpeed"), (""), 357)
end
local LuciHttp = require("luci.http")
local XQFunction = require("xiaoqiang.common.XQFunction")
local XQErrorUtil = require("xiaoqiang.util.XQErrorUtil")
function getWanStatus()
local XQLanWanUtil = require("xiaoqiang.util.XQLanWanUtil")
local result = {}
local wanType = XQLanWanUtil.getAutoWanType()
local wanInfo = XQLanWanUtil.getLanWanInfo("wan")
local wanMonitor = XQLanWanUtil.getWanMonitorStat()
result["code"] = 0
result["wanLink"] = wanType == 99 and 0 or 1
result["wanType"] = wanType
result["wanInfo"] = wanInfo
result["wanMonitor"] = wanMonitor
LuciHttp.write_json(result)
end
function getSysInfo()
local LuciSys = require("luci.sys")
local result = {}
local cpu = {}
local mem = {}
local system, model, memtotal, memcached, membuffers, memfree, bogomips = LuciSys.sysinfo()
cpu["info"] = system
mem["total"] = memtotal
mem["free"] = memfree
result["code"] = 0
result["cpuInfo"] = cpu
result["memInfo"] = mem
LuciHttp.write_json(result)
end
function pingTest()
local LuciSys = require("luci.sys")
local pingUrl = LuciHttp.formvalue("url")
local ping = LuciSys.net.pingtest(pingUrl)
local result = {}
result["code"] = 0
result["result"] = ping == 0 and 1 or 0
LuciHttp.write_json(result)
end
--[[
simple : 0/1/2 (,log/,,log/,,log)
]]--
function systemDiagnostics()
local XQLog = require("xiaoqiang.XQLog")
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local XQSecureUtil = require("xiaoqiang.util.XQSecureUtil")
local XQWifiUtil = require("xiaoqiang.util.XQWifiUtil")
local XQDeviceUtil = require("xiaoqiang.util.XQDeviceUtil")
local lan = XQDeviceUtil.getWanLanNetworkStatistics("lan")
local wan = XQDeviceUtil.getWanLanNetworkStatistics("wan")
local speed = {}
speed["lan"] = tonumber(lan.downspeed)
speed["wan"] = tonumber(wan.downspeed)
local simple = tonumber(LuciHttp.formvalue("simple") or 0)
local target = LuciHttp.formvalue("target")
local result = {}
local code = 0
local status = 0
local count = 0
local cpuTemperature = XQSysUtil.getCpuTemperature()
local network = XQSysUtil.getNetworkDetectInfo(simple,target)
XQSysUtil.setDetectionTimestamp()
local wifiInfo = XQWifiUtil.getAllWifiInfo()
local same = false
local strong = true
local wifi = {}
for i=1, #wifiInfo do
if XQSecureUtil.checkPlaintextPwd("admin", wifiInfo[i].password) then
same = true
end
if XQSecureUtil.checkStrong(wifiInfo[i].password) < 2 then
strong = false
end
end
wifi["same"] = same and 1 or 0
wifi["strong"] = strong and 1 or 0
local disk = {}
local diskinfo = XQFunction.thrift_tunnel_to_datacenter([[{"api":26}]])
if diskinfo and diskinfo.code == 0 then
local capacity = tonumber(diskinfo.capacity)
local free = tonumber(diskinfo.free)
disk["Used"] = string.format("%.3fG", (capacity - free)/1073741824)
disk["Available"] = string.format("%.3fG", free/1073741824)
end
if network then
local cputemp = {}
cputemp["temperature"] = cpuTemperature
if cpuTemperature > 70 then
count = count + 1
status = 1
cputemp["status"] = 0
else
cputemp["status"] = 1
end
local cpuavg = {}
cpuavg["loadavg"] = network.cpu
if tonumber(network.cpu) > 90 then
count = count + 1
status = 1
cpuavg["status"] = 0
else
cpuavg["status"] = 1
end
local memoryuse = {}
memoryuse["use"] = network.memory
if tonumber(network.memory) > 90 then
count = count + 1
status = 1
memoryuse["status"] = 0
else
memoryuse["status"] = 1
end
local link = {}
if network.wanLink ~= 1 then
count = count + 1
status = 2
link["status"] = 0
else
link["status"] = 1
end
local wan = {}
wan["type"] = network.wanType
if tonumber(network.wanLink) ~= 1 then
count = count + 1
status = 2
wan["status"] = 0
else
wan["status"] = 1
end
local gateway = {}
gateway["lost"] = network.gw
if tonumber(network.gw) > 80 then
count = count + 1
status = 1
gateway["status"] = 0
else
gateway["status"] = 1
end
local dnsstatus = {}
if tonumber(network.dns) ~= 1 then
count = count + 1
status = 2
dnsstatus["status"] = 0
else
dnsstatus["status"] = 1
end
local ping = {}
ping["lost"] = network.pingLost
if tonumber(network.pingLost) > 80 then
count = count + 1
status = 2
ping["status"] = 0
else
ping["status"] = 1
end
result = network
result["count"] = count
result["status"] = status
result["cpuavg"] = cpuavg
result["memoryuse"] = memoryuse
result["cputemp"] = cputemp
result["link"] = link
result["wan"] = wan
result["gateway"] = gateway
result["dnsstatus"] = dnsstatus
result["ping"] = ping
result["cpuTemperature"] = cpuTemperature
result["wifi"] = wifi
result["speed"] = speed
result["disk"] = disk
if count > 0 then
XQLog.check(0, XQLog.KEY_DETECT_ERROR, 1)
end
else
code = 1567
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
else
local XQPushHelper = require("xiaoqiang.XQPushHelper")
local LuciJson = require("json")
local payload = {
["type"] = 6,
["data"] = {
["lan"] = speed.lan,
["wan"] = speed.wan
}
}
XQPushHelper.push_request(LuciJson.encode(payload))
end
result["code"] = code
LuciHttp.write_json(result)
end
function systemStatus()
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local count = 0
local result = {}
local status = XQSysUtil.checkSystemStatus()
result["code"] = 0
result["status"] = 0
if (status.cpu and status.cpu > 90) then
count = count + 1
result["status"] = 1
end
if (status.mem and status.mem > 90) then
count = count + 1
result["status"] = 1
end
if (status.tmp and status.tmp > 70) then
count = count + 1
result["status"] = 1
end
if not status.wan or not status.link then
count = count + 1
result["status"] = 2
end
result["count"] = count
LuciHttp.write_json(result)
end
function netspeed()
local XQPreference = require("xiaoqiang.XQPreference")
local XQNSTUtil = require("xiaoqiang.module.XQNetworkSpeedTest")
local code = 0
local result = {}
local history = LuciHttp.formvalue("history")
if history then
result["bandwidth"] = tonumber(XQPreference.get("BANDWIDTH", 0, "xiaoqiang"))
result["download"] = tonumber(string.format("%.2f", 128 * result.bandwidth))
else
local download = XQNSTUtil.downloadSpeedTest()
if download then
result["download"] = download
result["bandwidth"] = tonumber(string.format("%.2f", 8 * download/1024))
XQPreference.set("BANDWIDTH", tostring(result.bandwidth), "xiaoqiang")
else
code = 1588
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
end
result["code"] = code
LuciHttp.write_json(result)
end
function uploadSpeed()
local XQNSTUtil = require("xiaoqiang.module.XQNetworkSpeedTest")
local code = 0
local result = {}
local upload = XQNSTUtil.uploadSpeedTest()
if upload then
result["upload"] = upload
result["bandwidth"] = tonumber(string.format("%.2f", 8 * upload/1024))
else
code = 1588
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,316 @@
module("luci.controller.api.xqpassport", package.seeall)
function index()
local page = node("api","xqpassport")
page.target = firstchild()
page.title = ("")
page.order = 400
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
entry({"api", "xqpassport"}, firstchild(), (""), 400)
entry({"api", "xqpassport", "login"}, call("passportLogin"), (""), 401, 0x01)
entry({"api", "xqpassport", "userInfo"}, call("getUserInfo"), (""), 402)
entry({"api", "xqpassport", "rigister"}, call("routerRegister"), (""), 405, 0x01)
entry({"api", "xqpassport", "binded"}, call("getBindInfo"), (""), 406, 0x01)
entry({"api", "xqpassport", "plugin_list"}, call("pluginList"), (""), 407)
entry({"api", "xqpassport", "plugin_enable"}, call("pluginEnable"), (""), 408)
entry({"api", "xqpassport", "plugin_disable"}, call("pluginDisable"), (""), 409)
entry({"api", "xqpassport", "plugin_detail"}, call("pluginDetail"), (""), 410)
entry({"api", "xqpassport", "unbound"}, call("unboundRouter"), (""), 411)
end
local LuciHttp = require("luci.http")
local XQErrorUtil = require("xiaoqiang.util.XQErrorUtil")
function getBindInfo()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local uuid = LuciHttp.formvalue("uuid") or ""
local force = tonumber(LuciHttp.formvalue("force") or "0")
local result = {}
local code = 0
local bindUUID = XQSysUtil.getPassportBindInfo()
if bindUUID then
result["bind"] = 1
local info = XQSysUtil.getBindUserInfo()
if info == nil or force ~= 0 then
info = XQNetUtil.getUserInfo(uuid)
end
if info then
if info.miliaoNick and info.miliaoNick ~= "" then
info.aliasNick = info.miliaoNick
end
result["info"] = info
else
info = {}
info["aliasNick"] = bindUUID
info["miliaoIcon"] = ""
info["miliaoIconOrig"] = ""
info["miliaoNick"] = ""
info["userId"] = bindUUID
result["info"] = info
end
else
result["bind"] = 0
end
result["routerName"] = XQSysUtil.getRouterName()
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end
function unboundRouter()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local XQDBUtil = require("xiaoqiang.util.XQDBUtil")
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local result = {}
local code = 0
local uuid = LuciHttp.formvalue("uuid")
local password = LuciHttp.formvalue("password")
if uuid == nil or uuid == "" then
uuid = XQSysUtil.getBindUUID()
end
if password ~= nil then
local login = XQNetUtil.xiaomiLogin(uuid,password)
if login and login.code == 0 then
if XQSysUtil.getPassportBindInfo() then
local unbound = XQNetUtil.dismissAccount(nil,uuid)
if unbound and (tonumber(unbound.code) == 0 or tonumber(unbound.code) == 3001 or tonumber(unbound.code) == 3002) then
XQSysUtil.setPassportBound(false,uuid)
else
code = 1550
end
end
else
code = 1556
end
else
code = 1557
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
else
LuciHttp.header("Set-Cookie", "psp=admin|||2|||0;path=/;")
end
result["code"] = code
LuciHttp.write_json(result)
end
function passportLogin()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local XQDBUtil = require("xiaoqiang.util.XQDBUtil")
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local result = {}
local code = 0
local uuid = LuciHttp.formvalue("uuid")
local password = LuciHttp.formvalue("password")
local encrypt = LuciHttp.formvalue("encrypt")
local login = XQNetUtil.xiaomiLogin(uuid,password)
if login and login.code == 0 then
local bindInfo = XQSysUtil.getPassportBindInfo()
if bindInfo then
if login.uuid == bindInfo then
local adminList = XQNetUtil.getAdminList()
if adminList and type(adminList) == "table" then
if tonumber(adminList.code) == 0 then
code = 0
LuciHttp.header("Set-Cookie", "psp=" .. login.uuid .. "|||" .. 1 .. "|||" .. login.token .. ";path=/;")
elseif tonumber(adminList.code) == 401 then
code = 1551
else
code = 1549
XQSysUtil.setPassportBound(false,login.uuid)
LuciHttp.header("Set-Cookie", "psp=admin|||2|||0;path=/;")
end
else
code = 1551
if adminList and adminList.msg then
result["errorDetail"] = adminList.msg
end
end
else
code = 1548
end
else
XQSysUtil.setBindUUID(login.uuid)
end
result["token"] = login.token
result["uuid"] = login.uuid
elseif login and login.code ~= 0 then
if login.code == 1 then
code = 1564
elseif login.code == 2 then
code = 1565
else
code = 1566
end
else
code = 1538
end
if code ~= 0 then
local XQFunction = require("xiaoqiang.common.XQFunction")
XQFunction.forkExec("/usr/sbin/ntpsetclock 99999 log >/dev/null 2>&1")
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end
function routerAdminList()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local result = {}
local code = 0
local uuid = LuciHttp.formvalue("uuid") or ""
if not XQSysUtil.getPassportBindInfo() then
code = 1542
else
local admin = XQNetUtil.getAdminList(uuid)
if admin and tonumber(admin.code) == 0 then
result["list"] = admin.adminList
elseif admin and tonumber(admin.code) == 401 then
code = 1581
else
code = 1543
end
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end
function routerRegister()
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local XQDBUtil = require("xiaoqiang.util.XQDBUtil")
local result = {}
local code = 0
local uuid = LuciHttp.formvalue("uuid")
local register = XQNetUtil.routerRegister(uuid)
local passport = XQNetUtil.getPassport(uuid)
if register and tonumber(register.code) == 0 then
result["deviceID"] = register.id
XQSysUtil.setPassportBound(true,passport.uuid)
else
XQSysUtil.setPassportBound(false,nil)
code = 1541
end
if code ~= 0 then
local XQFunction = require("xiaoqiang.common.XQFunction")
XQFunction.forkExec("/usr/sbin/ntpsetclock 99999 log >/dev/null 2>&1")
result["msg"] = XQErrorUtil.getErrorMessage(code)
else
LuciHttp.header("Set-Cookie", "psp=" .. uuid .. "|||" .. 1 .. "|||" .. passport.token .. ";path=/;")
end
result["code"] = code
LuciHttp.write_json(result)
end
function getUserInfo()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local result = {}
local code = 0
local uuid = LuciHttp.formvalue("uuid") or ""
local info = XQNetUtil.getUserInfo(uuid)
if info then
result["userInfo"] = info
else
code = 1539
end
if code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(code)
end
result["code"] = code
LuciHttp.write_json(result)
end
function pluginList()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local result = {}
local uuid = LuciHttp.formvalue("uuid") or ""
local pList = XQNetUtil.pluginList(uuid)
if pList and tonumber(pList.code) == 0 then
result["code"] = 0
result["list"] = pList
elseif pList and tonumber(pList.code) == 401 then
result["code"] = 1581
elseif pList and tonumber(pList.code) == 3001 then
result["code"] = 1580
else
result["code"] = 1544
end
if result.code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
end
LuciHttp.write_json(result)
end
function pluginEnable()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local result = {}
local uuid = LuciHttp.formvalue("uuid") or ""
local pluginId = LuciHttp.formvalue("pluginId")
local enable = XQNetUtil.pluginEnable(uuid,pluginId)
if enable and tonumber(enable.code) == 0 then
result["code"] = 0
elseif enable and tonumber(enable.code) == 401 then
result["code"] = 1581
elseif enable and tonumber(enable.code) == 3001 then
result["code"] = 1580
else
result["code"] = 1545
end
if result.code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
end
LuciHttp.write_json(result)
end
function pluginDisable()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local result = {}
local uuid = LuciHttp.formvalue("uuid") or ""
local pluginId = LuciHttp.formvalue("pluginId")
local disable = XQNetUtil.pluginDisable(uuid,pluginId)
if disable and tonumber(disable.code) == 0 then
result["code"] = 0
elseif disable and tonumber(disable.code) == 401 then
result["code"] = 1581
elseif disable and tonumber(disable.code) == 3001 then
result["code"] = 1580
else
result["code"] = 1546
end
if result.code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
end
LuciHttp.write_json(result)
end
function pluginDetail()
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
local result = {}
local uuid = LuciHttp.formvalue("uuid") or ""
local pluginId = LuciHttp.formvalue("pluginId")
local plugin = XQNetUtil.pluginDetail(uuid,pluginId)
if plugin and tonumber(plugin.code) == 0 then
result["code"] = 0
result["detail"] = plugin
elseif plugin and tonumber(plugin.code) == 401 then
result["code"] = 1581
elseif plugin and tonumber(plugin.code) == 3001 then
result["code"] = 1580
else
result["code"] = 1547
end
if result.code ~= 0 then
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
end
LuciHttp.write_json(result)
end

View File

@ -0,0 +1,67 @@
module("luci.controller.api.xqsmarthome", package.seeall)
function index()
local page = node("api","xqsmarthome")
page.target = firstchild()
page.title = ("")
page.order = 500
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
entry({"api", "xqsmarthome"}, firstchild(), _(""), 500)
entry({"api", "xqsmarthome", "request"}, call("tunnelSmartHomeRequest"), _(""), 501)
entry({"api", "xqsmarthome", "request_smartcontroller"}, call("tunnelSmartControllerRequest"), _(""), 502)
entry({"api", "xqsmarthome", "request_miio"}, call("tunnelMiioRequest"), _(""), 503)
entry({"api", "xqsmarthome", "request_mitv"}, call("requestMitv"), _(""), 504)
entry({"api", "xqsmarthome", "request_yeelink"}, call("tunnelYeelink"), _(""), 505)
entry({"api", "xqsmarthome", "request_camera"}, call("requestCamera"), _(""), 506)
end
local LuciHttp = require("luci.http")
local XQConfigs = require("xiaoqiang.common.XQConfigs")
local XQFunction = require("xiaoqiang.common.XQFunction")
function tunnelSmartHomeRequest()
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
local cmd = XQConfigs.THRIFT_TUNNEL_TO_SMARTHOME % payload
local LuciUtil = require("luci.util")
LuciHttp.write(LuciUtil.exec(cmd))
end
function tunnelSmartControllerRequest()
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
local cmd = XQConfigs.THRIFT_TUNNEL_TO_SMARTHOME_CONTROLLER % payload
local LuciUtil = require("luci.util")
LuciHttp.write(LuciUtil.exec(cmd))
end
function tunnelMiioRequest()
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
local cmd = XQConfigs.THRIFT_TUNNEL_TO_MIIO % payload
local LuciUtil = require("luci.util")
LuciHttp.write(LuciUtil.exec(cmd))
end
function tunnelYeelink()
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
-- merge yeelink daemon into miio, so tunnel into miio
local cmd = XQConfigs.THRIFT_TUNNEL_TO_MIIO % payload
local LuciUtil = require("luci.util")
LuciHttp.write(LuciUtil.exec(cmd))
end
function requestMitv()
local payload = LuciHttp.formvalue("payload");
local MitvUtil = require("xiaoqiang.util.XQMitvUtil");
LuciHttp.write(MitvUtil.request(payload));
end
function requestCamera()
local payload = LuciHttp.formvalue("payload");
local CamUtil = require("xiaoqiang.util.XQCameraUtil");
LuciHttp.write(CamUtil.request(payload));
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
module("luci.controller.api.xqtunnel", package.seeall)
function index()
local page = node("api","xqtunnel")
page.target = firstchild()
page.title = ("")
page.order = 300
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
entry({"api", "xqtunnel", "request"}, call("tunnelRequest"), _(""), 301)
end
local LuciHttp = require("luci.http")
local XQConfigs = require("xiaoqiang.common.XQConfigs")
local base64chars = {
['A']=true,['B']=true,['C']=true,['D']=true,
['E']=true,['F']=true,['G']=true,['H']=true,
['I']=true,['J']=true,['K']=true,['L']=true,
['M']=true,['N']=true,['O']=true,['P']=true,
['Q']=true,['R']=true,['S']=true,['T']=true,
['U']=true,['V']=true,['W']=true,['X']=true,
['Y']=true,['Z']=true,['a']=true,['b']=true,
['c']=true,['d']=true,['e']=true,['f']=true,
['g']=true,['h']=true,['i']=true,['j']=true,
['k']=true,['l']=true,['m']=true,['n']=true,
['o']=true,['p']=true,['q']=true,['r']=true,
['s']=true,['t']=true,['u']=true,['v']=true,
['w']=true,['x']=true,['y']=true,['z']=true,
['0']=true,['1']=true,['2']=true,['3']=true,
['4']=true,['5']=true,['6']=true,['7']=true,
['8']=true,['9']=true,['-']=true,['_']=true,
['+']=true,['/']=true,['=']=true
}
local function base64filter(input)
local result = ""
for i = 1, #input do
local c = input:sub(i,i)
if base64chars[c] ~= nil and base64chars[c] then
result = result .. c
end
end
return result
end
function tunnelRequest()
local payload = LuciHttp.formvalue("payloadB64")
local cmd = XQConfigs.TUNNEL_TOOL % base64filter(payload)
local LuciUtil = require("luci.util")
LuciHttp.write(LuciUtil.exec(cmd))
end

View File

@ -0,0 +1,18 @@
module("luci.controller.dispatch.index", package.seeall)
function index()
local root = node()
if not root.target then
root.target = alias("dispatch")
root.index = true
end
local page = node("dispatch")
page.target = firstchild()
page.title = _("")
page.order = 1
page.sysauth = "admin"
page.mediaurlbase = "/xiaoqiang/dispatch"
page.sysauth_authenticator = "htmlauth"
page.index = true
entry({"dispatch"}, template("index"), _("跳转"), 1, 0x09)
end

View File

@ -0,0 +1,23 @@
module("luci.controller.firewall", package.seeall)
function index()
-- entry({"admin", "network", "firewall"},
-- alias("admin", "network", "firewall", "zones"),
-- _("Firewall"), 60)
--
-- entry({"admin", "network", "firewall", "zones"},
-- arcombine(cbi("firewall/zones"), cbi("firewall/zone-details")),
-- _("General Settings"), 10).leaf = true
--
-- entry({"admin", "network", "firewall", "forwards"},
-- arcombine(cbi("firewall/forwards"), cbi("firewall/forward-details")),
-- _("Port Forwards"), 20).leaf = true
--
-- entry({"admin", "network", "firewall", "rules"},
-- arcombine(cbi("firewall/rules"), cbi("firewall/rule-details")),
-- _("Traffic Rules"), 30).leaf = true
--
-- entry({"admin", "network", "firewall", "custom"},
-- cbi("firewall/custom"),
-- _("Custom Rules"), 40).leaf = true
end

View File

@ -0,0 +1,32 @@
module("luci.controller.mobile.index", package.seeall)
function index()
local root = node()
if not root.target then
root.target = alias("mobile")
root.index = true
end
local page = node("mobile")
page.target = firstchild()
page.title = _("")
page.order = 110
page.sysauth = "admin"
page.mediaurlbase = "/xiaoqiang/mobile"
page.sysauth_authenticator = "htmlauth_moblie"
page.index = true
entry({"mobile"}, template("mobile/home"), _("首页"), 1, 0x08)
entry({"mobile", "logout"}, call("action_logout"), 2, 0x09)
entry({"mobile", "hello"}, template("mobile/init/hello"), _("初始化欢迎界面"), 3, 0x09)
entry({"mobile", "agreement"}, template("mobile/init/agreement"), _("查看协议"), 4, 0x09)
entry({"mobile", "guide"}, template("mobile/init/guide"), _("初始化引导"), 5, 0x08)
end
function action_logout()
local dsp = require "luci.dispatcher"
local sauth = require "luci.sauth"
if dsp.context.authsession then
sauth.kill(dsp.context.authsession)
dsp.context.urltoken.stok = nil
end
luci.http.header("Set-Cookie", "sysauth=; path=" .. dsp.build_url())
luci.http.redirect(luci.dispatcher.build_url().."/mobile")
end

View File

@ -0,0 +1,382 @@
module("luci.controller.service.datacenter", package.seeall)
function index()
local page = node("service","datacenter")
page.target = firstchild()
page.title = ("")
page.order = nil
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
entry({"service", "datacenter", "download_file"}, call("downloadFile"), _(""), nil, 0x11)
entry({"service", "datacenter", "device_id"}, call("getDeviceID"), _(""), nil, 0x11)
entry({"service", "datacenter", "download_info"}, call("getDownloadInfo"), _(""), nil, 0x11)
entry({"service", "datacenter", "upload_file"}, call("uploadFile"), _(""), nil, 0x11)
entry({"service", "datacenter", "batch_download_info"}, call("getBatchDownloadInfo"), _(""), nil, 0x11)
entry({"service", "datacenter", "config_info"}, call("getConfigInfo"), _(""), nil, 0x11)
entry({"service", "datacenter", "set_config"}, call("setConfigInfo"), _(""), nil, 0x11)
entry({"service", "datacenter", "plugin_enable"}, call("enablePlugin"), _(""), nil, 0x11)
entry({"service", "datacenter", "plugin_download_info"}, call("pluginDownloadInfo"), _(""), nil, 0x11)
entry({"service", "datacenter", "plugin_disable"}, call("disablePlugin"), _(""), nil, 0x11)
entry({"service", "datacenter", "plugin_control"}, call("controlPlugin"), _(""), nil, 0x11)
entry({"service", "datacenter", "download_delete"}, call("deleteDownload"), _(""), nil, 0x11)
entry({"service", "datacenter", "get_plugin_status"}, call("pluginStatus"), _(""), nil, 0x11)
entry({"service", "datacenter", "get_connected_device"}, call("connectedDevice"), _(""), nil, 0x11)
entry({"service", "datacenter", "get_router_mac"}, call("getMac"), _(""), nil, 0x11)
entry({"service", "datacenter", "set_wan_access"}, call("setWanAccess"), _(""), nil, 0x11)
entry({"service", "datacenter", "get_router_info"}, call("getRouterInfo"), _(""), nil, 0x11)
entry({"service", "datacenter", "xunlei_notify"}, call("xunleiNotify"), _(""), nil, 0x11)
end
local LuciHttp = require("luci.http")
local XQConfigs = require("xiaoqiang.common.XQConfigs")
local ServiceErrorUtil = require("service.util.ServiceErrorUtil")
function xunleiNotify()
local payload = {}
payload["api"] = 519
payload["info"] = LuciHttp.formvalue("tasks")
tunnelRequestDatacenter(payload)
end
function tunnelRequestDatacenter(payload)
local LuciJson = require("cjson")
local LuciUtil = require("luci.util")
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
payload = LuciJson.encode(payload)
payload = XQCryptoUtil.binaryBase64Enc(payload)
local cmd = XQConfigs.THRIFT_TUNNEL_TO_DATACENTER % payload
LuciHttp.write(LuciUtil.exec(cmd))
end
function requestDatacenter(payload)
local LuciJson = require("cjson")
local LuciUtil = require("luci.util")
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
payload = LuciJson.encode(payload)
payload = XQCryptoUtil.binaryBase64Enc(payload)
local cmd = XQConfigs.THRIFT_TUNNEL_TO_DATACENTER % payload
return LuciUtil.exec(cmd)
end
function downloadFile()
local payload = {}
payload["api"] = 1101
payload["appid"] = LuciHttp.formvalue("appId")
payload["path"] = LuciHttp.formvalue("path")
payload["url"] = LuciHttp.formvalue("url")
payload["name"] = LuciHttp.formvalue("downloadName")
payload["tag"] = LuciHttp.formvalue("tag")
payload["hidden"] = false
if LuciHttp.formvalue("hidden") == "true" then
payload["hidden"] = true
end
payload["redownload"] = 0
if LuciHttp.formvalue("redownload") == "1" then
payload["redownload"] = 1
end
payload["dupId"] = LuciHttp.formvalue("dupId")
tunnelRequestDatacenter(payload)
end
function setWanAccess()
local payload = {}
payload["api"] = 618
payload["appid"] = LuciHttp.formvalue("appId")
payload["mac"] = LuciHttp.formvalue("mac")
payload["enable"] = false
if LuciHttp.formvalue("enable") == "true" then
payload["enable"] = true
end
tunnelRequestDatacenter(payload)
end
function getDeviceID()
local payload = {}
payload["api"] = 1103
payload["appid"] = LuciHttp.formvalue("appId")
tunnelRequestDatacenter(payload)
end
function getMac()
local payload = {}
payload["api"] = 617
payload["appid"] = LuciHttp.formvalue("appId")
tunnelRequestDatacenter(payload)
end
function getRouterInfo()
local payload = {}
payload["api"] = 622
payload["appid"] = LuciHttp.formvalue("appId")
tunnelRequestDatacenter(payload)
end
function getOperateDeviceID()
local payload = {}
payload["api"] = 1103
payload["appid"] = LuciHttp.formvalue("appId")
local result = requestDatacenter(payload)
if result then
local LuciJson = require("cjson")
result = LuciJson.decode(result)
if result then
if result.code == 0 then
local deviceid = result["deviceid"]
if deviceid then
return 0, deviceid
end
elseif result.code == 5 then
return 5, nil
end
end
end
return 1559, nil
end
function urlEncode(url)
if url then
url = string.gsub(url, "\n", "\r\n")
url = string.gsub(url, "([^0-9a-zA-Z/])",
function(c) return string.format ("%%%02X", string.byte(c)) end)
end
return url
end
function generateUrlFromPath(path)
if path then
path = urlEncode(path)
local url, count = string.gsub (path, "^/userdisk/data/", "http://miwifi.com/api-third-party/download/public/")
if count == 1 then
return url
end
url, count = string.gsub (path, "^/userdisk/appdata/", "http://miwifi.com/api-third-party/download/private/")
if count == 1 then
return url
end
url, count = string.gsub (path, "^/extdisks/", "http://miwifi.com/api-third-party/download/extdisks/")
if count == 1 then
return url
end
end
return nil
end
function generateResponseFromCode(code)
local response = {}
response["code"] = code
response["msg"] = ServiceErrorUtil.getErrorMessage(code)
return response
end
function getDownloadInfo()
local LuciJson = require("cjson")
local payload = {}
payload["api"] = 1102
payload["appid"] = LuciHttp.formvalue("appId")
payload["deviceId"] = LuciHttp.formvalue("deviceId")
payload["downloadId"] = LuciHttp.formvalue("downloadId")
payload["hidden"] = false
if LuciHttp.formvalue("hidden") == "true" then
payload["hidden"] = true
end
local response = {}
local result = requestDatacenter(payload)
if result then
result = LuciJson.decode(result)
if result and result.code == 0 then
local url = generateUrlFromPath(result["path"])
if url then
response["code"] = result["code"]
response["msg"] = result["msg"]
response["url"] = url
else
response = generateResponseFromCode(1559)
end
else
response = result
end
else
response = generateResponseFromCode(1559)
end
LuciHttp.write_json(response)
LuciHttp.close()
end
function uploadFile()
local fp
local log = require("xiaoqiang.XQLog")
local fs = require("luci.fs")
local tmpfile = "/userdisk/upload.tmp"
if fs.isfile(tmpfile) then
fs.unlink(tmpfile)
end
local filename
LuciHttp.setfilehandler(
function(meta, chunk, eof)
if not fp then
if meta and meta.name == "file" then
fp = io.open(tmpfile, "w")
filename = meta.file
filename = string.gsub(filename, "+", " ")
filename = string.gsub(filename, "%%(%x%x)",
function(h)
return string.char(tonumber(h, 16))
end)
filename = filename.gsub(filename, "\r\n", "\n")
end
end
if chunk then
fp:write(chunk)
end
if eof then
fp:close()
end
end
)
local code, deviceId = getOperateDeviceID()
if code ~= 0 then
return LuciHttp.write_json(generateResponseFromCode(code))
end
local path
local appid = LuciHttp.formvalue("appId")
local saveType = LuciHttp.formvalue("saveType")
if saveType == "public" then
path = "/userdisk/data/上传/"
elseif saveType == "private" then
path = "/userdisk/appdata/" .. appid .. "/"
else
return LuciHttp.write_json(generateResponseFromCode(3))
end
fs.mkdir(path, true)
local savename = fs.basename(filename)
if fs.isfile(path .. savename) then
local basename = savename
local index = basename:match(".+()%.%w+$")
if index then
basename = basename:sub(1, index - 1)
end
local extension = savename:match(".+%.(%w+)$")
for i = 1, 100, 1 do
local tmpname = basename .. "(" .. i .. ")"
if extension then
tmpname = tmpname .. "." .. extension
end
if not fs.isfile(path .. tmpname) then
savename = tmpname
break
end
end
end
local dest = path .. savename
log.log("dest=" .. dest)
fs.rename(tmpfile, dest)
local response = {}
response["code"] = 0
response["url"] = generateUrlFromPath(dest)
response["deviceId"] = deviceId
response["msg"] = ""
LuciHttp.write_json(response)
LuciHttp.close()
end
function getBatchDownloadInfo()
local payload = {}
payload["api"] = 1105
payload["appid"] = LuciHttp.formvalue("appId")
payload["ids"] = LuciHttp.formvalue("ids")
payload["hidden"] = false
if LuciHttp.formvalue("hidden") == "true" then
payload["hidden"] = true
end
tunnelRequestDatacenter(payload)
end
function getConfigInfo()
local payload = {}
payload["api"] = 1106
payload["appid"] = LuciHttp.formvalue("appId")
payload["key"] = LuciHttp.formvalue("key")
tunnelRequestDatacenter(payload)
end
function connectedDevice()
local payload = {}
payload["api"] = 616
payload["appid"] = LuciHttp.formvalue("appId")
tunnelRequestDatacenter(payload)
end
function setConfigInfo()
local payload = {}
payload["api"] = 1107
payload["appid"] = LuciHttp.formvalue("appId")
payload["key"] = LuciHttp.formvalue("key")
payload["value"] = LuciHttp.formvalue("value")
tunnelRequestDatacenter(payload)
end
function enablePlugin()
local payload = {}
payload["api"] = 1108
payload["appid"] = LuciHttp.formvalue("appId")
payload["status"] = 5
tunnelRequestDatacenter(payload)
end
function disablePlugin ()
local payload = {}
payload["api"] = 1108
payload["appid"] = LuciHttp.formvalue("appId")
payload["status"] = 6
tunnelRequestDatacenter(payload)
end
function controlPlugin()
local payload = {}
payload["api"] = 600
payload["pluginID"] = LuciHttp.formvalue("appId")
payload["info"] = LuciHttp.formvalue("info")
tunnelRequestDatacenter(payload)
end
function deleteDownload()
local payload = {}
payload["api"] = 1110
payload["appid"] = LuciHttp.formvalue("appId")
payload["idList"] = LuciHttp.formvalue("idList")
payload["deletefile"] = false
if LuciHttp.formvalue("deletefile") == "true" then
payload["deletefile"] = true
end
tunnelRequestDatacenter(payload)
end
function pluginStatus()
local payload = {}
payload["api"] = 1111
payload["appid"] = LuciHttp.formvalue("appId")
tunnelRequestDatacenter(payload)
end
function pluginDownloadInfo()
local payload = {}
payload["api"] = 1109
payload["appid"] = LuciHttp.formvalue("appId")
payload["hidden"] = false
if LuciHttp.formvalue("hidden") == "true" then
payload["hidden"] = true
end
payload["lite"] = false
if LuciHttp.formvalue("lite") == "true" then
payload["lite"] = true
end
tunnelRequestDatacenter(payload)
end

View File

@ -0,0 +1,10 @@
module("luci.controller.service.index", package.seeall)
function index()
local page = node("service")
page.target = firstchild()
page.title = _("")
page.order = nil
page.sysauth = "admin"
page.sysauth_authenticator = "jsonauth"
page.index = true
end

View File

@ -0,0 +1,96 @@
module("luci.controller.web.index", package.seeall)
function index()
local root = node()
if not root.target then
root.target = alias("web")
root.index = true
end
local page = node("web")
page.target = firstchild()
page.title = _("")
page.order = 10
page.sysauth = "admin"
page.mediaurlbase = "/xiaoqiang/web"
page.sysauth_authenticator = "htmlauth"
page.index = true
entry({"web"}, template("web/index"), _("路由器状态"), 10, 0x08)
entry({"web", "home"}, template("web/index"), _("路由器状态"), 70, 0x08)
entry({"web", "manager"}, template("web/manager"), _("终端管理"), 71)
--entry({"web", "plugin"}, template("web/plugin"), _("插件管理"), 72)
--entry({"web", "plugin", "kuaipan"}, template("web/plugins/kuaipan"), _("插件管理_快盘"), 72)
--entry({"web", "plugin", "guest"}, template("web/plugins/guest"), _("插件管理_访客"), 72)
entry({"web", "logout"}, call("action_logout"), 11, 0x09)
entry({"web", "init"}, template("web/init/hello"), _("初始化引导"), 190, 0x09)
entry({"web", "init", "hello"}, template("web/init/hello"), _("欢迎界面"), 198, 0x09) --不需要登录
entry({"web", "init", "agreement"}, template("web/init/agreement"), _("用户协议"), 198, 0x09) --不需要登录
entry({"web", "init", "guide"}, template("web/init/guide"), _("引导模式"), 190, 0x08)
entry({"web", "netset"}, template("web/netset"), _("路由设置"), 73)
entry({"web", "sysset"}, template("web/sysset"), _("路由设置"), 73)
entry({"web", "sysset", "passport"}, template("web/setting/passport"), _("路由器权限"), 18)
entry({"web", "sysset", "reboot"}, template("web/setting/reboot"), _("重启路由器"), 73)
entry({"web", "sysset", "reset"}, template("web/setting/reset"), _("恢复出厂设置"), 73)
entry({"web", "netset", "wifi"}, template("web/setting/wifi_set"), _("WIFI网络设置"), 20)
entry({"web", "netset", "wifi_mini"}, template("web/setting/wifi_set_mini"), _("WIFI网络快捷设置"), 20)
entry({"web", "netset", "wifi_pro"}, template("web/setting/wifi_set_pro"), _("WIFI网络高级设置"), 60)
entry({"web", "netset", "wifi_txpwr"}, template("web/setting/wifi_txpwr"), _("WIFI强度设置"), 60)
entry({"web", "netset", "wifi_filter"}, template("web/setting/wifi_filter"), _("WIFI访问控制"), 60)
entry({"web", "netset" ,"net_wan"}, template("web/setting/net_wan"), _("网络设置WAN"), 20)
entry({"web", "netset", "net_lan"}, template("web/setting/net_lan"), _("网络设置LAN"), 30)
entry({"web", "netset", "mac"}, template("web/setting/net_setup_mac"), _("mac 设置"), 40)
entry({"web", "netset", "ipmacband"}, template("web/setting/net_ipmacband"), _("mac 设置"), 40)
entry({"web", "sysset", "qos_pro"}, template("web/setting/qos_pro"), _("QoS 设置"), 40)
entry({"web", "sysset", "upgrade"}, template("web/setting/upgrade"), _("路由器固件升级"), 198, 0x01)
entry({"web", "sysset", "upgrade_manual"}, template("web/setting/upgrade_manual", _("路由器手动升级"), 200))
entry({"web", "sysset", "log"}, template("web/setting/log", _("上传日志"), 201))
--entry({"web", "sysset", "upload_config"}, template("web/setting/upload_config"), _("上传配置信息"), 202)
--entry({"web", "setting", "sys_psp"}, template("web/setting/sys_psp"), _("管理小米账号"), 73)
entry({"web", "sysset", "sys_status"}, template("web/setting/sys_status"), _("系统状态"), 73)
entry({"web", "sysset", "diskformat"}, template("web/setting/diskformat"), _("格式化小强盘"), 202)
entry({"web", "sysset", "nginx"}, template("web/setting/nginx"), _("关闭NGINX"), 203)
entry({"web", "sysset", "upnp"}, template("web/setting/upnp"), _("upnp"), 204)
-- entry({"web", "sysset", "lamp"}, template("web/setting/lamp"), _("LAMP Settings"), 204)
entry({"web", "sysset", "qos"}, template("web/setting/qos"), _("应用限速"), 204)
entry({"web", "sysset", "vpn"}, template("web/setting/vpn"), _("VPN"), 204)
entry({"web", "sysset", "developer"}, template("web/setting/developer"), _("开发者选项"), 205)
entry({"web", "sysset", "dmz"}, template("web/setting/dmz"), _("DMZ"), 205)
entry({"web", "sysset", "ddns"}, template("web/setting/ddns"), _("DDNS"), 204)
entry({"web", "sysset", "nat"}, template("web/setting/nat"), _("端口转发"), 206)
entry({"web", "sysset", "noflushd"}, template("web/setting/noflushd"), _("磁盘休眠"), 207)
--entry({"web", "sysset", "predownload"}, template("web/setting/predownload"), _("预下载"), 208)
entry({"web", "detecte"}, template("web/netdetection"), _("网络检测"), 74, 0x01)
entry({"web", "detecte_pro"}, template("web/urldetection"), _("网络高级检测"), 75, 0x01)
entry({"web", "xmaccount"}, template("web/xmaccount"), _("小米帐号验证"), 75, 0x01)
-- entry({"web", "safeurl"}, call("action_safeurl"), _(""), 75, 0x09)
entry({"web", "webinitrdr"}, template("web/init/webinitrdr"), _("劫持页面"), 300, 0x09) --不需要登录
end
function action_logout()
local dsp = require "luci.dispatcher"
local sauth = require "luci.sauth"
if dsp.context.authsession then
sauth.kill(dsp.context.authsession)
dsp.context.urltoken.stok = nil
end
luci.http.header("Set-Cookie", "sysauth=; path=" .. dsp.build_url())
luci.http.header("Set-Cookie", "autologin_v2=;expires=-1;path=/;")
luci.http.redirect(luci.dispatcher.build_url())
end
function action_safeurl()
local safeUrl = luci.http.formvalue("safeurl")
require("luci.template")
luci.template.render("web/safeurl", {safeurl=safeUrl})
end

Binary file not shown.

37
1_1.mi_Lua/luci/debug.lua Normal file
View File

@ -0,0 +1,37 @@
local debug = require "debug"
local io = require "io"
local collectgarbage, floor = collectgarbage, math.floor
module "luci.debug"
__file__ = debug.getinfo(1, 'S').source:sub(2)
-- Enables the memory tracer with given flags and returns a function to disable the tracer again
function trap_memtrace(flags, dest)
flags = flags or "clr"
local tracefile = io.open(dest or "/tmp/memtrace", "w")
local peak = 0
local function trap(what, line)
local info = debug.getinfo(2, "Sn")
local size = floor(collectgarbage("count"))
if size > peak then
peak = size
end
if tracefile then
tracefile:write(
"[", what, "] ", info.source, ":", (line or "?"), "\t",
(info.namewhat or ""), "\t",
(info.name or ""), "\t",
size, " (", peak, ")\n"
)
end
end
debug.sethook(trap, flags)
return function()
debug.sethook()
tracefile:close()
end
end

File diff suppressed because it is too large Load Diff

244
1_1.mi_Lua/luci/fs.lua Normal file
View File

@ -0,0 +1,244 @@
--[[
LuCI - Filesystem tools
Description:
A module offering often needed filesystem manipulation functions
FileId:
$Id: fs.lua 5134 2009-07-24 17:34:40Z Cyrus $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local io = require "io"
local os = require "os"
local ltn12 = require "luci.ltn12"
local fs = require "nixio.fs"
local nutil = require "nixio.util"
local type = type
--- LuCI filesystem library.
module "luci.fs"
--- Test for file access permission on given path.
-- @class function
-- @name access
-- @param str String value containing the path
-- @return Number containing the return code, 0 on sucess or nil on error
-- @return String containing the error description (if any)
-- @return Number containing the os specific errno (if any)
access = fs.access
--- Evaluate given shell glob pattern and return a table containing all matching
-- file and directory entries.
-- @class function
-- @name glob
-- @param filename String containing the path of the file to read
-- @return Table containing file and directory entries or nil if no matches
-- @return String containing the error description (if no matches)
-- @return Number containing the os specific errno (if no matches)
function glob(...)
local iter, code, msg = fs.glob(...)
if iter then
return nutil.consume(iter)
else
return nil, code, msg
end
end
--- Checks wheather the given path exists and points to a regular file.
-- @param filename String containing the path of the file to test
-- @return Boolean indicating wheather given path points to regular file
function isfile(filename)
return fs.stat(filename, "type") == "reg"
end
--- Checks wheather the given path exists and points to a directory.
-- @param dirname String containing the path of the directory to test
-- @return Boolean indicating wheather given path points to directory
function isdirectory(dirname)
return fs.stat(dirname, "type") == "dir"
end
--- Read the whole content of the given file into memory.
-- @param filename String containing the path of the file to read
-- @return String containing the file contents or nil on error
-- @return String containing the error message on error
readfile = fs.readfile
--- Write the contents of given string to given file.
-- @param filename String containing the path of the file to read
-- @param data String containing the data to write
-- @return Boolean containing true on success or nil on error
-- @return String containing the error message on error
writefile = fs.writefile
--- Copies a file.
-- @param source Source file
-- @param dest Destination
-- @return Boolean containing true on success or nil on error
copy = fs.datacopy
--- Renames a file.
-- @param source Source file
-- @param dest Destination
-- @return Boolean containing true on success or nil on error
rename = fs.move
--- Get the last modification time of given file path in Unix epoch format.
-- @param path String containing the path of the file or directory to read
-- @return Number containing the epoch time or nil on error
-- @return String containing the error description (if any)
-- @return Number containing the os specific errno (if any)
function mtime(path)
return fs.stat(path, "mtime")
end
--- Set the last modification time of given file path in Unix epoch format.
-- @param path String containing the path of the file or directory to read
-- @param mtime Last modification timestamp
-- @param atime Last accessed timestamp
-- @return 0 in case of success nil on error
-- @return String containing the error description (if any)
-- @return Number containing the os specific errno (if any)
function utime(path, mtime, atime)
return fs.utimes(path, atime, mtime)
end
--- Return the last element - usually the filename - from the given path with
-- the directory component stripped.
-- @class function
-- @name basename
-- @param path String containing the path to strip
-- @return String containing the base name of given path
-- @see dirname
basename = fs.basename
--- Return the directory component of the given path with the last element
-- stripped of.
-- @class function
-- @name dirname
-- @param path String containing the path to strip
-- @return String containing the directory component of given path
-- @see basename
dirname = fs.dirname
--- Return a table containing all entries of the specified directory.
-- @class function
-- @name dir
-- @param path String containing the path of the directory to scan
-- @return Table containing file and directory entries or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
function dir(...)
local iter, code, msg = fs.dir(...)
if iter then
local t = nutil.consume(iter)
t[#t+1] = "."
t[#t+1] = ".."
return t
else
return nil, code, msg
end
end
--- Create a new directory, recursively on demand.
-- @param path String with the name or path of the directory to create
-- @param recursive Create multiple directory levels (optional, default is true)
-- @return Number with the return code, 0 on sucess or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
function mkdir(path, recursive)
return recursive and fs.mkdirr(path) or fs.mkdir(path)
end
--- Remove the given empty directory.
-- @class function
-- @name rmdir
-- @param path String containing the path of the directory to remove
-- @return Number with the return code, 0 on sucess or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
rmdir = fs.rmdir
local stat_tr = {
reg = "regular",
dir = "directory",
lnk = "link",
chr = "character device",
blk = "block device",
fifo = "fifo",
sock = "socket"
}
--- Get information about given file or directory.
-- @class function
-- @name stat
-- @param path String containing the path of the directory to query
-- @return Table containing file or directory properties or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
function stat(path, key)
local data, code, msg = fs.stat(path)
if data then
data.mode = data.modestr
data.type = stat_tr[data.type] or "?"
end
return key and data and data[key] or data, code, msg
end
--- Set permissions on given file or directory.
-- @class function
-- @name chmod
-- @param path String containing the path of the directory
-- @param perm String containing the permissions to set ([ugoa][+-][rwx])
-- @return Number with the return code, 0 on sucess or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
chmod = fs.chmod
--- Create a hard- or symlink from given file (or directory) to specified target
-- file (or directory) path.
-- @class function
-- @name link
-- @param path1 String containing the source path to link
-- @param path2 String containing the destination path for the link
-- @param symlink Boolean indicating wheather to create a symlink (optional)
-- @return Number with the return code, 0 on sucess or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
function link(src, dest, sym)
return sym and fs.symlink(src, dest) or fs.link(src, dest)
end
--- Remove the given file.
-- @class function
-- @name unlink
-- @param path String containing the path of the file to remove
-- @return Number with the return code, 0 on sucess or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
unlink = fs.unlink
--- Retrieve target of given symlink.
-- @class function
-- @name readlink
-- @param path String containing the path of the symlink to read
-- @return String containing the link target or nil on error
-- @return String containing the error description on error
-- @return Number containing the os specific errno on error
readlink = fs.readlink

336
1_1.mi_Lua/luci/http.lua Normal file
View File

@ -0,0 +1,336 @@
--[[
LuCI - HTTP-Interaction
Description:
HTTP-Header manipulator and form variable preprocessor
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local ltn12 = require "luci.ltn12"
local protocol = require "luci.http.protocol"
local util = require "luci.util"
local string = require "string"
local coroutine = require "coroutine"
local table = require "table"
local ipairs, pairs, next, type, tostring, error =
ipairs, pairs, next, type, tostring, error
--- LuCI Web Framework high-level HTTP functions.
module ("luci.http", package.seeall)
context = util.threadlocal()
Request = util.class()
function Request.__init__(self, env, sourcein, sinkerr)
self.input = sourcein
self.error = sinkerr
-- File handler
self.filehandler = function() end
-- HTTP-Message table
self.message = {
env = env,
headers = {},
params = protocol.urldecode_params(env.QUERY_STRING or ""),
}
self.parsed_input = false
end
function Request.formvalue(self, name, noparse)
if not noparse and not self.parsed_input then
self:_parse_input()
end
if name then
return self.message.params[name]
else
return self.message.params
end
end
function Request.formvaluetable(self, prefix)
local vals = {}
prefix = prefix and prefix .. "." or "."
if not self.parsed_input then
self:_parse_input()
end
local void = self.message.params[nil]
for k, v in pairs(self.message.params) do
if k:find(prefix, 1, true) == 1 then
vals[k:sub(#prefix + 1)] = tostring(v)
end
end
return vals
end
function Request.content(self)
if not self.parsed_input then
self:_parse_input()
end
return self.message.content, self.message.content_length
end
function Request.getcookie(self, name)
local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
local p = ";" .. name .. "=(.-);"
local i, j, value = c:find(p)
return value and urldecode(value)
end
function Request.getenv(self, name)
if name then
return self.message.env[name]
else
return self.message.env
end
end
function Request.setfilehandler(self, callback)
self.filehandler = callback
end
function Request._parse_input(self)
protocol.parse_message_body(
self.input,
self.message,
self.filehandler
)
self.parsed_input = true
end
--- Close the HTTP-Connection.
function close()
if not context.eoh then
context.eoh = true
coroutine.yield(3)
end
if not context.closed then
context.closed = true
coroutine.yield(5)
end
end
--- Return the request content if the request was of unknown type.
-- @return HTTP request body
-- @return HTTP request body length
function content()
return context.request:content()
end
--- Get a certain HTTP input value or a table of all input values.
-- @param name Name of the GET or POST variable to fetch
-- @param noparse Don't parse POST data before getting the value
-- @return HTTP input value or table of all input value
function formvalue(name, noparse)
return context.request:formvalue(name, noparse)
end
function xqformvalue(name, noparse)
local XQSecureUtil = require("xiaoqiang.util.XQSecureUtil")
local value = context.request:formvalue(name, noparse)
return XQSecureUtil.xssCheck(value)
end
--- Get a table of all HTTP input values with a certain prefix.
-- @param prefix Prefix
-- @return Table of all HTTP input values with given prefix
function formvaluetable(prefix)
return context.request:formvaluetable(prefix)
end
--- Get the value of a certain HTTP-Cookie.
-- @param name Cookie Name
-- @return String containing cookie data
function getcookie(name)
return context.request:getcookie(name)
end
--- Get the value of a certain HTTP environment variable
-- or the environment table itself.
-- @param name Environment variable
-- @return HTTP environment value or environment table
function getenv(name)
return context.request:getenv(name)
end
--- Set a handler function for incoming user file uploads.
-- @param callback Handler function
function setfilehandler(callback)
return context.request:setfilehandler(callback)
end
--- Send a HTTP-Header.
-- @param key Header key
-- @param value Header value
function header(key, value)
if not context.headers then
context.headers = {}
end
context.headers[key:lower()] = value
coroutine.yield(2, key, value)
end
--- Set the mime type of following content data.
-- @param mime Mimetype of following content
function prepare_content(mime)
if not context.headers or not context.headers["content-type"] then
if mime == "application/xhtml+xml" then
if not getenv("HTTP_ACCEPT") or
not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
mime = "text/html; charset=UTF-8"
end
header("Vary", "Accept")
end
header("Content-Type", mime)
end
end
--- Get the RAW HTTP input source
-- @return HTTP LTN12 source
function source()
return context.request.input
end
--- Set the HTTP status code and status message.
-- @param code Status code
-- @param message Status message
function status(code, message)
code = code or 200
message = message or "OK"
context.status = code
coroutine.yield(1, code, message)
end
--- Send a chunk of content data to the client.
-- This function is as a valid LTN12 sink.
-- If the content chunk is nil this function will automatically invoke close.
-- @param content Content chunk
-- @param src_err Error object from source (optional)
-- @see close
function write(content, src_err)
if not content then
if src_err then
error(src_err)
else
close()
end
return true
elseif #content == 0 then
return true
else
if not context.eoh then
if not context.status then
status()
end
if not context.headers or not context.headers["content-type"] then
header("Content-Type", "text/html; charset=utf-8")
end
if not context.headers["cache-control"] then
header("Cache-Control", "no-cache")
header("Expires", "0")
end
context.eoh = true
coroutine.yield(3)
end
coroutine.yield(4, content)
return true
end
end
--- Splice data from a filedescriptor to the client.
-- @param fp File descriptor
-- @param size Bytes to splice (optional)
function splice(fd, size)
coroutine.yield(6, fd, size)
end
--- Redirects the client to a new URL and closes the connection.
-- @param url Target URL
function redirect(url)
status(302, "Found")
header("Location", url)
close()
end
--- Create a querystring out of a table of key - value pairs.
-- @param table Query string source table
-- @return Encoded HTTP query string
function build_querystring(q)
local s = { "?" }
for k, v in pairs(q) do
if #s > 1 then s[#s+1] = "&" end
s[#s+1] = urldecode(k)
s[#s+1] = "="
s[#s+1] = urldecode(v)
end
return table.concat(s, "")
end
--- Return the URL-decoded equivalent of a string.
-- @param str URL-encoded string
-- @param no_plus Don't decode + to " "
-- @return URL-decoded string
-- @see urlencode
urldecode = protocol.urldecode
--- Return the URL-encoded equivalent of a string.
-- @param str Source string
-- @return URL-encoded string
-- @see urldecode
urlencode = protocol.urlencode
function writeJsonNoLog(x)
if x == nil then
write("null")
elseif type(x) == "table" then
local json = require("luci.json")
write(json.encode(x))
elseif type(x) == "number" or type(x) == "boolean" then
if (x ~= x) then
-- NaN is the only value that doesn't equal to itself.
write("Number.NaN")
else
write(tostring(x))
end
else
write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
return '\\u%04x' % c:byte(1)
end))
end
end
--- Send the given data as JSON encoded string.
-- @param data Data to send
function write_json(x)
local XQLog = require("xiaoqiang.XQLog")
XQLog.log(7,x)
writeJsonNoLog(x)
end

View File

@ -0,0 +1,727 @@
--[[
HTTP protocol implementation for LuCI
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: protocol.lua 9195 2012-08-29 13:06:58Z jow $
]]--
--- LuCI http protocol class.
-- This class contains several functions useful for http message- and content
-- decoding and to retrive form data from raw http messages.
module("luci.http.protocol", package.seeall)
local util = require "luci.util"
local ltn12 = require("luci.ltn12")
HTTP_MAX_CONTENT = 1024*32 -- 8 kB maximum content size
--- Decode an urlencoded string - optionally without decoding
-- the "+" sign to " " - and return the decoded string.
-- @param str Input string in x-www-urlencoded format
-- @param no_plus Don't decode "+" signs to spaces
-- @return The decoded string
-- @see urlencode
function urldecode( str, no_plus )
local function __chrdec( hex )
return string.char( tonumber( hex, 16 ) )
end
if type(str) == "string" then
if not no_plus then
str = str:gsub( "+", " " )
end
str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
end
return str
end
--- Extract and split urlencoded data pairs, separated bei either "&" or ";"
-- from given url or string. Returns a table with urldecoded values.
-- Simple parameters are stored as string values associated with the parameter
-- name within the table. Parameters with multiple values are stored as array
-- containing the corresponding values.
-- @param url The url or string which contains x-www-urlencoded form data
-- @param tbl Use the given table for storing values (optional)
-- @return Table containing the urldecoded parameters
-- @see urlencode_params
function urldecode_params( url, tbl )
local params = tbl or { }
if url:find("?") then
url = url:gsub( "^.+%?([^?]+)", "%1" )
end
for pair in url:gmatch( "[^&;]+" ) do
-- find key and value
local key = urldecode( pair:match("^([^=]+)") )
local val = urldecode( pair:match("^[^=]+=(.+)$") )
-- store
if type(key) == "string" and key:len() > 0 then
if type(val) ~= "string" then val = "" end
if not params[key] then
params[key] = val
elseif type(params[key]) ~= "table" then
params[key] = { params[key], val }
else
table.insert( params[key], val )
end
end
end
return params
end
--- Encode given string to x-www-urlencoded format.
-- @param str String to encode
-- @return String containing the encoded data
-- @see urldecode
function urlencode( str )
local function __chrenc( chr )
return string.format(
"%%%02x", string.byte( chr )
)
end
if type(str) == "string" then
str = str:gsub(
"([^a-zA-Z0-9$_%-%.%+!*'(),])",
__chrenc
)
end
return str
end
function xqurlencode(str)
if (str) then
--Ensure all newlines are in CRLF form
str = string.gsub (str, "\r?\n", "\r\n")
--Percent-encode all non-unreserved characters
--as per RFC 3986, Section 2.3
--(except for space, which gets plus-encoded)
str = string.gsub (str, "([^%w%-%.%_%~ ])",
function (c) return string.format ("%%%02X", string.byte(c)) end)
--Convert spaces to plus signs
str = string.gsub (str, " ", "+")
end
return str
end
function xq_urlencode_params( tbl )
local enc = ""
for k, v in pairs(tbl) do
if type(v) == "table" then
for i, v2 in ipairs(v) do
enc = enc .. ( #enc > 0 and "&" or "" ) ..
urlencode(k) .. "=" .. xqurlencode(v2)
end
else
enc = (enc .. ( #enc > 0 and "&" or "" ) ..
urlencode(k) .. "=" .. xqurlencode(v))
end
end
return enc
end
--- Encode each key-value-pair in given table to x-www-urlencoded format,
-- separated by "&". Tables are encoded as parameters with multiple values by
-- repeating the parameter name with each value.
-- @param tbl Table with the values
-- @return String containing encoded values
-- @see urldecode_params
function urlencode_params( tbl )
local enc = ""
for k, v in pairs(tbl) do
if type(v) == "table" then
for i, v2 in ipairs(v) do
enc = enc .. ( #enc > 0 and "&" or "" ) ..
urlencode(k) .. "=" .. urlencode(v2)
end
else
enc = (enc .. ( #enc > 0 and "&" or "" ) ..
urlencode(k) .. "=" .. urlencode(v))
end
end
return enc
end
-- (Internal function)
-- Initialize given parameter and coerce string into table when the parameter
-- already exists.
-- @param tbl Table where parameter should be created
-- @param key Parameter name
-- @return Always nil
local function __initval( tbl, key )
if tbl[key] == nil then
tbl[key] = ""
elseif type(tbl[key]) == "string" then
tbl[key] = { tbl[key], "" }
else
table.insert( tbl[key], "" )
end
end
-- (Internal function)
-- Append given data to given parameter, either by extending the string value
-- or by appending it to the last string in the parameter's value table.
-- @param tbl Table containing the previously initialized parameter value
-- @param key Parameter name
-- @param chunk String containing the data to append
-- @return Always nil
-- @see __initval
local function __appendval( tbl, key, chunk )
if type(tbl[key]) == "table" then
tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
else
tbl[key] = tbl[key] .. chunk
end
end
-- (Internal function)
-- Finish the value of given parameter, either by transforming the string value
-- or - in the case of multi value parameters - the last element in the
-- associated values table.
-- @param tbl Table containing the previously initialized parameter value
-- @param key Parameter name
-- @param handler Function which transforms the parameter value
-- @return Always nil
-- @see __initval
-- @see __appendval
local function __finishval( tbl, key, handler )
if handler then
if type(tbl[key]) == "table" then
tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
else
tbl[key] = handler( tbl[key] )
end
end
end
-- Table of our process states
local process_states = { }
-- Extract "magic", the first line of a http message.
-- Extracts the message type ("get", "post" or "response"), the requested uri
-- or the status code if the line descripes a http response.
process_states['magic'] = function( msg, chunk, err )
if chunk ~= nil then
-- ignore empty lines before request
if #chunk == 0 then
return true, nil
end
-- Is it a request?
local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
-- Yup, it is
if method then
msg.type = "request"
msg.request_method = method:lower()
msg.request_uri = uri
msg.http_version = tonumber( http_ver )
msg.headers = { }
-- We're done, next state is header parsing
return true, function( chunk )
return process_states['headers']( msg, chunk )
end
-- Is it a response?
else
local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
-- Is a response
if code then
msg.type = "response"
msg.status_code = code
msg.status_message = message
msg.http_version = tonumber( http_ver )
msg.headers = { }
-- We're done, next state is header parsing
return true, function( chunk )
return process_states['headers']( msg, chunk )
end
end
end
end
-- Can't handle it
return nil, "Invalid HTTP message magic"
end
-- Extract headers from given string.
process_states['headers'] = function( msg, chunk )
if chunk ~= nil then
-- Look for a valid header format
local hdr, val = chunk:match( "^([A-Za-z][A-Za-z0-9%-_]+): +(.+)$" )
if type(hdr) == "string" and hdr:len() > 0 and
type(val) == "string" and val:len() > 0
then
msg.headers[hdr] = val
-- Valid header line, proceed
return true, nil
elseif #chunk == 0 then
-- Empty line, we won't accept data anymore
return false, nil
else
-- Junk data
return nil, "Invalid HTTP header received"
end
else
return nil, "Unexpected EOF"
end
end
--- Creates a ltn12 source from the given socket. The source will return it's
-- data line by line with the trailing \r\n stripped of.
-- @param sock Readable network socket
-- @return Ltn12 source function
function header_source( sock )
return ltn12.source.simplify( function()
local chunk, err, part = sock:receive("*l")
-- Line too long
if chunk == nil then
if err ~= "timeout" then
return nil, part
and "Line exceeds maximum allowed length"
or "Unexpected EOF"
else
return nil, err
end
-- Line ok
elseif chunk ~= nil then
-- Strip trailing CR
chunk = chunk:gsub("\r$","")
return chunk, nil
end
end )
end
--- Decode a mime encoded http message body with multipart/form-data
-- Content-Type. Stores all extracted data associated with its parameter name
-- in the params table withing the given message object. Multiple parameter
-- values are stored as tables, ordinary ones as strings.
-- If an optional file callback function is given then it is feeded with the
-- file contents chunk by chunk and only the extracted file name is stored
-- within the params table. The callback function will be called subsequently
-- with three arguments:
-- o Table containing decoded (name, file) and raw (headers) mime header data
-- o String value containing a chunk of the file data
-- o Boolean which indicates wheather the current chunk is the last one (eof)
-- @param src Ltn12 source function
-- @param msg HTTP message object
-- @param filecb File callback function (optional)
-- @return Value indicating successful operation (not nil means "ok")
-- @return String containing the error if unsuccessful
-- @see parse_message_header
function mimedecode_message_body( src, msg, filecb )
if msg and msg.env.CONTENT_TYPE then
msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
end
if not msg.mime_boundary then
return nil, "Invalid Content-Type found"
end
local tlen = 0
local inhdr = false
local field = nil
local store = nil
local lchunk = nil
local function parse_headers( chunk, field )
local stat
repeat
chunk, stat = chunk:gsub(
"^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n",
function(k,v)
field.headers[k] = v
return ""
end
)
until stat == 0
chunk, stat = chunk:gsub("^\r\n","")
-- End of headers
if stat > 0 then
if field.headers["Content-Disposition"] then
if field.headers["Content-Disposition"]:match("^form%-data; ") then
field.name = field.headers["Content-Disposition"]:match('name="(.-)"')
field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$')
end
end
if not field.headers["Content-Type"] then
field.headers["Content-Type"] = "text/plain"
end
if field.name and field.file and filecb then
__initval( msg.params, field.name )
__appendval( msg.params, field.name, field.file )
store = filecb
elseif field.name then
__initval( msg.params, field.name )
store = function( hdr, buf, eof )
__appendval( msg.params, field.name, buf )
end
else
store = nil
end
return chunk, true
end
return chunk, false
end
local function snk( chunk )
tlen = tlen + ( chunk and #chunk or 0 )
if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
return nil, "Message body size exceeds Content-Length"
end
if chunk and not lchunk then
lchunk = "\r\n" .. chunk
elseif lchunk then
local data = lchunk .. ( chunk or "" )
local spos, epos, found
repeat
spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
if not spos then
spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
end
if spos then
local predata = data:sub( 1, spos - 1 )
if inhdr then
predata, eof = parse_headers( predata, field )
if not eof then
return nil, "Invalid MIME section header"
elseif not field.name then
return nil, "Invalid Content-Disposition header"
end
end
if store then
store( field, predata, true )
end
field = { headers = { } }
found = found or true
data, eof = parse_headers( data:sub( epos + 1, #data ), field )
inhdr = not eof
end
until not spos
if found then
-- We found at least some boundary. Save
-- the unparsed remaining data for the
-- next chunk.
lchunk, data = data, nil
else
-- There was a complete chunk without a boundary. Parse it as headers or
-- append it as data, depending on our current state.
if inhdr then
lchunk, eof = parse_headers( data, field )
inhdr = not eof
else
-- We're inside data, so append the data. Note that we only append
-- lchunk, not all of data, since there is a chance that chunk
-- contains half a boundary. Assuming that each chunk is at least the
-- boundary in size, this should prevent problems
store( field, lchunk, false )
lchunk, chunk = chunk, nil
end
end
end
return true
end
return ltn12.pump.all( src, snk )
end
--- Decode an urlencoded http message body with application/x-www-urlencoded
-- Content-Type. Stores all extracted data associated with its parameter name
-- in the params table withing the given message object. Multiple parameter
-- values are stored as tables, ordinary ones as strings.
-- @param src Ltn12 source function
-- @param msg HTTP message object
-- @return Value indicating successful operation (not nil means "ok")
-- @return String containing the error if unsuccessful
-- @see parse_message_header
function urldecode_message_body( src, msg )
local tlen = 0
local lchunk = nil
local function snk( chunk )
tlen = tlen + ( chunk and #chunk or 0 )
if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
return nil, "Message body size exceeds Content-Length"
elseif tlen > HTTP_MAX_CONTENT then
return nil, "Message body size exceeds maximum allowed length"
end
if not lchunk and chunk then
lchunk = chunk
elseif lchunk then
local data = lchunk .. ( chunk or "&" )
local spos, epos
repeat
spos, epos = data:find("^.-[;&]")
if spos then
local pair = data:sub( spos, epos - 1 )
local key = pair:match("^(.-)=")
local val = pair:match("=([^%s]*)%s*$")
if key and #key > 0 then
__initval( msg.params, key )
__appendval( msg.params, key, val )
__finishval( msg.params, key, urldecode )
else
key = "invalid_param"
__initval( msg.params, key )
__appendval( msg.params, key, pair )
__finishval( msg.params, key, urldecode )
end
data = data:sub( epos + 1, #data )
end
until not spos
lchunk = data
end
return true
end
return ltn12.pump.all( src, snk )
end
--- Try to extract an http message header including information like protocol
-- version, message headers and resulting CGI environment variables from the
-- given ltn12 source.
-- @param src Ltn12 source function
-- @return HTTP message object
-- @see parse_message_body
function parse_message_header( src )
local ok = true
local msg = { }
local sink = ltn12.sink.simplify(
function( chunk )
return process_states['magic']( msg, chunk )
end
)
-- Pump input data...
while ok do
-- get data
ok, err = ltn12.pump.step( src, sink )
-- error
if not ok and err then
return nil, err
-- eof
elseif not ok then
-- Process get parameters
if ( msg.request_method == "get" or msg.request_method == "post" ) and
msg.request_uri:match("?")
then
msg.params = urldecode_params( msg.request_uri )
else
msg.params = { }
end
-- Populate common environment variables
msg.env = {
CONTENT_LENGTH = msg.headers['Content-Length'];
CONTENT_TYPE = msg.headers['Content-Type'] or msg.headers['Content-type'];
REQUEST_METHOD = msg.request_method:upper();
REQUEST_URI = msg.request_uri;
SCRIPT_NAME = msg.request_uri:gsub("?.+$","");
SCRIPT_FILENAME = ""; -- XXX implement me
SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version);
QUERY_STRING = msg.request_uri:match("?")
and msg.request_uri:gsub("^.+?","") or ""
}
-- Populate HTTP_* environment variables
for i, hdr in ipairs( {
'Accept',
'Accept-Charset',
'Accept-Encoding',
'Accept-Language',
'Connection',
'Cookie',
'Host',
'Referer',
'User-Agent',
} ) do
local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
local val = msg.headers[hdr]
msg.env[var] = val
end
end
end
return msg
end
--- Try to extract and decode a http message body from the given ltn12 source.
-- This function will examine the Content-Type within the given message object
-- to select the appropriate content decoder.
-- Currently the application/x-www-urlencoded and application/form-data
-- mime types are supported. If the encountered content encoding can't be
-- handled then the whole message body will be stored unaltered as "content"
-- property within the given message object.
-- @param src Ltn12 source function
-- @param msg HTTP message object
-- @param filecb File data callback (optional, see mimedecode_message_body())
-- @return Value indicating successful operation (not nil means "ok")
-- @return String containing the error if unsuccessful
-- @see parse_message_header
function parse_message_body( src, msg, filecb )
-- Is it multipart/mime ?
if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
msg.env.CONTENT_TYPE:match("^multipart/form%-data")
then
return mimedecode_message_body( src, msg, filecb )
-- Is it application/x-www-form-urlencoded ?
elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded")
then
return urldecode_message_body( src, msg, filecb )
-- Unhandled encoding
-- If a file callback is given then feed it chunk by chunk, else
-- store whole buffer in message.content
else
local sink
-- If we have a file callback then feed it
if type(filecb) == "function" then
sink = filecb
-- ... else append to .content
else
msg.content = ""
msg.content_length = 0
sink = function( chunk, err )
if chunk then
if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
msg.content = msg.content .. chunk
msg.content_length = msg.content_length + #chunk
return true
else
return nil, "POST data exceeds maximum allowed length"
end
end
return true
end
end
-- Pump data...
while true do
local ok, err = ltn12.pump.step( src, sink )
if not ok and err then
return nil, err
elseif not err then
return true
end
end
return true
end
end
--- Table containing human readable messages for several http status codes.
-- @class table
statusmsg = {
[200] = "OK",
[206] = "Partial Content",
[301] = "Moved Permanently",
[302] = "Found",
[304] = "Not Modified",
[400] = "Bad Request",
[403] = "Forbidden",
[404] = "Not Found",
[405] = "Method Not Allowed",
[408] = "Request Time-out",
[411] = "Length Required",
[412] = "Precondition Failed",
[416] = "Requested range not satisfiable",
[500] = "Internal Server Error",
[503] = "Server Unavailable",
}

View File

@ -0,0 +1,153 @@
--[[
HTTP protocol implementation for LuCI - RFC2616 / 14.19, 14.24 - 14.28
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: conditionals.lua 5637 2009-12-20 18:35:05Z jow $
]]--
--- LuCI http protocol implementation - HTTP/1.1 bits.
-- This class provides basic ETag handling and implements most of the
-- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
module("luci.http.protocol.conditionals", package.seeall)
local date = require("luci.http.protocol.date")
--- Implement 14.19 / ETag.
-- @param stat A file.stat structure
-- @return String containing the generated tag suitable for ETag headers
function mk_etag( stat )
if stat ~= nil then
return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
end
end
--- 14.24 / If-Match
-- Test whether the given message object contains an "If-Match" header and
-- compare it against the given stat object.
-- @param req HTTP request message object
-- @param stat A file.stat object
-- @return Boolean indicating whether the precondition is ok
-- @return Alternative status code if the precondition failed
function if_match( req, stat )
local h = req.headers
local etag = mk_etag( stat )
-- Check for matching resource
if type(h['If-Match']) == "string" then
for ent in h['If-Match']:gmatch("([^, ]+)") do
if ( ent == '*' or ent == etag ) and stat ~= nil then
return true
end
end
return false, 412
end
return true
end
--- 14.25 / If-Modified-Since
-- Test whether the given message object contains an "If-Modified-Since" header
-- and compare it against the given stat object.
-- @param req HTTP request message object
-- @param stat A file.stat object
-- @return Boolean indicating whether the precondition is ok
-- @return Alternative status code if the precondition failed
-- @return Table containing extra HTTP headers if the precondition failed
function if_modified_since( req, stat )
local h = req.headers
-- Compare mtimes
if type(h['If-Modified-Since']) == "string" then
local since = date.to_unix( h['If-Modified-Since'] )
if stat == nil or since < stat.mtime then
return true
end
return false, 304, {
["ETag"] = mk_etag( stat );
["Date"] = date.to_http( os.time() );
["Last-Modified"] = date.to_http( stat.mtime )
}
end
return true
end
--- 14.26 / If-None-Match
-- Test whether the given message object contains an "If-None-Match" header and
-- compare it against the given stat object.
-- @param req HTTP request message object
-- @param stat A file.stat object
-- @return Boolean indicating whether the precondition is ok
-- @return Alternative status code if the precondition failed
-- @return Table containing extra HTTP headers if the precondition failed
function if_none_match( req, stat )
local h = req.headers
local etag = mk_etag( stat )
local method = req.env and req.env.REQUEST_METHOD or "GET"
-- Check for matching resource
if type(h['If-None-Match']) == "string" then
for ent in h['If-None-Match']:gmatch("([^, ]+)") do
if ( ent == '*' or ent == etag ) and stat ~= nil then
if method == "GET" or method == "HEAD" then
return false, 304, {
["ETag"] = etag;
["Date"] = date.to_http( os.time() );
["Last-Modified"] = date.to_http( stat.mtime )
}
else
return false, 412
end
end
end
end
return true
end
--- 14.27 / If-Range
-- The If-Range header is currently not implemented due to the lack of general
-- byte range stuff in luci.http.protocol . This function will always return
-- false, 412 to indicate a failed precondition.
-- @param req HTTP request message object
-- @param stat A file.stat object
-- @return Boolean indicating whether the precondition is ok
-- @return Alternative status code if the precondition failed
function if_range( req, stat )
-- Sorry, no subranges (yet)
return false, 412
end
--- 14.28 / If-Unmodified-Since
-- Test whether the given message object contains an "If-Unmodified-Since"
-- header and compare it against the given stat object.
-- @param req HTTP request message object
-- @param stat A file.stat object
-- @return Boolean indicating whether the precondition is ok
-- @return Alternative status code if the precondition failed
function if_unmodified_since( req, stat )
local h = req.headers
-- Compare mtimes
if type(h['If-Unmodified-Since']) == "string" then
local since = date.to_unix( h['If-Unmodified-Since'] )
if stat ~= nil and since <= stat.mtime then
return false, 412
end
end
return true
end

View File

@ -0,0 +1,115 @@
--[[
HTTP protocol implementation for LuCI - date handling
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: date.lua 3860 2008-12-06 03:18:14Z jow $
]]--
--- LuCI http protocol implementation - date helper class.
-- This class contains functions to parse, compare and format http dates.
module("luci.http.protocol.date", package.seeall)
require("luci.sys.zoneinfo")
MONTHS = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
}
--- Return the time offset in seconds between the UTC and given time zone.
-- @param tz Symbolic or numeric timezone specifier
-- @return Time offset to UTC in seconds
function tz_offset(tz)
if type(tz) == "string" then
-- check for a numeric identifier
local s, v = tz:match("([%+%-])([0-9]+)")
if s == '+' then s = 1 else s = -1 end
if v then v = tonumber(v) end
if s and v then
return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) )
-- lookup symbolic tz
elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then
return luci.sys.zoneinfo.OFFSET[tz:lower()]
end
end
-- bad luck
return 0
end
--- Parse given HTTP date string and convert it to unix epoch time.
-- @param data String containing the date
-- @return Unix epoch time
function to_unix(date)
local wd, day, mon, yr, hr, min, sec, tz = date:match(
"([A-Z][a-z][a-z]), ([0-9]+) " ..
"([A-Z][a-z][a-z]) ([0-9]+) " ..
"([0-9]+):([0-9]+):([0-9]+) " ..
"([A-Z0-9%+%-]+)"
)
if day and mon and yr and hr and min and sec then
-- find month
local month = 1
for i = 1, 12 do
if MONTHS[i] == mon then
month = i
break
end
end
-- convert to epoch time
return tz_offset(tz) + os.time( {
year = yr,
month = month,
day = day,
hour = hr,
min = min,
sec = sec
} )
end
return 0
end
--- Convert the given unix epoch time to valid HTTP date string.
-- @param time Unix epoch time
-- @return String containing the formatted date
function to_http(time)
return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
end
--- Compare two dates which can either be unix epoch times or HTTP date strings.
-- @param d1 The first date or epoch time to compare
-- @param d2 The first date or epoch time to compare
-- @return -1 - if d1 is lower then d2
-- @return 0 - if both dates are equal
-- @return 1 - if d1 is higher then d2
function compare(d1, d2)
if d1:match("[^0-9]") then d1 = to_unix(d1) end
if d2:match("[^0-9]") then d2 = to_unix(d2) end
if d1 == d2 then
return 0
elseif d1 < d2 then
return -1
else
return 1
end
end

View File

@ -0,0 +1,99 @@
--[[
HTTP protocol implementation for LuCI - mime handling
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: mime.lua 5327 2009-09-11 01:55:10Z jow $
]]--
--- LuCI http protocol implementation - mime helper class.
-- This class provides functions to guess mime types from file extensions and
-- vice versa.
module("luci.http.protocol.mime", package.seeall)
require("luci.util")
--- MIME mapping table containg extension - mimetype relations.
-- @class table
MIME_TYPES = {
["txt"] = "text/plain";
["js"] = "text/javascript";
["css"] = "text/css";
["htm"] = "text/html";
["html"] = "text/html";
["patch"] = "text/x-patch";
["c"] = "text/x-csrc";
["h"] = "text/x-chdr";
["o"] = "text/x-object";
["ko"] = "text/x-object";
["bmp"] = "image/bmp";
["gif"] = "image/gif";
["png"] = "image/png";
["jpg"] = "image/jpeg";
["jpeg"] = "image/jpeg";
["svg"] = "image/svg+xml";
["zip"] = "application/zip";
["pdf"] = "application/pdf";
["xml"] = "application/xml";
["xsl"] = "application/xml";
["doc"] = "application/msword";
["ppt"] = "application/vnd.ms-powerpoint";
["xls"] = "application/vnd.ms-excel";
["odt"] = "application/vnd.oasis.opendocument.text";
["odp"] = "application/vnd.oasis.opendocument.presentation";
["pl"] = "application/x-perl";
["sh"] = "application/x-shellscript";
["php"] = "application/x-php";
["deb"] = "application/x-deb";
["iso"] = "application/x-cd-image";
["tgz"] = "application/x-compressed-tar";
["mp3"] = "audio/mpeg";
["ogg"] = "audio/x-vorbis+ogg";
["wav"] = "audio/x-wav";
["mpg"] = "video/mpeg";
["mpeg"] = "video/mpeg";
["avi"] = "video/x-msvideo";
}
--- Extract extension from a filename and return corresponding mime-type or
-- "application/octet-stream" if the extension is unknown.
-- @param filename The filename for which the mime type is guessed
-- @return String containign the determined mime type
function to_mime(filename)
if type(filename) == "string" then
local ext = filename:match("[^%.]+$")
if ext and MIME_TYPES[ext:lower()] then
return MIME_TYPES[ext:lower()]
end
end
return "application/octet-stream"
end
--- Return corresponding extension for a given mime type or nil if the
-- given mime-type is unknown.
-- @param mimetype The mimetype to retrieve the extension from
-- @return String with the extension or nil for unknown type
function to_ext(mimetype)
if type(mimetype) == "string" then
for ext, type in luci.util.kspairs( MIME_TYPES ) do
if type == mimetype then
return ext
end
end
end
return nil
end

104
1_1.mi_Lua/luci/i18n.lua Normal file
View File

@ -0,0 +1,104 @@
--[[
LuCI - Internationalisation
Description:
A very minimalistic but yet effective internationalisation module
FileId:
$Id: i18n.lua 9558 2012-12-18 13:58:22Z jow $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
--- LuCI translation library.
module("luci.i18n", package.seeall)
require("luci.util")
local tparser = require "luci.template.parser"
table = {}
i18ndir = luci.util.libpath() .. "/i18n/"
loaded = {}
context = luci.util.threadlocal()
default = "en"
--- Clear the translation table.
function clear()
end
--- Load a translation and copy its data into the translation table.
-- @param file Language file
-- @param lang Two-letter language code
-- @param force Force reload even if already loaded (optional)
-- @return Success status
function load(file, lang, force)
end
--- Load a translation file using the default translation language.
-- Alternatively load the translation of the fallback language.
-- @param file Language file
-- @param force Force reload even if already loaded (optional)
function loadc(file, force)
end
--- Set the context default translation language.
-- @param lang Two-letter language code
function setlanguage(lang)
context.lang = lang:gsub("_", "-")
context.parent = (context.lang:match("^([a-z][a-z])_"))
if not tparser.load_catalog(context.lang, i18ndir) then
if context.parent then
tparser.load_catalog(context.parent, i18ndir)
return context.parent
end
end
return context.lang
end
--- Return the translated value for a specific translation key.
-- @param key Default translation text
-- @return Translated string
function translate(key)
return tparser.translate(key) or key
end
--- Return the translated value for a specific translation key and use it as sprintf pattern.
-- @param key Default translation text
-- @param ... Format parameters
-- @return Translated and formatted string
function translatef(key, ...)
return tostring(translate(key)):format(...)
end
--- Return the translated value for a specific translation key
-- and ensure that the returned value is a Lua string value.
-- This is the same as calling <code>tostring(translate(...))</code>
-- @param key Default translation text
-- @return Translated string
function string(key)
return tostring(translate(key))
end
--- Return the translated value for a specific translation key and use it as sprintf pattern.
-- Ensure that the returned value is a Lua string value.
-- This is the same as calling <code>tostring(translatef(...))</code>
-- @param key Default translation text
-- @param ... Format parameters
-- @return Translated and formatted string
function stringf(key, ...)
return tostring(translate(key)):format(...)
end

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.

39
1_1.mi_Lua/luci/init.lua Normal file
View File

@ -0,0 +1,39 @@
--[[
LuCI - Lua Configuration Interface
Description:
Main class
FileId:
$Id: init.lua 7365 2011-08-13 09:52:50Z jow $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local require = require
-- Make sure that bitlib is loaded
if not _G.bit then
_G.bit = require "bit"
end
module "luci"
local v = require "luci.version"
__version__ = v.luciversion or "trunk"
__appname__ = v.luciname or "LuCI"

684
1_1.mi_Lua/luci/ip.lua Normal file
View File

@ -0,0 +1,684 @@
--[[
LuCI ip calculation libarary
(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
(c) 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: ip.lua 8142 2012-01-01 15:51:37Z jow $
]]--
--- LuCI IP calculation library.
module( "luci.ip", package.seeall )
require "nixio"
local bit = nixio.bit
local util = require "luci.util"
--- Boolean; true if system is little endian
LITTLE_ENDIAN = not util.bigendian()
--- Boolean; true if system is big endian
BIG_ENDIAN = not LITTLE_ENDIAN
--- Specifier for IPv4 address family
FAMILY_INET4 = 0x04
--- Specifier for IPv6 address family
FAMILY_INET6 = 0x06
local function __bless(x)
return setmetatable( x, {
__index = luci.ip.cidr,
__add = luci.ip.cidr.add,
__sub = luci.ip.cidr.sub,
__lt = luci.ip.cidr.lower,
__eq = luci.ip.cidr.equal,
__le =
function(...)
return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...)
end
} )
end
local function __array16( x, family )
local list
if type(x) == "number" then
list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) }
elseif type(x) == "string" then
if x:find(":") then x = IPv6(x) else x = IPv4(x) end
if x then
assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
list = { unpack(x[2]) }
end
elseif type(x) == "table" and type(x[2]) == "table" then
assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
list = { unpack(x[2]) }
elseif type(x) == "table" then
list = { unpack(x) }
end
assert( list, "Invalid operand" )
return list
end
local function __mask16(bits)
return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 )
end
local function __not16(bits)
return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF )
end
local function __maxlen(family)
return ( family == FAMILY_INET4 ) and 32 or 128
end
local function __sublen(family)
return ( family == FAMILY_INET4 ) and 30 or 127
end
--- Convert given short value to network byte order on little endian hosts
-- @param x Unsigned integer value between 0x0000 and 0xFFFF
-- @return Byte-swapped value
-- @see htonl
-- @see ntohs
function htons(x)
if LITTLE_ENDIAN then
return bit.bor(
bit.rshift( x, 8 ),
bit.band( bit.lshift( x, 8 ), 0xFF00 )
)
else
return x
end
end
--- Convert given long value to network byte order on little endian hosts
-- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF
-- @return Byte-swapped value
-- @see htons
-- @see ntohl
function htonl(x)
if LITTLE_ENDIAN then
return bit.bor(
bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ),
htons( bit.rshift( x, 16 ) )
)
else
return x
end
end
--- Convert given short value to host byte order on little endian hosts
-- @class function
-- @name ntohs
-- @param x Unsigned integer value between 0x0000 and 0xFFFF
-- @return Byte-swapped value
-- @see htonl
-- @see ntohs
ntohs = htons
--- Convert given short value to host byte order on little endian hosts
-- @class function
-- @name ntohl
-- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF
-- @return Byte-swapped value
-- @see htons
-- @see ntohl
ntohl = htonl
--- Parse given IPv4 address in dotted quad or CIDR notation. If an optional
-- netmask is given as second argument and the IP address is encoded in CIDR
-- notation then the netmask parameter takes precedence. If neither a CIDR
-- encoded prefix nor a netmask parameter is given, then a prefix length of
-- 32 bit is assumed.
-- @param address IPv4 address in dotted quad or CIDR notation
-- @param netmask IPv4 netmask in dotted quad notation (optional)
-- @return luci.ip.cidr instance or nil if given address was invalid
-- @see IPv6
-- @see Hex
function IPv4(address, netmask)
address = address or "0.0.0.0/0"
local obj = __bless({ FAMILY_INET4 })
local data = {}
local prefix = address:match("/(.+)")
address = address:gsub("/.+","")
address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "")
if netmask then
prefix = obj:prefix(netmask)
elseif prefix then
prefix = tonumber(prefix)
if not prefix or prefix < 0 or prefix > 32 then return nil end
else
prefix = 32
end
local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
b1 = tonumber(b1)
b2 = tonumber(b2)
b3 = tonumber(b3)
b4 = tonumber(b4)
if b1 and b1 <= 255 and
b2 and b2 <= 255 and
b3 and b3 <= 255 and
b4 and b4 <= 255 and
prefix
then
table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 })
table.insert(obj, prefix)
return obj
end
end
--- Parse given IPv6 address in full, compressed, mixed or CIDR notation.
-- If an optional netmask is given as second argument and the IP address is
-- encoded in CIDR notation then the netmask parameter takes precedence.
-- If neither a CIDR encoded prefix nor a netmask parameter is given, then a
-- prefix length of 128 bit is assumed.
-- @param address IPv6 address in full/compressed/mixed or CIDR notation
-- @param netmask IPv6 netmask in full/compressed/mixed notation (optional)
-- @return luci.ip.cidr instance or nil if given address was invalid
-- @see IPv4
-- @see Hex
function IPv6(address, netmask)
address = address or "::/0"
local obj = __bless({ FAMILY_INET6 })
local data = {}
local prefix = address:match("/(.+)")
address = address:gsub("/.+","")
address = address:gsub("^%[(.*)%]$", "%1")
if netmask then
prefix = obj:prefix(netmask)
elseif prefix then
prefix = tonumber(prefix)
if not prefix or prefix < 0 or prefix > 128 then return nil end
else
prefix = 128
end
local borderl = address:sub(1, 1) == ":" and 2 or 1
local borderh, zeroh, chunk, block, i
if #address > 45 then return nil end
repeat
borderh = address:find(":", borderl, true)
if not borderh then break end
block = tonumber(address:sub(borderl, borderh - 1), 16)
if block and block <= 0xFFFF then
data[#data+1] = block
else
if zeroh or borderh - borderl > 1 then return nil end
zeroh = #data + 1
end
borderl = borderh + 1
until #data == 7
chunk = address:sub(borderl)
if #chunk > 0 and #chunk <= 4 then
block = tonumber(chunk, 16)
if not block or block > 0xFFFF then return nil end
data[#data+1] = block
elseif #chunk > 4 then
if #data == 7 or #chunk > 15 then return nil end
borderl = 1
for i=1, 4 do
borderh = chunk:find(".", borderl, true)
if not borderh and i < 4 then return nil end
borderh = borderh and borderh - 1
block = tonumber(chunk:sub(borderl, borderh))
if not block or block > 255 then return nil end
if i == 1 or i == 3 then
data[#data+1] = block * 256
else
data[#data] = data[#data] + block
end
borderl = borderh and borderh + 2
end
end
if zeroh then
if #data == 8 then return nil end
while #data < 8 do
table.insert(data, zeroh, 0)
end
end
if #data == 8 and prefix then
table.insert(obj, data)
table.insert(obj, prefix)
return obj
end
end
--- Transform given hex-encoded value to luci.ip.cidr instance of specified
-- address family.
-- @param hex String containing hex encoded value
-- @param prefix Prefix length of CIDR instance (optional, default is 32/128)
-- @param family Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6
-- @param swap Bool indicating whether to swap byteorder on low endian host
-- @return luci.ip.cidr instance or nil if given value was invalid
-- @see IPv4
-- @see IPv6
function Hex( hex, prefix, family, swap )
family = ( family ~= nil ) and family or FAMILY_INET4
swap = ( swap == nil ) and true or swap
prefix = prefix or __maxlen(family)
local len = __maxlen(family)
local tmp = ""
local data = { }
local i
for i = 1, (len/4) - #hex do tmp = tmp .. '0' end
if swap and LITTLE_ENDIAN then
for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end
else
tmp = tmp .. hex
end
hex = tmp
for i = 1, ( len / 4 ), 4 do
local n = tonumber( hex:sub( i, i+3 ), 16 )
if n then
data[#data+1] = n
else
return nil
end
end
return __bless({ family, data, prefix })
end
--- LuCI IP Library / CIDR instances
-- @class module
-- @cstyle instance
-- @name luci.ip.cidr
cidr = util.class()
--- Test whether the instance is a IPv4 address.
-- @return Boolean indicating a IPv4 address type
-- @see cidr.is6
function cidr.is4( self )
return self[1] == FAMILY_INET4
end
--- Test whether this instance is an IPv4 RFC1918 private address
-- @return Boolean indicating whether this instance is an RFC1918 address
function cidr.is4rfc1918( self )
if self[1] == FAMILY_INET4 then
return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or
((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or
(self[2][1] == 0xC0A8)
end
return false
end
--- Test whether this instance is an IPv4 link-local address (Zeroconf)
-- @return Boolean indicating whether this instance is IPv4 link-local
function cidr.is4linklocal( self )
if self[1] == FAMILY_INET4 then
return (self[2][1] == 0xA9FE)
end
return false
end
--- Test whether the instance is a IPv6 address.
-- @return Boolean indicating a IPv6 address type
-- @see cidr.is4
function cidr.is6( self )
return self[1] == FAMILY_INET6
end
--- Test whether this instance is an IPv6 link-local address
-- @return Boolean indicating whether this instance is IPv6 link-local
function cidr.is6linklocal( self )
if self[1] == FAMILY_INET6 then
return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF)
end
return false
end
--- Return a corresponding string representation of the instance.
-- If the prefix length is lower then the maximum possible prefix length for the
-- corresponding address type then the address is returned in CIDR notation,
-- otherwise the prefix will be left out.
function cidr.string( self )
local str
if self:is4() then
str = string.format(
"%d.%d.%d.%d",
bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF),
bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF)
)
if self[3] < 32 then
str = str .. "/" .. self[3]
end
elseif self:is6() then
str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) )
if self[3] < 128 then
str = str .. "/" .. self[3]
end
end
return str
end
--- Test whether the value of the instance is lower then the given address.
-- This function will throw an exception if the given address has a different
-- family than this instance.
-- @param addr A luci.ip.cidr instance to compare against
-- @return Boolean indicating whether this instance is lower
-- @see cidr.higher
-- @see cidr.equal
function cidr.lower( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
local i
for i = 1, #self[2] do
if self[2][i] ~= addr[2][i] then
return self[2][i] < addr[2][i]
end
end
return false
end
--- Test whether the value of the instance is higher then the given address.
-- This function will throw an exception if the given address has a different
-- family than this instance.
-- @param addr A luci.ip.cidr instance to compare against
-- @return Boolean indicating whether this instance is higher
-- @see cidr.lower
-- @see cidr.equal
function cidr.higher( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
local i
for i = 1, #self[2] do
if self[2][i] ~= addr[2][i] then
return self[2][i] > addr[2][i]
end
end
return false
end
--- Test whether the value of the instance is equal to the given address.
-- This function will throw an exception if the given address is a different
-- family than this instance.
-- @param addr A luci.ip.cidr instance to compare against
-- @return Boolean indicating whether this instance is equal
-- @see cidr.lower
-- @see cidr.higher
function cidr.equal( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
local i
for i = 1, #self[2] do
if self[2][i] ~= addr[2][i] then
return false
end
end
return true
end
--- Return the prefix length of this CIDR instance.
-- @param mask Override instance prefix with given netmask (optional)
-- @return Prefix length in bit
function cidr.prefix( self, mask )
local prefix = self[3]
if mask then
prefix = 0
local stop = false
local obj = type(mask) ~= "table"
and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask
if not obj then return nil end
local _, word
for _, word in ipairs(obj[2]) do
if word == 0xFFFF then
prefix = prefix + 16
else
local bitmask = bit.lshift(1, 15)
while bit.band(word, bitmask) == bitmask do
prefix = prefix + 1
bitmask = bit.lshift(1, 15 - (prefix % 16))
end
break
end
end
end
return prefix
end
--- Return a corresponding CIDR representing the network address of this
-- instance.
-- @param bits Override prefix length of this instance (optional)
-- @return CIDR instance containing the network address
-- @see cidr.host
-- @see cidr.broadcast
-- @see cidr.mask
function cidr.network( self, bits )
local data = { }
bits = bits or self[3]
local i
for i = 1, math.floor( bits / 16 ) do
data[#data+1] = self[2][i]
end
if #data < #self[2] then
data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) )
for i = #data + 1, #self[2] do
data[#data+1] = 0
end
end
return __bless({ self[1], data, __maxlen(self[1]) })
end
--- Return a corresponding CIDR representing the host address of this
-- instance. This is intended to extract the host address from larger subnet.
-- @return CIDR instance containing the network address
-- @see cidr.network
-- @see cidr.broadcast
-- @see cidr.mask
function cidr.host( self )
return __bless({ self[1], self[2], __maxlen(self[1]) })
end
--- Return a corresponding CIDR representing the netmask of this instance.
-- @param bits Override prefix length of this instance (optional)
-- @return CIDR instance containing the netmask
-- @see cidr.network
-- @see cidr.host
-- @see cidr.broadcast
function cidr.mask( self, bits )
local data = { }
bits = bits or self[3]
for i = 1, math.floor( bits / 16 ) do
data[#data+1] = 0xFFFF
end
if #data < #self[2] then
data[#data+1] = __mask16(bits)
for i = #data + 1, #self[2] do
data[#data+1] = 0
end
end
return __bless({ self[1], data, __maxlen(self[1]) })
end
--- Return CIDR containing the broadcast address of this instance.
-- @return CIDR instance containing the netmask, always nil for IPv6
-- @see cidr.network
-- @see cidr.host
-- @see cidr.mask
function cidr.broadcast( self )
-- IPv6 has no broadcast addresses (XXX: assert() instead?)
if self[1] == FAMILY_INET4 then
local data = { unpack(self[2]) }
local offset = math.floor( self[3] / 16 ) + 1
if offset <= #data then
data[offset] = bit.bor( data[offset], __not16(self[3]) )
for i = offset + 1, #data do data[i] = 0xFFFF end
return __bless({ self[1], data, __maxlen(self[1]) })
end
end
end
--- Test whether this instance fully contains the given CIDR instance.
-- @param addr CIDR instance to test against
-- @return Boolean indicating whether this instance contains the given CIDR
function cidr.contains( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
if self:prefix() <= addr:prefix() then
return self:network() == addr:network(self:prefix())
end
return false
end
--- Add specified amount of hosts to this instance.
-- @param amount Number of hosts to add to this instance
-- @param inplace Boolen indicating whether to alter values inplace (optional)
-- @return CIDR representing the new address or nil on overflow error
-- @see cidr.sub
function cidr.add( self, amount, inplace )
local pos
local data = { unpack(self[2]) }
local shorts = __array16( amount, self[1] )
for pos = #data, 1, -1 do
local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
if ( data[pos] + add ) > 0xFFFF then
data[pos] = ( data[pos] + add ) % 0xFFFF
if pos > 1 then
data[pos-1] = data[pos-1] + ( add - data[pos] )
else
return nil
end
else
data[pos] = data[pos] + add
end
end
if inplace then
self[2] = data
return self
else
return __bless({ self[1], data, self[3] })
end
end
--- Substract specified amount of hosts from this instance.
-- @param amount Number of hosts to substract from this instance
-- @param inplace Boolen indicating whether to alter values inplace (optional)
-- @return CIDR representing the new address or nil on underflow error
-- @see cidr.add
function cidr.sub( self, amount, inplace )
local pos
local data = { unpack(self[2]) }
local shorts = __array16( amount, self[1] )
for pos = #data, 1, -1 do
local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
if ( data[pos] - sub ) < 0 then
data[pos] = ( sub - data[pos] ) % 0xFFFF
if pos > 1 then
data[pos-1] = data[pos-1] - ( sub + data[pos] )
else
return nil
end
else
data[pos] = data[pos] - sub
end
end
if inplace then
self[2] = data
return self
else
return __bless({ self[1], data, self[3] })
end
end
--- Return CIDR containing the lowest available host address within this subnet.
-- @return CIDR containing the host address, nil if subnet is too small
-- @see cidr.maxhost
function cidr.minhost( self )
if self[3] <= __sublen(self[1]) then
-- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6
return self:network():add(1, true)
end
end
--- Return CIDR containing the highest available host address within the subnet.
-- @return CIDR containing the host address, nil if subnet is too small
-- @see cidr.minhost
function cidr.maxhost( self )
if self[3] <= __sublen(self[1]) then
local i
local data = { unpack(self[2]) }
local offset = math.floor( self[3] / 16 ) + 1
data[offset] = bit.bor( data[offset], __not16(self[3]) )
for i = offset + 1, #data do data[i] = 0xFFFF end
data = __bless({ self[1], data, __maxlen(self[1]) })
-- Last address in reserved for Broadcast Address in IPv4
if data[1] == FAMILY_INET4 then data:sub(1, true) end
return data
end
end
function iptonl( ip )
local a,b,c,d = ip:match("^(%d+).(%d+).(%d+).(%d+)")
local ipnl = bit.lshift(a, 24) + bit.lshift(b, 16) + bit.lshift(c, 8) + d
return ipnl
end
function ipnot ( ip )
local ipnl = iptonl(ip)
return bit.band(bit.bnot(ipnl),0xFFFFFFFF)
end

566
1_1.mi_Lua/luci/json.lua Normal file
View File

@ -0,0 +1,566 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: json.lua 6988 2011-04-17 11:39:17Z jow $
Decoder:
Info:
null will be decoded to luci.json.null if first parameter of Decoder() is true
Example:
decoder = luci.json.Decoder()
luci.ltn12.pump.all(luci.ltn12.source.string("decodableJSON"), decoder:sink())
luci.util.dumptable(decoder:get())
Known issues:
does not support unicode conversion \uXXYY with XX != 00 will be ignored
Encoder:
Info:
Accepts numbers, strings, nil, booleans as they are
Accepts luci.json.null as replacement for nil
Accepts full associative and full numerically indexed tables
Mixed tables will loose their associative values during conversion
Iterator functions will be encoded as an array of their return values
Non-iterator functions will probably corrupt the encoder
Example:
encoder = luci.json.Encoder(encodableData)
luci.ltn12.pump.all(encoder:source(), luci.ltn12.sink.file(io.open("someFile", w)))
]]--
local util = require "luci.util"
local table = require "table"
local string = require "string"
local coroutine = require "coroutine"
local assert = assert
local tonumber = tonumber
local tostring = tostring
local error = error
local type = type
local pairs = pairs
local ipairs = ipairs
local next = next
local pcall = pcall
local getmetatable = getmetatable
--- LuCI JSON-Library
-- @cstyle instance
module "luci.json"
--- Directly decode a JSON string
-- @param json JSON-String
-- @return Lua object
function decode(json, ...)
local a = ActiveDecoder(function() return nil end, ...)
a.chunk = json
local s, obj = pcall(a.get, a)
return s and obj or nil
end
--- Direcly encode a Lua object into a JSON string.
-- @param obj Lua Object
-- @return JSON string
function encode(obj, ...)
local out = {}
local e = Encoder(obj, 1, ...):source()
local chnk, err
repeat
chnk, err = e()
out[#out+1] = chnk
until not chnk
return not err and table.concat(out) or nil
end
--- Null replacement function
-- @return null
function null()
return null
end
--- Create a new JSON-Encoder.
-- @class function
-- @name Encoder
-- @param data Lua-Object to be encoded.
-- @param buffersize Blocksize of returned data source.
-- @param fastescape Use non-standard escaping (don't escape control chars)
-- @return JSON-Encoder
Encoder = util.class()
function Encoder.__init__(self, data, buffersize, fastescape)
self.data = data
self.buffersize = buffersize or 512
self.buffer = ""
self.fastescape = fastescape
getmetatable(self).__call = Encoder.source
end
--- Create an LTN12 source providing the encoded JSON-Data.
-- @return LTN12 source
function Encoder.source(self)
local source = coroutine.create(self.dispatch)
return function()
local res, data = coroutine.resume(source, self, self.data, true)
if res then
return data
else
return nil, data
end
end
end
function Encoder.dispatch(self, data, start)
local parser = self.parsers[type(data)]
parser(self, data)
if start then
if #self.buffer > 0 then
coroutine.yield(self.buffer)
end
coroutine.yield()
end
end
function Encoder.put(self, chunk)
if self.buffersize < 2 then
coroutine.yield(chunk)
else
if #self.buffer + #chunk > self.buffersize then
local written = 0
local fbuffer = self.buffersize - #self.buffer
coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
written = fbuffer
while #chunk - written > self.buffersize do
fbuffer = written + self.buffersize
coroutine.yield(chunk:sub(written + 1, fbuffer))
written = fbuffer
end
self.buffer = chunk:sub(written + 1)
else
self.buffer = self.buffer .. chunk
end
end
end
function Encoder.parse_nil(self)
self:put("null")
end
function Encoder.parse_bool(self, obj)
self:put(obj and "true" or "false")
end
function Encoder.parse_number(self, obj)
self:put(tostring(obj))
end
function Encoder.parse_string(self, obj)
if self.fastescape then
self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
else
self:put('"' ..
obj:gsub('[%c\\"]',
function(char)
return '\\u00%02x' % char:byte()
end
)
.. '"')
end
end
function Encoder.parse_iter(self, obj)
if obj == null then
return self:put("null")
end
if type(obj) == "table" and (#obj == 0 and next(obj)) then
self:put("{")
local first = true
for key, entry in pairs(obj) do
first = first or self:put(",")
first = first and false
self:parse_string(tostring(key))
self:put(":")
self:dispatch(entry)
end
self:put("}")
else
self:put("[")
local first = true
if type(obj) == "table" then
for i=1, #obj do
first = first or self:put(",")
first = first and nil
self:dispatch(obj[i])
end
else
for entry in obj do
first = first or self:put(",")
first = first and nil
self:dispatch(entry)
end
end
self:put("]")
end
end
Encoder.parsers = {
['nil'] = Encoder.parse_nil,
['table'] = Encoder.parse_iter,
['number'] = Encoder.parse_number,
['string'] = Encoder.parse_string,
['boolean'] = Encoder.parse_bool,
['function'] = Encoder.parse_iter
}
--- Create a new JSON-Decoder.
-- @class function
-- @name Decoder
-- @param customnull Use luci.json.null instead of nil for decoding null
-- @return JSON-Decoder
Decoder = util.class()
function Decoder.__init__(self, customnull)
self.cnull = customnull
getmetatable(self).__call = Decoder.sink
end
--- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
-- @return LTN12 sink
function Decoder.sink(self)
local sink = coroutine.create(self.dispatch)
return function(...)
return coroutine.resume(sink, self, ...)
end
end
--- Get the decoded data packets after the rawdata has been sent to the sink.
-- @return Decoded data
function Decoder.get(self)
return self.data
end
function Decoder.dispatch(self, chunk, src_err, strict)
local robject, object
local oset = false
while chunk do
while chunk and #chunk < 1 do
chunk = self:fetch()
end
assert(not strict or chunk, "Unexpected EOS")
if not chunk then break end
local char = chunk:sub(1, 1)
local parser = self.parsers[char]
or (char:match("%s") and self.parse_space)
or (char:match("[0-9-]") and self.parse_number)
or error("Unexpected char '%s'" % char)
chunk, robject = parser(self, chunk)
if parser ~= self.parse_space then
assert(not oset, "Scope violation: Too many objects")
object = robject
oset = true
if strict then
return chunk, object
end
end
end
assert(not src_err, src_err)
assert(oset, "Unexpected EOS")
self.data = object
end
function Decoder.fetch(self)
local tself, chunk, src_err = coroutine.yield()
assert(chunk or not src_err, src_err)
return chunk
end
function Decoder.fetch_atleast(self, chunk, bytes)
while #chunk < bytes do
local nchunk = self:fetch()
assert(nchunk, "Unexpected EOS")
chunk = chunk .. nchunk
end
return chunk
end
function Decoder.fetch_until(self, chunk, pattern)
local start = chunk:find(pattern)
while not start do
local nchunk = self:fetch()
assert(nchunk, "Unexpected EOS")
chunk = chunk .. nchunk
start = chunk:find(pattern)
end
return chunk, start
end
function Decoder.parse_space(self, chunk)
local start = chunk:find("[^%s]")
while not start do
chunk = self:fetch()
if not chunk then
return nil
end
start = chunk:find("[^%s]")
end
return chunk:sub(start)
end
function Decoder.parse_literal(self, chunk, literal, value)
chunk = self:fetch_atleast(chunk, #literal)
assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
return chunk:sub(#literal + 1), value
end
function Decoder.parse_null(self, chunk)
return self:parse_literal(chunk, "null", self.cnull and null)
end
function Decoder.parse_true(self, chunk)
return self:parse_literal(chunk, "true", true)
end
function Decoder.parse_false(self, chunk)
return self:parse_literal(chunk, "false", false)
end
function Decoder.parse_number(self, chunk)
local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
local number = tonumber(chunk:sub(1, start - 1))
assert(number, "Invalid number specification")
return chunk:sub(start), number
end
function Decoder.parse_string(self, chunk)
local str = ""
local object = nil
assert(chunk:sub(1, 1) == '"', 'Expected "')
chunk = chunk:sub(2)
while true do
local spos = chunk:find('[\\"]')
if spos then
str = str .. chunk:sub(1, spos - 1)
local char = chunk:sub(spos, spos)
if char == '"' then -- String end
chunk = chunk:sub(spos + 1)
break
elseif char == "\\" then -- Escape sequence
chunk, object = self:parse_escape(chunk:sub(spos))
str = str .. object
end
else
str = str .. chunk
chunk = self:fetch()
assert(chunk, "Unexpected EOS while parsing a string")
end
end
return chunk, str
end
function Decoder.parse_escape(self, chunk)
local str = ""
chunk = self:fetch_atleast(chunk:sub(2), 1)
local char = chunk:sub(1, 1)
chunk = chunk:sub(2)
if char == '"' then
return chunk, '"'
elseif char == "\\" then
return chunk, "\\"
elseif char == "u" then
chunk = self:fetch_atleast(chunk, 4)
local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
assert(s1 and s2, "Invalid Unicode character")
-- ToDo: Unicode support
return chunk:sub(5), s1 == 0 and string.char(s2) or ""
elseif char == "/" then
return chunk, "/"
elseif char == "b" then
return chunk, "\b"
elseif char == "f" then
return chunk, "\f"
elseif char == "n" then
return chunk, "\n"
elseif char == "r" then
return chunk, "\r"
elseif char == "t" then
return chunk, "\t"
else
error("Unexpected escaping sequence '\\%s'" % char)
end
end
function Decoder.parse_array(self, chunk)
chunk = chunk:sub(2)
local array = {}
local nextp = 1
local chunk, object = self:parse_delimiter(chunk, "%]")
if object then
return chunk, array
end
repeat
chunk, object = self:dispatch(chunk, nil, true)
table.insert(array, nextp, object)
nextp = nextp + 1
chunk, object = self:parse_delimiter(chunk, ",%]")
assert(object, "Delimiter expected")
until object == "]"
return chunk, array
end
function Decoder.parse_object(self, chunk)
chunk = chunk:sub(2)
local array = {}
local name
local chunk, object = self:parse_delimiter(chunk, "}")
if object then
return chunk, array
end
repeat
chunk = self:parse_space(chunk)
assert(chunk, "Unexpected EOS")
chunk, name = self:parse_string(chunk)
chunk, object = self:parse_delimiter(chunk, ":")
assert(object, "Separator expected")
chunk, object = self:dispatch(chunk, nil, true)
array[name] = object
chunk, object = self:parse_delimiter(chunk, ",}")
assert(object, "Delimiter expected")
until object == "}"
return chunk, array
end
function Decoder.parse_delimiter(self, chunk, delimiter)
while true do
chunk = self:fetch_atleast(chunk, 1)
local char = chunk:sub(1, 1)
if char:match("%s") then
chunk = self:parse_space(chunk)
assert(chunk, "Unexpected EOS")
elseif char:match("[%s]" % delimiter) then
return chunk:sub(2), char
else
return chunk, nil
end
end
end
Decoder.parsers = {
['"'] = Decoder.parse_string,
['t'] = Decoder.parse_true,
['f'] = Decoder.parse_false,
['n'] = Decoder.parse_null,
['['] = Decoder.parse_array,
['{'] = Decoder.parse_object
}
--- Create a new Active JSON-Decoder.
-- @class function
-- @name ActiveDecoder
-- @param customnull Use luci.json.null instead of nil for decoding null
-- @return Active JSON-Decoder
ActiveDecoder = util.class(Decoder)
function ActiveDecoder.__init__(self, source, customnull)
Decoder.__init__(self, customnull)
self.source = source
self.chunk = nil
getmetatable(self).__call = self.get
end
--- Fetches one JSON-object from given source
-- @return Decoded object
function ActiveDecoder.get(self)
local chunk, src_err, object
if not self.chunk then
chunk, src_err = self.source()
else
chunk = self.chunk
end
self.chunk, object = self:dispatch(chunk, src_err, true)
return object
end
function ActiveDecoder.fetch(self)
local chunk, src_err = self.source()
assert(chunk or not src_err, src_err)
return chunk
end

391
1_1.mi_Lua/luci/ltn12.lua Normal file
View File

@ -0,0 +1,391 @@
--[[
LuaSocket 2.0.2 license
Copyright <EFBFBD> 2004-2007 Diego Nehab
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
]]--
--[[
Changes made by LuCI project:
* Renamed to luci.ltn12 to avoid collisions with luasocket
* Added inline documentation
]]--
-----------------------------------------------------------------------------
-- LTN12 - Filters, sources, sinks and pumps.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: ltn12.lua 3212 2008-09-09 12:44:41Z Cyrus $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local base = _G
--- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
-- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts
module("luci.ltn12")
filter = {}
source = {}
sink = {}
pump = {}
-- 2048 seems to be better in windows...
BLOCKSIZE = 2048
_VERSION = "LTN12 1.0.1"
-----------------------------------------------------------------------------
-- Filter stuff
-----------------------------------------------------------------------------
--- LTN12 Filter constructors
-- @class module
-- @name luci.ltn12.filter
--- Return a high level filter that cycles a low-level filter
-- by passing it each chunk and updating a context between calls.
-- @param low Low-level filter
-- @param ctx Context
-- @param extra Extra argument passed to the low-level filter
-- @return LTN12 filter
function filter.cycle(low, ctx, extra)
base.assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
--- Chain a bunch of filters together.
-- (thanks to Wim Couwenberg)
-- @param ... filters to be chained
-- @return LTN12 filter
function filter.chain(...)
local n = table.getn(arg)
local top, index = 1, 1
local retry = ""
return function(chunk)
retry = chunk and retry
while true do
if index == top then
chunk = arg[index](chunk)
if chunk == "" or top == n then return chunk
elseif chunk then index = index + 1
else
top = top+1
index = top
end
else
chunk = arg[index](chunk or "")
if chunk == "" then
index = index - 1
chunk = retry
elseif chunk then
if index == n then return chunk
else index = index + 1 end
else base.error("filter returned inappropriate nil") end
end
end
end
end
-----------------------------------------------------------------------------
-- Source stuff
-----------------------------------------------------------------------------
--- LTN12 Source constructors
-- @class module
-- @name luci.ltn12.source
-- create an empty source
local function empty()
return nil
end
--- Create an empty source.
-- @return LTN12 source
function source.empty()
return empty
end
--- Return a source that just outputs an error.
-- @param err Error object
-- @return LTN12 source
function source.error(err)
return function()
return nil, err
end
end
--- Create a file source.
-- @param handle File handle ready for reading
-- @param io_err IO error object
-- @return LTN12 source
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(BLOCKSIZE)
if not chunk then handle:close() end
return chunk
end
else return source.error(io_err or "unable to open file") end
end
--- Turn a fancy source into a simple source.
-- @param src fancy source
-- @return LTN12 source
function source.simplify(src)
base.assert(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
--- Create a string source.
-- @param s Data
-- @return LTN12 source
function source.string(s)
if s then
local i = 1
return function()
local chunk = string.sub(s, i, i+BLOCKSIZE-1)
i = i + BLOCKSIZE
if chunk ~= "" then return chunk
else return nil end
end
else return source.empty() end
end
--- Creates rewindable source.
-- @param src LTN12 source to be made rewindable
-- @return LTN12 source
function source.rewind(src)
base.assert(src)
local t = {}
return function(chunk)
if not chunk then
chunk = table.remove(t)
if not chunk then return src()
else return chunk end
else
t[#t+1] = chunk
end
end
end
--- Chain a source and a filter together.
-- @param src LTN12 source
-- @param f LTN12 filter
-- @return LTN12 source
function source.chain(src, f)
base.assert(src and f)
local last_in, last_out = "", ""
local state = "feeding"
local err
return function()
if not last_out then
base.error('source is empty!', 2)
end
while true do
if state == "feeding" then
last_in, err = src()
if err then return nil, err end
last_out = f(last_in)
if not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
elseif last_out ~= "" then
state = "eating"
if last_in then last_in = "" end
return last_out
end
else
last_out = f(last_in)
if last_out == "" then
if last_in == "" then
state = "feeding"
else
base.error('filter returned ""')
end
elseif not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
else
return last_out
end
end
end
end
end
--- Create a source that produces contents of several sources.
-- Sources will be used one after the other, as if they were concatenated
-- (thanks to Wim Couwenberg)
-- @param ... LTN12 sources
-- @return LTN12 source
function source.cat(...)
local src = table.remove(arg, 1)
return function()
while src do
local chunk, err = src()
if chunk then return chunk end
if err then return nil, err end
src = table.remove(arg, 1)
end
end
end
-----------------------------------------------------------------------------
-- Sink stuff
-----------------------------------------------------------------------------
--- LTN12 sink constructors
-- @class module
-- @name luci.ltn12.sink
--- Create a sink that stores into a table.
-- @param t output table to store into
-- @return LTN12 sink
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then t[#t+1] = chunk end
return 1
end
return f, t
end
--- Turn a fancy sink into a simple sink.
-- @param snk fancy sink
-- @return LTN12 sink
function sink.simplify(snk)
base.assert(snk)
return function(chunk, err)
local ret, err_or_new = snk(chunk, err)
if not ret then return nil, err_or_new end
snk = err_or_new or snk
return 1
end
end
--- Create a file sink.
-- @param handle file handle to write to
-- @param io_err IO error
-- @return LTN12 sink
function sink.file(handle, io_err)
if handle then
return function(chunk, err)
if not chunk then
handle:close()
return 1
else return handle:write(chunk) end
end
else return sink.error(io_err or "unable to open file") end
end
-- creates a sink that discards data
local function null()
return 1
end
--- Create a sink that discards data.
-- @return LTN12 sink
function sink.null()
return null
end
--- Create a sink that just returns an error.
-- @param err Error object
-- @return LTN12 sink
function sink.error(err)
return function()
return nil, err
end
end
--- Chain a sink with a filter.
-- @param f LTN12 filter
-- @param snk LTN12 sink
-- @return LTN12 sink
function sink.chain(f, snk)
base.assert(f and snk)
return function(chunk, err)
if chunk ~= "" then
local filtered = f(chunk)
local done = chunk and ""
while true do
local ret, snkerr = snk(filtered, err)
if not ret then return nil, snkerr end
if filtered == done then return 1 end
filtered = f(done)
end
else return 1 end
end
end
-----------------------------------------------------------------------------
-- Pump stuff
-----------------------------------------------------------------------------
--- LTN12 pump functions
-- @class module
-- @name luci.ltn12.pump
--- Pump one chunk from the source to the sink.
-- @param src LTN12 source
-- @param snk LTN12 sink
-- @return Chunk of data or nil if an error occured
-- @return Error object
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1
else return nil, src_err or snk_err end
end
--- Pump all data from a source to a sink, using a step function.
-- @param src LTN12 source
-- @param snk LTN12 sink
-- @param step step function (optional)
-- @return 1 if the operation succeeded otherwise nil
-- @return Error object
function pump.all(src, snk, step)
base.assert(src and snk)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then
if err then return nil, err
else return 1 end
end
end
end

View File

@ -0,0 +1,88 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011-2012 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local ifc = net:get_interface()
local hostname, accept_ra, send_rs
local bcast, defaultroute, peerdns, dns, metric, clientid, vendorclass
hostname = section:taboption("general", Value, "hostname",
translate("Hostname to send when requesting DHCP"))
hostname.placeholder = luci.sys.hostname()
hostname.datatype = "hostname"
if luci.model.network:has_ipv6() then
accept_ra = s:taboption("general", Flag, "accept_ra", translate("Accept router advertisements"))
accept_ra.default = accept_ra.enabled
send_rs = s:taboption("general", Flag, "send_rs", translate("Send router solicitations"))
send_rs.default = send_rs.disabled
send_rs:depends("accept_ra", "")
end
bcast = section:taboption("advanced", Flag, "broadcast",
translate("Use broadcast flag"),
translate("Required for certain ISPs, e.g. Charter with DOCSIS 3"))
bcast.default = bcast.disabled
defaultroute = section:taboption("advanced", Flag, "defaultroute",
translate("Use default gateway"),
translate("If unchecked, no default route is configured"))
defaultroute.default = defaultroute.enabled
peerdns = section:taboption("advanced", Flag, "peerdns",
translate("Use DNS servers advertised by peer"),
translate("If unchecked, the advertised DNS server addresses are ignored"))
peerdns.default = peerdns.enabled
dns = section:taboption("advanced", DynamicList, "dns",
translate("Use custom DNS servers"))
dns:depends("peerdns", "")
dns.datatype = "ipaddr"
dns.cast = "string"
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"
clientid = section:taboption("advanced", Value, "clientid",
translate("Client ID to send when requesting DHCP"))
vendorclass = section:taboption("advanced", Value, "vendorid",
translate("Vendor Class to send when requesting DHCP"))
luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"

View File

@ -0,0 +1,69 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local server, username, password
local ipv6, defaultroute, metric, peerdns, dns, mtu
server = section:taboption("general", Value, "server", translate("L2TP Server"))
server.datatype = "host"
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
password.password = true
if luci.model.network:has_ipv6() then
ipv6 = section:taboption("advanced", Flag, "ipv6",
translate("Enable IPv6 negotiation on the PPP link"))
ipv6.default = ipv6.disabled
end
defaultroute = section:taboption("advanced", Flag, "defaultroute",
translate("Use default gateway"),
translate("If unchecked, no default route is configured"))
defaultroute.default = defaultroute.enabled
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"
metric:depends("defaultroute", defaultroute.enabled)
peerdns = section:taboption("advanced", Flag, "peerdns",
translate("Use DNS servers advertised by peer"),
translate("If unchecked, the advertised DNS server addresses are ignored"))
peerdns.default = peerdns.enabled
dns = section:taboption("advanced", DynamicList, "dns",
translate("Use custom DNS servers"))
dns:depends("peerdns", "")
dns.datatype = "ipaddr"
dns.cast = "string"
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"

View File

@ -0,0 +1,13 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...

View File

@ -0,0 +1,136 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local device, username, password
local ipv6, defaultroute, metric, peerdns, dns,
keepalive_failure, keepalive_interval, demand, mtu
device = section:taboption("general", Value, "device", translate("Modem device"))
device.rmempty = false
local device_suggestions = nixio.fs.glob("/dev/tty*S*")
or nixio.fs.glob("/dev/tts/*")
if device_suggestions then
local node
for node in device_suggestions do
device:value(node)
end
end
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
password.password = true
if luci.model.network:has_ipv6() then
ipv6 = section:taboption("advanced", Flag, "ipv6",
translate("Enable IPv6 negotiation on the PPP link"))
ipv6.default = ipv6.disabled
end
defaultroute = section:taboption("advanced", Flag, "defaultroute",
translate("Use default gateway"),
translate("If unchecked, no default route is configured"))
defaultroute.default = defaultroute.enabled
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"
metric:depends("defaultroute", defaultroute.enabled)
peerdns = section:taboption("advanced", Flag, "peerdns",
translate("Use DNS servers advertised by peer"),
translate("If unchecked, the advertised DNS server addresses are ignored"))
peerdns.default = peerdns.enabled
dns = section:taboption("advanced", DynamicList, "dns",
translate("Use custom DNS servers"))
dns:depends("peerdns", "")
dns.datatype = "ipaddr"
dns.cast = "string"
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
translate("LCP echo failure threshold"),
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
function keepalive_failure.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
end
end
function keepalive_failure.write() end
function keepalive_failure.remove() end
keepalive_failure.placeholder = "0"
keepalive_failure.datatype = "uinteger"
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
translate("LCP echo interval"),
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
function keepalive_interval.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^%d+[ ,]+(%d+)"))
end
end
function keepalive_interval.write(self, section, value)
local f = tonumber(keepalive_failure:formvalue(section)) or 0
local i = tonumber(value) or 5
if i < 1 then i = 1 end
if f > 0 then
m:set(section, "keepalive", "%d %d" %{ f, i })
else
m:del(section, "keepalive")
end
end
keepalive_interval.remove = keepalive_interval.write
keepalive_interval.placeholder = "5"
keepalive_interval.datatype = "min(1)"
demand = section:taboption("advanced", Value, "demand",
translate("Inactivity timeout"),
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
demand.placeholder = "0"
demand.datatype = "uinteger"
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"

View File

@ -0,0 +1,142 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local encaps, atmdev, vci, vpi, username, password
local ipv6, defaultroute, metric, peerdns, dns,
keepalive_failure, keepalive_interval, demand, mtu
encaps = section:taboption("general", ListValue, "encaps", translate("PPPoA Encapsulation"))
encaps:value("vc", "VC-Mux")
encaps:value("llc", "LLC")
atmdev = section:taboption("general", Value, "atmdev", translate("ATM device number"))
atmdev.default = "0"
atmdev.datatype = "uinteger"
vci = section:taboption("general", Value, "vci", translate("ATM Virtual Channel Identifier (VCI)"))
vci.default = "35"
vci.datatype = "uinteger"
vpi = section:taboption("general", Value, "vpi", translate("ATM Virtual Path Identifier (VPI)"))
vpi.default = "8"
vpi.datatype = "uinteger"
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
password.password = true
if luci.model.network:has_ipv6() then
ipv6 = section:taboption("advanced", Flag, "ipv6",
translate("Enable IPv6 negotiation on the PPP link"))
ipv6.default = ipv6.disabled
end
defaultroute = section:taboption("advanced", Flag, "defaultroute",
translate("Use default gateway"),
translate("If unchecked, no default route is configured"))
defaultroute.default = defaultroute.enabled
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"
metric:depends("defaultroute", defaultroute.enabled)
peerdns = section:taboption("advanced", Flag, "peerdns",
translate("Use DNS servers advertised by peer"),
translate("If unchecked, the advertised DNS server addresses are ignored"))
peerdns.default = peerdns.enabled
dns = section:taboption("advanced", DynamicList, "dns",
translate("Use custom DNS servers"))
dns:depends("peerdns", "")
dns.datatype = "ipaddr"
dns.cast = "string"
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
translate("LCP echo failure threshold"),
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
function keepalive_failure.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
end
end
function keepalive_failure.write() end
function keepalive_failure.remove() end
keepalive_failure.placeholder = "0"
keepalive_failure.datatype = "uinteger"
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
translate("LCP echo interval"),
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
function keepalive_interval.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^%d+[ ,]+(%d+)"))
end
end
function keepalive_interval.write(self, section, value)
local f = tonumber(keepalive_failure:formvalue(section)) or 0
local i = tonumber(value) or 5
if i < 1 then i = 1 end
if f > 0 then
m:set(section, "keepalive", "%d %d" %{ f, i })
else
m:del(section, "keepalive")
end
end
keepalive_interval.remove = keepalive_interval.write
keepalive_interval.placeholder = "5"
keepalive_interval.datatype = "min(1)"
demand = section:taboption("advanced", Value, "demand",
translate("Inactivity timeout"),
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
demand.placeholder = "0"
demand.datatype = "uinteger"
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"

View File

@ -0,0 +1,136 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local username, password, ac, service
local ipv6, defaultroute, metric, peerdns, dns,
keepalive_failure, keepalive_interval, demand, mtu
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
password.password = true
ac = section:taboption("general", Value, "ac",
translate("Access Concentrator"),
translate("Leave empty to autodetect"))
ac.placeholder = translate("auto")
service = section:taboption("general", Value, "service",
translate("Service Name"),
translate("Leave empty to autodetect"))
service.placeholder = translate("auto")
if luci.model.network:has_ipv6() then
ipv6 = section:taboption("advanced", Flag, "ipv6",
translate("Enable IPv6 negotiation on the PPP link"))
ipv6.default = ipv6.disabled
end
defaultroute = section:taboption("advanced", Flag, "defaultroute",
translate("Use default gateway"),
translate("If unchecked, no default route is configured"))
defaultroute.default = defaultroute.enabled
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"
metric:depends("defaultroute", defaultroute.enabled)
peerdns = section:taboption("advanced", Flag, "peerdns",
translate("Use DNS servers advertised by peer"),
translate("If unchecked, the advertised DNS server addresses are ignored"))
peerdns.default = peerdns.enabled
dns = section:taboption("advanced", DynamicList, "dns",
translate("Use custom DNS servers"))
dns:depends("peerdns", "")
dns.datatype = "ipaddr"
dns.cast = "string"
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
translate("LCP echo failure threshold"),
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
function keepalive_failure.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
end
end
function keepalive_failure.write() end
function keepalive_failure.remove() end
keepalive_failure.placeholder = "0"
keepalive_failure.datatype = "uinteger"
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
translate("LCP echo interval"),
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
function keepalive_interval.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^%d+[ ,]+(%d+)"))
end
end
function keepalive_interval.write(self, section, value)
local f = tonumber(keepalive_failure:formvalue(section)) or 0
local i = tonumber(value) or 5
if i < 1 then i = 1 end
if f > 0 then
m:set(section, "keepalive", "%d %d" %{ f, i })
else
m:del(section, "keepalive")
end
end
keepalive_interval.remove = keepalive_interval.write
keepalive_interval.placeholder = "5"
keepalive_interval.datatype = "min(1)"
demand = section:taboption("advanced", Value, "demand",
translate("Inactivity timeout"),
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
demand.placeholder = "0"
demand.datatype = "uinteger"
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"

View File

@ -0,0 +1,116 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011-2012 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local server, username, password
local defaultroute, metric, peerdns, dns,
keepalive_failure, keepalive_interval, demand, mtu
server = section:taboption("general", Value, "server", translate("VPN Server"))
server.datatype = "host"
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
password.password = true
defaultroute = section:taboption("advanced", Flag, "defaultroute",
translate("Use default gateway"),
translate("If unchecked, no default route is configured"))
defaultroute.default = defaultroute.enabled
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"
metric:depends("defaultroute", defaultroute.enabled)
peerdns = section:taboption("advanced", Flag, "peerdns",
translate("Use DNS servers advertised by peer"),
translate("If unchecked, the advertised DNS server addresses are ignored"))
peerdns.default = peerdns.enabled
dns = section:taboption("advanced", DynamicList, "dns",
translate("Use custom DNS servers"))
dns:depends("peerdns", "")
dns.datatype = "ipaddr"
dns.cast = "string"
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
translate("LCP echo failure threshold"),
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
function keepalive_failure.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
end
end
function keepalive_failure.write() end
function keepalive_failure.remove() end
keepalive_failure.placeholder = "0"
keepalive_failure.datatype = "uinteger"
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
translate("LCP echo interval"),
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
function keepalive_interval.cfgvalue(self, section)
local v = m:get(section, "keepalive")
if v and #v > 0 then
return tonumber(v:match("^%d+[ ,]+(%d+)"))
end
end
function keepalive_interval.write(self, section, value)
local f = tonumber(keepalive_failure:formvalue(section)) or 0
local i = tonumber(value) or 5
if i < 1 then i = 1 end
if f > 0 then
m:set(section, "keepalive", "%d %d" %{ f, i })
else
m:del(section, "keepalive")
end
end
keepalive_interval.remove = keepalive_interval.write
keepalive_interval.placeholder = "5"
keepalive_interval.datatype = "min(1)"
demand = section:taboption("advanced", Value, "demand",
translate("Inactivity timeout"),
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
demand.placeholder = "0"
demand.datatype = "uinteger"
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"

View File

@ -0,0 +1,83 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local map, section, net = ...
local ifc = net:get_interface()
local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
local mtu, metric
ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
ipaddr.datatype = "ip4addr"
netmask = section:taboption("general", Value, "netmask",
translate("IPv4 netmask"))
netmask.datatype = "ip4addr"
netmask:value("255.255.255.0")
netmask:value("255.255.0.0")
netmask:value("255.0.0.0")
gateway = section:taboption("general", Value, "gateway", translate("IPv4 gateway"))
gateway.datatype = "ip4addr"
broadcast = section:taboption("general", Value, "broadcast", translate("IPv4 broadcast"))
broadcast.datatype = "ip4addr"
dns = section:taboption("general", DynamicList, "dns",
translate("Use custom DNS servers"))
dns.datatype = "ipaddr"
dns.cast = "string"
if luci.model.network:has_ipv6() then
accept_ra = s:taboption("general", Flag, "accept_ra", translate("Accept router advertisements"))
accept_ra.default = accept_ra.disabled
send_rs = s:taboption("general", Flag, "send_rs", translate("Send router solicitations"))
send_rs.default = send_rs.enabled
send_rs:depends("accept_ra", "")
ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address"))
ip6addr.datatype = "ip6addr"
ip6addr:depends("accept_ra", "")
ip6gw = section:taboption("general", Value, "ip6gw", translate("IPv6 gateway"))
ip6gw.datatype = "ip6addr"
ip6gw:depends("accept_ra", "")
end
luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
mtu.placeholder = "1500"
mtu.datatype = "max(1500)"
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
metric.placeholder = "0"
metric.datatype = "uinteger"

View File

@ -0,0 +1,38 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: custom.lua 8108 2011-12-19 21:16:31Z jow $
]]--
local fs = require "nixio.fs"
local f = SimpleForm("firewall",
translate("Firewall - Custom Rules"),
translate("Custom rules allow you to execute arbritary iptables commands \
which are not otherwise covered by the firewall framework. \
The commands are executed after each firewall restart, right after \
the default ruleset has been loaded."))
local o = f:field(Value, "_custom")
o.template = "cbi/tvalue"
o.rows = 20
function o.cfgvalue(self, section)
return fs.readfile("/etc/firewall.user")
end
function o.write(self, section, value)
value = value:gsub("\r\n?", "\n")
fs.writefile("/etc/firewall.user", value)
end
return f

View File

@ -0,0 +1,178 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: forward-details.lua 8962 2012-08-09 10:03:32Z jow $
]]--
local sys = require "luci.sys"
local dsp = require "luci.dispatcher"
local ft = require "luci.tools.firewall"
local m, s, o
arg[1] = arg[1] or ""
m = Map("firewall",
translate("Firewall - Port Forwards"),
translate("This page allows you to change advanced properties of the port \
forwarding entry. In most cases there is no need to modify \
those settings."))
m.redirect = dsp.build_url("admin/network/firewall/forwards")
if m.uci:get("firewall", arg[1]) ~= "redirect" then
luci.http.redirect(m.redirect)
return
else
local name = m:get(arg[1], "name") or m:get(arg[1], "_name")
if not name or #name == 0 then
name = translate("(Unnamed Entry)")
end
m.title = "%s - %s" %{ translate("Firewall - Port Forwards"), name }
end
local wan_zone = nil
m.uci:foreach("firewall", "zone",
function(s)
local n = s.network or s.name
if n then
local i
for i in n:gmatch("%S+") do
if i == "wan" then
wan_zone = s.name
return false
end
end
end
end)
s = m:section(NamedSection, arg[1], "redirect", "")
s.anonymous = true
s.addremove = false
ft.opt_enabled(s, Button)
ft.opt_name(s, Value, translate("Name"))
o = s:option(Value, "proto", translate("Protocol"))
o:value("tcp udp", "TCP+UDP")
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
if not v or v == "tcpudp" then
return "tcp udp"
end
return v
end
o = s:option(Value, "src", translate("Source zone"))
o.nocreate = true
o.default = "wan"
o.template = "cbi/firewall_zonelist"
o = s:option(DynamicList, "src_mac",
translate("Source MAC address"),
translate("Only match incoming traffic from these MACs."))
o.rmempty = true
o.datatype = "neg(macaddr)"
o.placeholder = translate("any")
luci.sys.net.mac_hints(function(mac, name)
o:value(mac, "%s (%s)" %{ mac, name })
end)
o = s:option(Value, "src_ip",
translate("Source IP address"),
translate("Only match incoming traffic from this IP or range."))
o.rmempty = true
o.datatype = "neg(ip4addr)"
o.placeholder = translate("any")
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o = s:option(Value, "src_port",
translate("Source port"),
translate("Only match incoming traffic originating from the given source port or port range on the client host"))
o.rmempty = true
o.datatype = "neg(portrange)"
o.placeholder = translate("any")
o = s:option(Value, "src_dip",
translate("External IP address"),
translate("Only match incoming traffic directed at the given IP address."))
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o.rmempty = true
o.datatype = "neg(ip4addr)"
o.placeholder = translate("any")
o = s:option(Value, "src_dport", translate("External port"),
translate("Match incoming traffic directed at the given " ..
"destination port or port range on this host"))
o.datatype = "neg(portrange)"
o = s:option(Value, "dest", translate("Internal zone"))
o.nocreate = true
o.default = "lan"
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "dest_ip", translate("Internal IP address"),
translate("Redirect matched incoming traffic to the specified \
internal host"))
o.datatype = "ip4addr"
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o = s:option(Value, "dest_port",
translate("Internal port"),
translate("Redirect matched incoming traffic to the given port on \
the internal host"))
o.placeholder = translate("any")
o.datatype = "portrange"
o = s:option(Flag, "reflection", translate("Enable NAT Loopback"))
o.rmempty = true
o.default = o.enabled
o:depends("src", wan_zone)
o.cfgvalue = function(...)
return Flag.cfgvalue(...) or "1"
end
s:option(Value, "extra",
translate("Extra arguments"),
translate("Passes additional arguments to iptables. Use with care!"))
return m

View File

@ -0,0 +1,144 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local ds = require "luci.dispatcher"
local ft = require "luci.tools.firewall"
m = Map("firewall", translate("Firewall - Port Forwards"),
translate("Port forwarding allows remote computers on the Internet to \
connect to a specific computer or service within the \
private LAN."))
--
-- Port Forwards
--
s = m:section(TypedSection, "redirect", translate("Port Forwards"))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.sortable = true
s.extedit = ds.build_url("admin/network/firewall/forwards/%s")
s.template_addremove = "firewall/cbi_addforward"
function s.create(self, section)
local n = m:formvalue("_newfwd.name")
local p = m:formvalue("_newfwd.proto")
local E = m:formvalue("_newfwd.extzone")
local e = m:formvalue("_newfwd.extport")
local I = m:formvalue("_newfwd.intzone")
local a = m:formvalue("_newfwd.intaddr")
local i = m:formvalue("_newfwd.intport")
if p == "other" or (p and a) then
created = TypedSection.create(self, section)
self.map:set(created, "target", "DNAT")
self.map:set(created, "src", E or "wan")
self.map:set(created, "dest", I or "lan")
self.map:set(created, "proto", (p ~= "other") and p or "all")
self.map:set(created, "src_dport", e)
self.map:set(created, "dest_ip", a)
self.map:set(created, "dest_port", i)
self.map:set(created, "name", n)
end
if p ~= "other" then
created = nil
end
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin/network/firewall/redirect", created
))
end
end
function s.filter(self, sid)
return (self.map:get(sid, "target") ~= "SNAT")
end
ft.opt_name(s, DummyValue, translate("Name"))
local function forward_proto_txt(self, s)
return "%s-%s" %{
translate("IPv4"),
ft.fmt_proto(self.map:get(s, "proto"),
self.map:get(s, "icmp_type")) or "TCP+UDP"
}
end
local function forward_src_txt(self, s)
local z = ft.fmt_zone(self.map:get(s, "src"), translate("any zone"))
local a = ft.fmt_ip(self.map:get(s, "src_ip"), translate("any host"))
local p = ft.fmt_port(self.map:get(s, "src_port"))
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
if p and m then
return translatef("From %s in %s with source %s and %s", a, z, p, m)
elseif p or m then
return translatef("From %s in %s with source %s", a, z, p or m)
else
return translatef("From %s in %s", a, z)
end
end
local function forward_via_txt(self, s)
local a = ft.fmt_ip(self.map:get(s, "src_dip"), translate("any router IP"))
local p = ft.fmt_port(self.map:get(s, "src_dport"))
if p then
return translatef("Via %s at %s", a, p)
else
return translatef("Via %s", a)
end
end
match = s:option(DummyValue, "match", translate("Match"))
match.rawhtml = true
match.width = "50%"
function match.cfgvalue(self, s)
return "<small>%s<br />%s<br />%s</small>" % {
forward_proto_txt(self, s),
forward_src_txt(self, s),
forward_via_txt(self, s)
}
end
dest = s:option(DummyValue, "dest", translate("Forward to"))
dest.rawhtml = true
dest.width = "40%"
function dest.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"), translate("any zone"))
local a = ft.fmt_ip(self.map:get(s, "dest_ip"), translate("any host"))
local p = ft.fmt_port(self.map:get(s, "dest_port")) or
ft.fmt_port(self.map:get(s, "src_dport"))
if p then
return translatef("%s, %s in %s", a, p, z)
else
return translatef("%s in %s", a, z)
end
end
ft.opt_enabled(s, Flag, translate("Enable")).width = "1%"
return m

View File

@ -0,0 +1,338 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local sys = require "luci.sys"
local dsp = require "luci.dispatcher"
local nxo = require "nixio"
local ft = require "luci.tools.firewall"
local nw = require "luci.model.network"
local m, s, o, k, v
arg[1] = arg[1] or ""
m = Map("firewall",
translate("Firewall - Traffic Rules"),
translate("This page allows you to change advanced properties of the \
traffic rule entry, such as matched source and destination \
hosts."))
m.redirect = dsp.build_url("admin/network/firewall/rules")
nw.init(m.uci)
local rule_type = m.uci:get("firewall", arg[1])
if rule_type == "redirect" and m:get(arg[1], "target") ~= "SNAT" then
rule_type = nil
end
if not rule_type then
luci.http.redirect(m.redirect)
return
--
-- SNAT
--
elseif rule_type == "redirect" then
local name = m:get(arg[1], "name") or m:get(arg[1], "_name")
if not name or #name == 0 then
name = translate("(Unnamed SNAT)")
else
name = "SNAT %s" % name
end
m.title = "%s - %s" %{ translate("Firewall - Traffic Rules"), name }
local wan_zone = nil
m.uci:foreach("firewall", "zone",
function(s)
local n = s.network or s.name
if n then
local i
for i in n:gmatch("%S+") do
if i == "wan" then
wan_zone = s.name
return false
end
end
end
end)
s = m:section(NamedSection, arg[1], "redirect", "")
s.anonymous = true
s.addremove = false
ft.opt_enabled(s, Button)
ft.opt_name(s, Value, translate("Name"))
o = s:option(Value, "proto",
translate("Protocol"),
translate("You may specify multiple by selecting \"-- custom --\" and \
then entering protocols separated by space."))
o:value("all", "All protocols")
o:value("tcp udp", "TCP+UDP")
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
if not v or v == "tcpudp" then
return "tcp udp"
end
return v
end
o = s:option(Value, "src", translate("Source zone"))
o.nocreate = true
o.default = "wan"
o.template = "cbi/firewall_zonelist"
o = s:option(DynamicList, "src_mac", translate("Source MAC address"))
o.rmempty = true
o.datatype = "neg(macaddr)"
o.placeholder = translate("any")
luci.sys.net.mac_hints(function(mac, name)
o:value(mac, "%s (%s)" %{ mac, name })
end)
o = s:option(Value, "src_ip", translate("Source IP address"))
o.rmempty = true
o.datatype = "neg(ipaddr)"
o.placeholder = translate("any")
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o = s:option(Value, "src_port",
translate("Source port"),
translate("Match incoming traffic originating from the given source \
port or port range on the client host."))
o.rmempty = true
o.datatype = "neg(portrange)"
o.placeholder = translate("any")
o = s:option(Value, "dest", translate("Destination zone"))
o.nocreate = true
o.default = "lan"
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "dest_ip", translate("Destination IP address"))
o.datatype = "neg(ip4addr)"
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o = s:option(Value, "dest_port",
translate("Destination port"),
translate("Match forwarded traffic to the given destination port or \
port range."))
o.rmempty = true
o.placeholder = translate("any")
o.datatype = "neg(portrange)"
o = s:option(Value, "src_dip",
translate("SNAT IP address"),
translate("Rewrite matched traffic to the given address."))
o.rmempty = false
o.datatype = "ip4addr"
for k, v in ipairs(nw:get_interfaces()) do
local a
for k, a in ipairs(v:ipaddrs()) do
o:value(a:host():string(), '%s (%s)' %{
a:host():string(), v:shortname()
})
end
end
o = s:option(Value, "src_dport", translate("SNAT port"),
translate("Rewrite matched traffic to the given source port. May be \
left empty to only rewrite the IP address."))
o.datatype = "portrange"
o.rmempty = true
o.placeholder = translate('Do not rewrite')
s:option(Value, "extra",
translate("Extra arguments"),
translate("Passes additional arguments to iptables. Use with care!"))
--
-- Rule
--
else
local name = m:get(arg[1], "name") or m:get(arg[1], "_name")
if not name or #name == 0 then
name = translate("(Unnamed Rule)")
end
m.title = "%s - %s" %{ translate("Firewall - Traffic Rules"), name }
s = m:section(NamedSection, arg[1], "rule", "")
s.anonymous = true
s.addremove = false
ft.opt_enabled(s, Button)
ft.opt_name(s, Value, translate("Name"))
o = s:option(ListValue, "family", translate("Restrict to address family"))
o.rmempty = true
o:value("", translate("IPv4 and IPv6"))
o:value("ipv4", translate("IPv4 only"))
o:value("ipv6", translate("IPv6 only"))
o = s:option(Value, "proto", translate("Protocol"))
o:value("all", translate("Any"))
o:value("tcp udp", "TCP+UDP")
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
if not v or v == "tcpudp" then
return "tcp udp"
end
return v
end
o = s:option(DynamicList, "icmp_type", translate("Match ICMP type"))
o:value("", "any")
o:value("echo-reply")
o:value("destination-unreachable")
o:value("network-unreachable")
o:value("host-unreachable")
o:value("protocol-unreachable")
o:value("port-unreachable")
o:value("fragmentation-needed")
o:value("source-route-failed")
o:value("network-unknown")
o:value("host-unknown")
o:value("network-prohibited")
o:value("host-prohibited")
o:value("TOS-network-unreachable")
o:value("TOS-host-unreachable")
o:value("communication-prohibited")
o:value("host-precedence-violation")
o:value("precedence-cutoff")
o:value("source-quench")
o:value("redirect")
o:value("network-redirect")
o:value("host-redirect")
o:value("TOS-network-redirect")
o:value("TOS-host-redirect")
o:value("echo-request")
o:value("router-advertisement")
o:value("router-solicitation")
o:value("time-exceeded")
o:value("ttl-zero-during-transit")
o:value("ttl-zero-during-reassembly")
o:value("parameter-problem")
o:value("ip-header-bad")
o:value("required-option-missing")
o:value("timestamp-request")
o:value("timestamp-reply")
o:value("address-mask-request")
o:value("address-mask-reply")
o = s:option(Value, "src", translate("Source zone"))
o.nocreate = true
o.allowany = true
o.default = "wan"
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "src_mac", translate("Source MAC address"))
o.datatype = "list(macaddr)"
o.placeholder = translate("any")
luci.sys.net.mac_hints(function(mac, name)
o:value(mac, "%s (%s)" %{ mac, name })
end)
o = s:option(Value, "src_ip", translate("Source address"))
o.datatype = "neg(ipaddr)"
o.placeholder = translate("any")
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o = s:option(Value, "src_port", translate("Source port"))
o.datatype = "list(neg(portrange))"
o.placeholder = translate("any")
o = s:option(Value, "dest", translate("Destination zone"))
o.nocreate = true
o.allowany = true
o.allowlocal = true
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "dest_ip", translate("Destination address"))
o.datatype = "neg(ipaddr)"
o.placeholder = translate("any")
luci.sys.net.ipv4_hints(function(ip, name)
o:value(ip, "%s (%s)" %{ ip, name })
end)
o = s:option(Value, "dest_port", translate("Destination port"))
o.datatype = "list(neg(portrange))"
o.placeholder = translate("any")
o = s:option(ListValue, "target", translate("Action"))
o.default = "ACCEPT"
o:value("DROP", translate("drop"))
o:value("ACCEPT", translate("accept"))
o:value("REJECT", translate("reject"))
o:value("NOTRACK", translate("don't track"))
s:option(Value, "extra",
translate("Extra arguments"),
translate("Passes additional arguments to iptables. Use with care!"))
end
return m

View File

@ -0,0 +1,269 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local ds = require "luci.dispatcher"
local ft = require "luci.tools.firewall"
m = Map("firewall",
translate("Firewall - Traffic Rules"),
translate("Traffic rules define policies for packets traveling between \
different zones, for example to reject traffic between certain hosts \
or to open WAN ports on the router."))
--
-- Rules
--
s = m:section(TypedSection, "rule", translate("Traffic Rules"))
s.addremove = true
s.anonymous = true
s.sortable = true
s.template = "cbi/tblsection"
s.extedit = ds.build_url("admin/network/firewall/rules/%s")
s.defaults.target = "ACCEPT"
s.template_addremove = "firewall/cbi_addrule"
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
local i_n = m:formvalue("_newopen.name")
local i_p = m:formvalue("_newopen.proto")
local i_e = m:formvalue("_newopen.extport")
local i_x = m:formvalue("_newopen.submit")
local f_n = m:formvalue("_newfwd.name")
local f_s = m:formvalue("_newfwd.src")
local f_d = m:formvalue("_newfwd.dest")
local f_x = m:formvalue("_newfwd.submit")
if i_x then
created = TypedSection.create(self, section)
self.map:set(created, "target", "ACCEPT")
self.map:set(created, "src", "wan")
self.map:set(created, "proto", (i_p ~= "other") and i_p or "all")
self.map:set(created, "dest_port", i_e)
self.map:set(created, "name", i_n)
if i_p ~= "other" and i_e and #i_e > 0 then
created = nil
end
elseif f_x then
created = TypedSection.create(self, section)
self.map:set(created, "target", "ACCEPT")
self.map:set(created, "src", f_s)
self.map:set(created, "dest", f_d)
self.map:set(created, "name", f_n)
end
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin/network/firewall/rules", created
))
end
end
ft.opt_name(s, DummyValue, translate("Name"))
local function rule_proto_txt(self, s)
local f = self.map:get(s, "family")
local p = ft.fmt_proto(self.map:get(s, "proto"),
self.map:get(s, "icmp_type")) or "TCP+UDP"
if f and f:match("4") then
return "%s-%s" %{ translate("IPv4"), p }
elseif f and f:match("6") then
return "%s-%s" %{ translate("IPv6"), p }
else
return "%s %s" %{ translate("Any"), p }
end
end
local function rule_src_txt(self, s)
local z = ft.fmt_zone(self.map:get(s, "src"), translate("any zone"))
local a = ft.fmt_ip(self.map:get(s, "src_ip"), translate("any host"))
local p = ft.fmt_port(self.map:get(s, "src_port"))
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
if p and m then
return translatef("From %s in %s with source %s and %s", a, z, p, m)
elseif p or m then
return translatef("From %s in %s with source %s", a, z, p or m)
else
return translatef("From %s in %s", a, z)
end
end
local function rule_dest_txt(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"))
local p = ft.fmt_port(self.map:get(s, "dest_port"))
-- Forward
if z then
local a = ft.fmt_ip(self.map:get(s, "dest_ip"), translate("any host"))
if p then
return translatef("To %s, %s in %s", a, p, z)
else
return translatef("To %s in %s", a, z)
end
-- Input
else
local a = ft.fmt_ip(self.map:get(s, "dest_ip"),
translate("any router IP"))
if p then
return translatef("To %s at %s on <var>this device</var>", a, p)
else
return translatef("To %s on <var>this device</var>", a)
end
end
end
local function snat_dest_txt(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"), translate("any zone"))
local a = ft.fmt_ip(self.map:get(s, "dest_ip"), translate("any host"))
local p = ft.fmt_port(self.map:get(s, "dest_port")) or
ft.fmt_port(self.map:get(s, "src_dport"))
if p then
return translatef("To %s, %s in %s", a, p, z)
else
return translatef("To %s in %s", a, z)
end
end
match = s:option(DummyValue, "match", translate("Match"))
match.rawhtml = true
match.width = "70%"
function match.cfgvalue(self, s)
return "<small>%s<br />%s<br />%s</small>" % {
rule_proto_txt(self, s),
rule_src_txt(self, s),
rule_dest_txt(self, s)
}
end
target = s:option(DummyValue, "target", translate("Action"))
target.rawhtml = true
target.width = "20%"
function target.cfgvalue(self, s)
local t = ft.fmt_target(self.map:get(s, "target"), self.map:get(s, "dest"))
local l = ft.fmt_limit(self.map:get(s, "limit"),
self.map:get(s, "limit_burst"))
if l then
return translatef("<var>%s</var> and limit to %s", t, l)
else
return "<var>%s</var>" % t
end
end
ft.opt_enabled(s, Flag, translate("Enable")).width = "1%"
--
-- SNAT
--
s = m:section(TypedSection, "redirect",
translate("Source NAT"),
translate("Source NAT is a specific form of masquerading which allows \
fine grained control over the source IP used for outgoing traffic, \
for example to map multiple WAN addresses to internal subnets."))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.sortable = true
s.extedit = ds.build_url("admin/network/firewall/rules/%s")
s.template_addremove = "firewall/cbi_addsnat"
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
local n = m:formvalue("_newsnat.name")
local s = m:formvalue("_newsnat.src")
local d = m:formvalue("_newsnat.dest")
local a = m:formvalue("_newsnat.dip")
local p = m:formvalue("_newsnat.dport")
local x = m:formvalue("_newsnat.submit")
if x and a and #a > 0 then
created = TypedSection.create(self, section)
self.map:set(created, "target", "SNAT")
self.map:set(created, "src", s)
self.map:set(created, "dest", d)
self.map:set(created, "proto", "all")
self.map:set(created, "src_dip", a)
self.map:set(created, "src_dport", p)
self.map:set(created, "name", n)
end
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin/network/firewall/rules", created
))
end
end
function s.filter(self, sid)
return (self.map:get(sid, "target") == "SNAT")
end
ft.opt_name(s, DummyValue, translate("Name"))
match = s:option(DummyValue, "match", translate("Match"))
match.rawhtml = true
match.width = "70%"
function match.cfgvalue(self, s)
return "<small>%s<br />%s<br />%s</small>" % {
rule_proto_txt(self, s),
rule_src_txt(self, s),
snat_dest_txt(self, s)
}
end
snat = s:option(DummyValue, "via", translate("Action"))
snat.rawhtml = true
snat.width = "20%"
function snat.cfgvalue(self, s)
local a = ft.fmt_ip(self.map:get(s, "src_dip"))
local p = ft.fmt_port(self.map:get(s, "src_dport"))
if a and p then
return translatef("Rewrite to source %s, %s", a, p)
else
return translatef("Rewrite to source %s", a or p)
end
end
ft.opt_enabled(s, Flag, translate("Enable")).width = "1%"
return m

View File

@ -0,0 +1,243 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010-2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: zone-details.lua 8169 2012-01-09 05:48:27Z jow $
]]--
local nw = require "luci.model.network"
local fw = require "luci.model.firewall"
local ds = require "luci.dispatcher"
local ut = require "luci.util"
local m, p, i, v
local s, name, net, family, msrc, mdest, log, lim
local s2, out, inp
m = Map("firewall", translate("Firewall - Zone Settings"))
m.redirect = luci.dispatcher.build_url("admin/network/firewall/zones")
fw.init(m.uci)
nw.init(m.uci)
local zone = fw:get_zone(arg[1])
if not zone then
luci.http.redirect(dsp.build_url("admin/network/firewall/zones"))
return
else
m.title = "%s - %s" %{
translate("Firewall - Zone Settings"),
translatef("Zone %q", zone:name() or "?")
}
end
s = m:section(NamedSection, zone.sid, "zone",
translatef("Zone %q", zone:name()),
translatef("This section defines common properties of %q. \
The <em>input</em> and <em>output</em> options set the default \
policies for traffic entering and leaving this zone while the \
<em>forward</em> option describes the policy for forwarded traffic \
between different networks within the zone. \
<em>Covered networks</em> specifies which available networks are \
member of this zone.", zone:name()))
s.anonymous = true
s.addremove = false
m.on_commit = function(map)
local zone = fw:get_zone(arg[1])
if zone then
s.section = zone.sid
s2.section = zone.sid
end
end
s:tab("general", translate("General Settings"))
s:tab("advanced", translate("Advanced Settings"))
name = s:taboption("general", Value, "name", translate("Name"))
name.optional = false
name.forcewrite = true
name.datatype = "uciname"
function name.write(self, section, value)
if zone:name() ~= value then
fw:rename_zone(zone:name(), value)
out.exclude = value
inp.exclude = value
end
m.redirect = ds.build_url("admin/network/firewall/zones", value)
m.title = "%s - %s" %{
translate("Firewall - Zone Settings"),
translatef("Zone %q", value or "?")
}
end
p = {
s:taboption("general", ListValue, "input", translate("Input")),
s:taboption("general", ListValue, "output", translate("Output")),
s:taboption("general", ListValue, "forward", translate("Forward"))
}
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
s:taboption("general", Flag, "masq", translate("Masquerading"))
s:taboption("general", Flag, "mtu_fix", translate("MSS clamping"))
net = s:taboption("general", Value, "network", translate("Covered networks"))
net.template = "cbi/network_netlist"
net.widget = "checkbox"
net.cast = "string"
function net.formvalue(self, section)
return Value.formvalue(self, section) or "-"
end
function net.cfgvalue(self, section)
return Value.cfgvalue(self, section) or name:cfgvalue(section)
end
function net.write(self, section, value)
zone:clear_networks()
local n
for n in ut.imatch(value) do
zone:add_network(n)
end
end
family = s:taboption("advanced", ListValue, "family",
translate("Restrict to address family"))
family.rmempty = true
family:value("", translate("IPv4 and IPv6"))
family:value("ipv4", translate("IPv4 only"))
family:value("ipv6", translate("IPv6 only"))
msrc = s:taboption("advanced", DynamicList, "masq_src",
translate("Restrict Masquerading to given source subnets"))
msrc.optional = true
msrc.datatype = "list(neg(or(uciname,hostname,ip4addr)))"
msrc.placeholder = "0.0.0.0/0"
msrc:depends("family", "")
msrc:depends("family", "ipv4")
mdest = s:taboption("advanced", DynamicList, "masq_dest",
translate("Restrict Masquerading to given destination subnets"))
mdest.optional = true
mdest.datatype = "list(neg(or(uciname,hostname,ip4addr)))"
mdest.placeholder = "0.0.0.0/0"
mdest:depends("family", "")
mdest:depends("family", "ipv4")
s:taboption("advanced", Flag, "conntrack",
translate("Force connection tracking"))
log = s:taboption("advanced", Flag, "log",
translate("Enable logging on this zone"))
log.rmempty = true
log.enabled = "1"
lim = s:taboption("advanced", Value, "log_limit",
translate("Limit log messages"))
lim.placeholder = "10/minute"
lim:depends("log", "1")
s2 = m:section(NamedSection, zone.sid, "fwd_out",
translate("Inter-Zone Forwarding"),
translatef("The options below control the forwarding policies between \
this zone (%s) and other zones. <em>Destination zones</em> cover \
forwarded traffic <strong>originating from %q</strong>. \
<em>Source zones</em> match forwarded traffic from other zones \
<strong>targeted at %q</strong>. The forwarding rule is \
<em>unidirectional</em>, e.g. a forward from lan to wan does \
<em>not</em> imply a permission to forward from wan to lan as well.",
zone:name(), zone:name(), zone:name()
))
out = s2:option(Value, "out",
translate("Allow forward to <em>destination zones</em>:"))
out.nocreate = true
out.widget = "checkbox"
out.exclude = zone:name()
out.template = "cbi/firewall_zonelist"
inp = s2:option(Value, "in",
translate("Allow forward from <em>source zones</em>:"))
inp.nocreate = true
inp.widget = "checkbox"
inp.exclude = zone:name()
inp.template = "cbi/firewall_zonelist"
function out.cfgvalue(self, section)
local v = { }
local f
for _, f in ipairs(zone:get_forwardings_by("src")) do
v[#v+1] = f:dest()
end
return table.concat(v, " ")
end
function inp.cfgvalue(self, section)
local v = { }
local f
for _, f in ipairs(zone:get_forwardings_by("dest")) do
v[#v+1] = f:src()
end
return v
end
function out.formvalue(self, section)
return Value.formvalue(self, section) or "-"
end
function inp.formvalue(self, section)
return Value.formvalue(self, section) or "-"
end
function out.write(self, section, value)
zone:del_forwardings_by("src")
local f
for f in ut.imatch(value) do
zone:add_forwarding_to(f)
end
end
function inp.write(self, section, value)
zone:del_forwardings_by("dest")
local f
for f in ut.imatch(value) do
zone:add_forwarding_from(f)
end
end
return m

View File

@ -0,0 +1,88 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: zones.lua 8108 2011-12-19 21:16:31Z jow $
]]--
local ds = require "luci.dispatcher"
local fw = require "luci.model.firewall"
local m, s, o, p, i, v
m = Map("firewall",
translate("Firewall - Zone Settings"),
translate("The firewall creates zones over your network interfaces to control network traffic flow."))
fw.init(m.uci)
s = m:section(TypedSection, "defaults", translate("General Settings"))
s.anonymous = true
s.addremove = false
s:option(Flag, "syn_flood", translate("Enable SYN-flood protection"))
o = s:option(Flag, "drop_invalid", translate("Drop invalid packets"))
o.default = o.disabled
p = {
s:option(ListValue, "input", translate("Input")),
s:option(ListValue, "output", translate("Output")),
s:option(ListValue, "forward", translate("Forward"))
}
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
s = m:section(TypedSection, "zone", translate("Zones"))
s.template = "cbi/tblsection"
s.anonymous = true
s.addremove = true
s.extedit = ds.build_url("admin", "network", "firewall", "zones", "%s")
function s.create(self)
local z = fw:new_zone()
if z then
luci.http.redirect(
ds.build_url("admin", "network", "firewall", "zones", z.sid)
)
end
end
function s.remove(self, section)
return fw:del_zone(section)
end
o = s:option(DummyValue, "_info", translate("Zone ⇒ Forwardings"))
o.template = "cbi/firewall_zoneforwards"
o.cfgvalue = function(self, section)
return self.map:get(section, "name")
end
p = {
s:option(ListValue, "input", translate("Input")),
s:option(ListValue, "output", translate("Output")),
s:option(ListValue, "forward", translate("Forward"))
}
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
s:option(Flag, "masq", translate("Masquerading"))
s:option(Flag, "mtu_fix", translate("MSS clamping"))
return m

View File

@ -0,0 +1,582 @@
--[[
LuCI - Firewall model
Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local type, pairs, ipairs, table, luci, math
= type, pairs, ipairs, table, luci, math
local tpl = require "luci.template.parser"
local utl = require "luci.util"
local uci = require "luci.model.uci"
module "luci.model.firewall"
local uci_r, uci_s
function _valid_id(x)
return (x and #x > 0 and x:match("^[a-zA-Z0-9_]+$"))
end
function _get(c, s, o)
return uci_r:get(c, s, o)
end
function _set(c, s, o, v)
if v ~= nil then
if type(v) == "boolean" then v = v and "1" or "0" end
return uci_r:set(c, s, o, v)
else
return uci_r:delete(c, s, o)
end
end
function init(cursor)
uci_r = cursor or uci_r or uci.cursor()
uci_s = uci_r:substate()
return _M
end
function save(self, ...)
uci_r:save(...)
uci_r:load(...)
end
function commit(self, ...)
uci_r:commit(...)
uci_r:load(...)
end
function get_defaults()
return defaults()
end
function new_zone(self)
local name = "newzone"
local count = 1
while self:get_zone(name) do
count = count + 1
name = "newzone%d" % count
end
return self:add_zone(name)
end
function add_zone(self, n)
if _valid_id(n) and not self:get_zone(n) then
local d = defaults()
local z = uci_r:section("firewall", "zone", nil, {
name = n,
network = " ",
input = d:input() or "DROP",
forward = d:forward() or "DROP",
output = d:output() or "DROP"
})
return z and zone(z)
end
end
function get_zone(self, n)
if uci_r:get("firewall", n) == "zone" then
return zone(n)
else
local z
uci_r:foreach("firewall", "zone",
function(s)
if n and s.name == n then
z = s['.name']
return false
end
end)
return z and zone(z)
end
end
function get_zones(self)
local zones = { }
local znl = { }
uci_r:foreach("firewall", "zone",
function(s)
if s.name then
znl[s.name] = zone(s['.name'])
end
end)
local z
for z in utl.kspairs(znl) do
zones[#zones+1] = znl[z]
end
return zones
end
function get_zone_by_network(self, net)
local z
uci_r:foreach("firewall", "zone",
function(s)
if s.name and net then
local n
for n in utl.imatch(s.network or s.name) do
if n == net then
z = s['.name']
return false
end
end
end
end)
return z and zone(z)
end
function del_zone(self, n)
local r = false
if uci_r:get("firewall", n) == "zone" then
local z = uci_r:get("firewall", n, "name")
r = uci_r:delete("firewall", n)
n = z
else
uci_r:foreach("firewall", "zone",
function(s)
if n and s.name == n then
r = uci_r:delete("firewall", s['.name'])
return false
end
end)
end
if r then
uci_r:foreach("firewall", "rule",
function(s)
if s.src == n or s.dest == n then
uci_r:delete("firewall", s['.name'])
end
end)
uci_r:foreach("firewall", "redirect",
function(s)
if s.src == n or s.dest == n then
uci_r:delete("firewall", s['.name'])
end
end)
uci_r:foreach("firewall", "forwarding",
function(s)
if s.src == n or s.dest == n then
uci_r:delete("firewall", s['.name'])
end
end)
end
return r
end
function rename_zone(self, old, new)
local r = false
if _valid_id(new) and not self:get_zone(new) then
uci_r:foreach("firewall", "zone",
function(s)
if old and s.name == old then
if not s.network then
uci_r:set("firewall", s['.name'], "network", old)
end
uci_r:set("firewall", s['.name'], "name", new)
r = true
return false
end
end)
if r then
uci_r:foreach("firewall", "rule",
function(s)
if s.src == old then
uci_r:set("firewall", s['.name'], "src", new)
end
if s.dest == old then
uci_r:set("firewall", s['.name'], "dest", new)
end
end)
uci_r:foreach("firewall", "redirect",
function(s)
if s.src == old then
uci_r:set("firewall", s['.name'], "src", new)
end
if s.dest == old then
uci_r:set("firewall", s['.name'], "dest", new)
end
end)
uci_r:foreach("firewall", "forwarding",
function(s)
if s.src == old then
uci_r:set("firewall", s['.name'], "src", new)
end
if s.dest == old then
uci_r:set("firewall", s['.name'], "dest", new)
end
end)
end
end
return r
end
function del_network(self, net)
local z
if net then
for _, z in ipairs(self:get_zones()) do
z:del_network(net)
end
end
end
defaults = utl.class()
function defaults.__init__(self)
uci_r:foreach("firewall", "defaults",
function(s)
self.sid = s['.name']
return false
end)
self.sid = self.sid or uci_r:section("firewall", "defaults", nil, { })
end
function defaults.get(self, opt)
return _get("firewall", self.sid, opt)
end
function defaults.set(self, opt, val)
return _set("firewall", self.sid, opt, val)
end
function defaults.syn_flood(self)
return (self:get("syn_flood") == "1")
end
function defaults.drop_invalid(self)
return (self:get("drop_invalid") == "1")
end
function defaults.input(self)
return self:get("input") or "DROP"
end
function defaults.forward(self)
return self:get("forward") or "DROP"
end
function defaults.output(self)
return self:get("output") or "DROP"
end
zone = utl.class()
function zone.__init__(self, z)
if uci_r:get("firewall", z) == "zone" then
self.sid = z
self.data = uci_r:get_all("firewall", z)
else
uci_r:foreach("firewall", "zone",
function(s)
if s.name == z then
self.sid = s['.name']
self.data = s
return false
end
end)
end
end
function zone.get(self, opt)
return _get("firewall", self.sid, opt)
end
function zone.set(self, opt, val)
return _set("firewall", self.sid, opt, val)
end
function zone.masq(self)
return (self:get("masq") == "1")
end
function zone.name(self)
return self:get("name")
end
function zone.network(self)
return self:get("network")
end
function zone.input(self)
return self:get("input") or defaults():input() or "DROP"
end
function zone.forward(self)
return self:get("forward") or defaults():forward() or "DROP"
end
function zone.output(self)
return self:get("output") or defaults():output() or "DROP"
end
function zone.add_network(self, net)
if uci_r:get("network", net) == "interface" then
local nets = { }
local n
for n in utl.imatch(self:get("network") or self:get("name")) do
if n ~= net then
nets[#nets+1] = n
end
end
nets[#nets+1] = net
_M:del_network(net)
self:set("network", table.concat(nets, " "))
end
end
function zone.del_network(self, net)
local nets = { }
local n
for n in utl.imatch(self:get("network") or self:get("name")) do
if n ~= net then
nets[#nets+1] = n
end
end
if #nets > 0 then
self:set("network", table.concat(nets, " "))
else
self:set("network", " ")
end
end
function zone.get_networks(self)
local nets = { }
local n
for n in utl.imatch(self:get("network") or self:get("name")) do
nets[#nets+1] = n
end
return nets
end
function zone.clear_networks(self)
self:set("network", " ")
end
function zone.get_forwardings_by(self, what)
local name = self:name()
local forwards = { }
uci_r:foreach("firewall", "forwarding",
function(s)
if s.src and s.dest and s[what] == name then
forwards[#forwards+1] = forwarding(s['.name'])
end
end)
return forwards
end
function zone.add_forwarding_to(self, dest)
local exist, forward
for _, forward in ipairs(self:get_forwardings_by('src')) do
if forward:dest() == dest then
exist = true
break
end
end
if not exist and dest ~= self:name() and _valid_id(dest) then
local s = uci_r:section("firewall", "forwarding", nil, {
src = self:name(),
dest = dest
})
return s and forwarding(s)
end
end
function zone.add_forwarding_from(self, src)
local exist, forward
for _, forward in ipairs(self:get_forwardings_by('dest')) do
if forward:src() == src then
exist = true
break
end
end
if not exist and src ~= self:name() and _valid_id(src) then
local s = uci_r:section("firewall", "forwarding", nil, {
src = src,
dest = self:name()
})
return s and forwarding(s)
end
end
function zone.del_forwardings_by(self, what)
local name = self:name()
uci_r:delete_all("firewall", "forwarding",
function(s)
return (s.src and s.dest and s[what] == name)
end)
end
function zone.add_redirect(self, options)
options = options or { }
options.src = self:name()
local s = uci_r:section("firewall", "redirect", nil, options)
return s and redirect(s)
end
function zone.add_rule(self, options)
options = options or { }
options.src = self:name()
local s = uci_r:section("firewall", "rule", nil, options)
return s and rule(s)
end
function zone.get_color(self)
if self and self:name() == "lan" then
return "#90f090"
elseif self and self:name() == "wan" then
return "#f09090"
elseif self then
math.randomseed(tpl.hash(self:name()))
local r = math.random(128)
local g = math.random(128)
local min = 0
local max = 128
if ( r + g ) < 128 then
min = 128 - r - g
else
max = 255 - r - g
end
local b = min + math.floor( math.random() * ( max - min ) )
return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
else
return "#eeeeee"
end
end
forwarding = utl.class()
function forwarding.__init__(self, f)
self.sid = f
end
function forwarding.src(self)
return uci_r:get("firewall", self.sid, "src")
end
function forwarding.dest(self)
return uci_r:get("firewall", self.sid, "dest")
end
function forwarding.src_zone(self)
return zone(self:src())
end
function forwarding.dest_zone(self)
return zone(self:dest())
end
rule = utl.class()
function rule.__init__(self, f)
self.sid = f
end
function rule.get(self, opt)
return _get("firewall", self.sid, opt)
end
function rule.set(self, opt, val)
return _set("firewall", self.sid, opt, val)
end
function rule.src(self)
return uci_r:get("firewall", self.sid, "src")
end
function rule.dest(self)
return uci_r:get("firewall", self.sid, "dest")
end
function rule.src_zone(self)
return zone(self:src())
end
function rule.dest_zone(self)
return zone(self:dest())
end
redirect = utl.class()
function redirect.__init__(self, f)
self.sid = f
end
function redirect.get(self, opt)
return _get("firewall", self.sid, opt)
end
function redirect.set(self, opt, val)
return _set("firewall", self.sid, opt, val)
end
function redirect.src(self)
return uci_r:get("firewall", self.sid, "src")
end
function redirect.dest(self)
return uci_r:get("firewall", self.sid, "dest")
end
function redirect.src_zone(self)
return zone(self:src())
end
function redirect.dest_zone(self)
return zone(self:dest())
end

View File

@ -0,0 +1,239 @@
--[[
LuCI - Lua Configuration Interface
(c) 2008-2011 Jo-Philipp Wich <xm@subsignal.org>
(c) 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local os = require "os"
local io = require "io"
local fs = require "nixio.fs"
local util = require "luci.util"
local type = type
local pairs = pairs
local error = error
local table = table
local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
local icfg = "/etc/opkg.conf"
--- LuCI OPKG call abstraction library
module "luci.model.ipkg"
-- Internal action function
local function _action(cmd, ...)
local pkg = ""
for k, v in pairs({...}) do
pkg = pkg .. " '" .. v:gsub("'", "") .. "'"
end
local c = "%s %s %s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" %{ ipkg, cmd, pkg }
local r = os.execute(c)
local e = fs.readfile("/tmp/opkg.stderr")
local o = fs.readfile("/tmp/opkg.stdout")
fs.unlink("/tmp/opkg.stderr")
fs.unlink("/tmp/opkg.stdout")
return r, o or "", e or ""
end
-- Internal parser function
local function _parselist(rawdata)
if type(rawdata) ~= "function" then
error("OPKG: Invalid rawdata given")
end
local data = {}
local c = {}
local l = nil
for line in rawdata do
if line:sub(1, 1) ~= " " then
local key, val = line:match("(.-): ?(.*)%s*")
if key and val then
if key == "Package" then
c = {Package = val}
data[val] = c
elseif key == "Status" then
c.Status = {}
for j in val:gmatch("([^ ]+)") do
c.Status[j] = true
end
else
c[key] = val
end
l = key
end
else
-- Multi-line field
c[l] = c[l] .. "\n" .. line
end
end
return data
end
-- Internal lookup function
local function _lookup(act, pkg)
local cmd = ipkg .. " " .. act
if pkg then
cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'"
end
-- OPKG sometimes kills the whole machine because it sucks
-- Therefore we have to use a sucky approach too and use
-- tmpfiles instead of directly reading the output
local tmpfile = os.tmpname()
os.execute(cmd .. (" >%s 2>/dev/null" % tmpfile))
local data = _parselist(io.lines(tmpfile))
os.remove(tmpfile)
return data
end
--- Return information about installed and available packages.
-- @param pkg Limit output to a (set of) packages
-- @return Table containing package information
function info(pkg)
return _lookup("info", pkg)
end
--- Return the package status of one or more packages.
-- @param pkg Limit output to a (set of) packages
-- @return Table containing package status information
function status(pkg)
return _lookup("status", pkg)
end
--- Install one or more packages.
-- @param ... List of packages to install
-- @return Boolean indicating the status of the action
-- @return OPKG return code, STDOUT and STDERR
function install(...)
return _action("install", ...)
end
--- Determine whether a given package is installed.
-- @param pkg Package
-- @return Boolean
function installed(pkg)
local p = status(pkg)[pkg]
return (p and p.Status and p.Status.installed)
end
--- Remove one or more packages.
-- @param ... List of packages to install
-- @return Boolean indicating the status of the action
-- @return OPKG return code, STDOUT and STDERR
function remove(...)
return _action("remove", ...)
end
--- Update package lists.
-- @return Boolean indicating the status of the action
-- @return OPKG return code, STDOUT and STDERR
function update()
return _action("update")
end
--- Upgrades all installed packages.
-- @return Boolean indicating the status of the action
-- @return OPKG return code, STDOUT and STDERR
function upgrade()
return _action("upgrade")
end
-- List helper
function _list(action, pat, cb)
local fd = io.popen(ipkg .. " " .. action ..
(pat and (" '%s'" % pat:gsub("'", "")) or ""))
if fd then
local name, version, desc
while true do
local line = fd:read("*l")
if not line then break end
name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
if not name then
name, version = line:match("^(.-) %- (.+)")
desc = ""
end
cb(name, version, desc)
name = nil
version = nil
desc = nil
end
fd:close()
end
end
--- List all packages known to opkg.
-- @param pat Only find packages matching this pattern, nil lists all packages
-- @param cb Callback function invoked for each package, receives name, version and description as arguments
-- @return nothing
function list_all(pat, cb)
_list("list", pat, cb)
end
--- List installed packages.
-- @param pat Only find packages matching this pattern, nil lists all packages
-- @param cb Callback function invoked for each package, receives name, version and description as arguments
-- @return nothing
function list_installed(pat, cb)
_list("list_installed", pat, cb)
end
--- Find packages that match the given pattern.
-- @param pat Find packages whose names or descriptions match this pattern, nil results in zero results
-- @param cb Callback function invoked for each patckage, receives name, version and description as arguments
-- @return nothing
function find(pat, cb)
_list("find", pat, cb)
end
--- Determines the overlay root used by opkg.
-- @return String containing the directory path of the overlay root.
function overlay_root()
local od = "/"
local fd = io.open(icfg, "r")
if fd then
local ln
repeat
ln = fd:read("*l")
if ln and ln:match("^%s*option%s+overlay_root%s+") then
od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
local s = fs.stat(od)
if not s or s.type ~= "dir" then
od = "/"
end
break
end
until not ln
fd:close()
end
return od
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
--[[
LuCI - Network model - 3G, PPP, PPtP, PPPoE and PPPoA protocol extension
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local netmod = luci.model.network
local _, p
for _, p in ipairs({"ppp", "pptp", "pppoe", "pppoa", "3g", "l2tp"}) do
local proto = netmod:register_protocol(p)
function proto.get_i18n(self)
if p == "ppp" then
return luci.i18n.translate("PPP")
elseif p == "pptp" then
return luci.i18n.translate("PPtP")
elseif p == "3g" then
return luci.i18n.translate("UMTS/GPRS/EV-DO")
elseif p == "pppoe" then
return luci.i18n.translate("PPPoE")
elseif p == "pppoa" then
return luci.i18n.translate("PPPoATM")
elseif p == "l2tp" then
return luci.i18n.translate("L2TP")
end
end
function proto.ifname(self)
return p .. "-" .. self.sid
end
function proto.opkg_package(self)
if p == "ppp" then
return p
elseif p == "3g" then
return "comgt"
elseif p == "pptp" then
return "ppp-mod-pptp"
elseif p == "pppoe" then
return "ppp-mod-pppoe"
elseif p == "pppoa" then
return "ppp-mod-pppoa"
elseif p == "l2tp" then
return "xl2tpd"
end
end
function proto.is_installed(self)
if p == "pppoa" then
return (nixio.fs.glob("/usr/lib/pppd/*/pppoatm.so")() ~= nil)
elseif p == "pppoe" then
return (nixio.fs.glob("/usr/lib/pppd/*/rp-pppoe.so")() ~= nil)
elseif p == "pptp" then
return (nixio.fs.glob("/usr/lib/pppd/*/pptp.so")() ~= nil)
elseif p == "3g" then
return nixio.fs.access("/lib/netifd/proto/3g.sh")
elseif p == "l2tp" then
return nixio.fs.access("/lib/netifd/proto/l2tp.sh")
else
return nixio.fs.access("/lib/netifd/proto/ppp.sh")
end
end
function proto.is_floating(self)
return (p ~= "pppoe")
end
function proto.is_virtual(self)
return true
end
function proto.get_interfaces(self)
if self:is_floating() then
return nil
else
return netmod.protocol.get_interfaces(self)
end
end
function proto.contains_interface(self, ifc)
if self:is_floating() then
return (netmod:ifnameof(ifc) == self:ifname())
else
return netmod.protocol.contains_interface(self, ifc)
end
end
netmod:register_pattern_virtual("^%s-%%w" % p)
end

View File

@ -0,0 +1,404 @@
--[[
LuCI - UCI model
Description:
Generalized UCI model
FileId:
$Id: uci.lua 8127 2011-12-20 19:02:14Z jow $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local os = require "os"
local uci = require "uci"
local util = require "luci.util"
local table = require "table"
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
local require, getmetatable = require, getmetatable
local error, pairs, ipairs = error, pairs, ipairs
local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
--- LuCI UCI model library.
-- The typical workflow for UCI is: Get a cursor instance from the
-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
-- save the changes to the staging area via Cursor.save and finally
-- Cursor.commit the data to the actual config files.
-- LuCI then needs to Cursor.apply the changes so deamons etc. are
-- reloaded.
-- @cstyle instance
module "luci.model.uci"
--- Create a new UCI-Cursor.
-- @class function
-- @name cursor
-- @return UCI-Cursor
cursor = uci.cursor
APIVERSION = uci.APIVERSION
--- Create a new Cursor initialized to the state directory.
-- @return UCI cursor
function cursor_state()
return cursor(nil, "/var/state")
end
inst = cursor()
inst_state = cursor_state()
local Cursor = getmetatable(inst)
--- Applies UCI configuration changes
-- @param configlist List of UCI configurations
-- @param command Don't apply only return the command
function Cursor.apply(self, configlist, command)
configlist = self:_affected(configlist)
if command then
return { "/sbin/luci-reload", unpack(configlist) }
else
return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
% table.concat(configlist, " "))
end
end
--- Delete all sections of a given type that match certain criteria.
-- @param config UCI config
-- @param type UCI section type
-- @param comparator Function that will be called for each section and
-- returns a boolean whether to delete the current section (optional)
function Cursor.delete_all(self, config, stype, comparator)
local del = {}
if type(comparator) == "table" then
local tbl = comparator
comparator = function(section)
for k, v in pairs(tbl) do
if section[k] ~= v then
return false
end
end
return true
end
end
local function helper (section)
if not comparator or comparator(section) then
del[#del+1] = section[".name"]
end
end
self:foreach(config, stype, helper)
for i, j in ipairs(del) do
self:delete(config, j)
end
end
--- Create a new section and initialize it with data.
-- @param config UCI config
-- @param type UCI section type
-- @param name UCI section name (optional)
-- @param values Table of key - value pairs to initialize the section with
-- @return Name of created section
function Cursor.section(self, config, type, name, values)
local stat = true
if name then
stat = self:set(config, name, type)
else
name = self:add(config, type)
stat = name and true
end
if stat and values then
stat = self:tset(config, name, values)
end
return stat and name
end
--- Updated the data of a section using data from a table.
-- @param config UCI config
-- @param section UCI section name (optional)
-- @param values Table of key - value pairs to update the section with
function Cursor.tset(self, config, section, values)
local stat = true
for k, v in pairs(values) do
if k:sub(1, 1) ~= "." then
stat = stat and self:set(config, section, k, v)
end
end
return stat
end
--- Get a boolean option and return it's value as true or false.
-- @param config UCI config
-- @param section UCI section name
-- @param option UCI option
-- @return Boolean
function Cursor.get_bool(self, ...)
local val = self:get(...)
return ( val == "1" or val == "true" or val == "yes" or val == "on" )
end
--- Get an option or list and return values as table.
-- @param config UCI config
-- @param section UCI section name
-- @param option UCI option
-- @return UCI value
function Cursor.get_list(self, config, section, option)
if config and section and option then
local val = self:get(config, section, option)
return ( type(val) == "table" and val or { val } )
end
return nil
end
--- Get the given option from the first section with the given type.
-- @param config UCI config
-- @param type UCI section type
-- @param option UCI option (optional)
-- @param default Default value (optional)
-- @return UCI value
function Cursor.get_first(self, conf, stype, opt, def)
local rv = def
self:foreach(conf, stype,
function(s)
local val = not opt and s['.name'] or s[opt]
if type(def) == "number" then
val = tonumber(val)
elseif type(def) == "boolean" then
val = (val == "1" or val == "true" or
val == "yes" or val == "on")
end
if val ~= nil then
rv = val
return false
end
end)
return rv
end
--- Set given values as list.
-- @param config UCI config
-- @param section UCI section name
-- @param option UCI option
-- @param value UCI value
-- @return Boolean whether operation succeeded
function Cursor.set_list(self, config, section, option, value)
if config and section and option then
return self:set(
config, section, option,
( type(value) == "table" and value or { value } )
)
end
return false
end
-- Return a list of initscripts affected by configuration changes.
function Cursor._affected(self, configlist)
configlist = type(configlist) == "table" and configlist or {configlist}
local c = cursor()
c:load("ucitrack")
-- Resolve dependencies
local reloadlist = {}
local function _resolve_deps(name)
local reload = {name}
local deps = {}
c:foreach("ucitrack", name,
function(section)
if section.affects then
for i, aff in ipairs(section.affects) do
deps[#deps+1] = aff
end
end
end)
for i, dep in ipairs(deps) do
for j, add in ipairs(_resolve_deps(dep)) do
reload[#reload+1] = add
end
end
return reload
end
-- Collect initscripts
for j, config in ipairs(configlist) do
for i, e in ipairs(_resolve_deps(config)) do
if not util.contains(reloadlist, e) then
reloadlist[#reloadlist+1] = e
end
end
end
return reloadlist
end
--- Create a sub-state of this cursor. The sub-state is tied to the parent
-- curser, means it the parent unloads or loads configs, the sub state will
-- do so as well.
-- @return UCI state cursor tied to the parent cursor
function Cursor.substate(self)
Cursor._substates = Cursor._substates or { }
Cursor._substates[self] = Cursor._substates[self] or cursor_state()
return Cursor._substates[self]
end
local _load = Cursor.load
function Cursor.load(self, ...)
if Cursor._substates and Cursor._substates[self] then
_load(Cursor._substates[self], ...)
end
return _load(self, ...)
end
local _unload = Cursor.unload
function Cursor.unload(self, ...)
if Cursor._substates and Cursor._substates[self] then
_unload(Cursor._substates[self], ...)
end
return _unload(self, ...)
end
--- Add an anonymous section.
-- @class function
-- @name Cursor.add
-- @param config UCI config
-- @param type UCI section type
-- @return Name of created section
--- Get a table of saved but uncommitted changes.
-- @class function
-- @name Cursor.changes
-- @param config UCI config
-- @return Table of changes
-- @see Cursor.save
--- Commit saved changes.
-- @class function
-- @name Cursor.commit
-- @param config UCI config
-- @return Boolean whether operation succeeded
-- @see Cursor.revert
-- @see Cursor.save
--- Deletes a section or an option.
-- @class function
-- @name Cursor.delete
-- @param config UCI config
-- @param section UCI section name
-- @param option UCI option (optional)
-- @return Boolean whether operation succeeded
--- Call a function for every section of a certain type.
-- @class function
-- @name Cursor.foreach
-- @param config UCI config
-- @param type UCI section type
-- @param callback Function to be called
-- @return Boolean whether operation succeeded
--- Get a section type or an option
-- @class function
-- @name Cursor.get
-- @param config UCI config
-- @param section UCI section name
-- @param option UCI option (optional)
-- @return UCI value
--- Get all sections of a config or all values of a section.
-- @class function
-- @name Cursor.get_all
-- @param config UCI config
-- @param section UCI section name (optional)
-- @return Table of UCI sections or table of UCI values
--- Manually load a config.
-- @class function
-- @name Cursor.load
-- @param config UCI config
-- @return Boolean whether operation succeeded
-- @see Cursor.save
-- @see Cursor.unload
--- Revert saved but uncommitted changes.
-- @class function
-- @name Cursor.revert
-- @param config UCI config
-- @return Boolean whether operation succeeded
-- @see Cursor.commit
-- @see Cursor.save
--- Saves changes made to a config to make them committable.
-- @class function
-- @name Cursor.save
-- @param config UCI config
-- @return Boolean whether operation succeeded
-- @see Cursor.load
-- @see Cursor.unload
--- Set a value or create a named section.
-- @class function
-- @name Cursor.set
-- @param config UCI config
-- @param section UCI section name
-- @param option UCI option or UCI section type
-- @param value UCI value or nil if you want to create a section
-- @return Boolean whether operation succeeded
--- Get the configuration directory.
-- @class function
-- @name Cursor.get_confdir
-- @return Configuration directory
--- Get the directory for uncomitted changes.
-- @class function
-- @name Cursor.get_savedir
-- @return Save directory
--- Set the configuration directory.
-- @class function
-- @name Cursor.set_confdir
-- @param directory UCI configuration directory
-- @return Boolean whether operation succeeded
--- Set the directory for uncommited changes.
-- @class function
-- @name Cursor.set_savedir
-- @param directory UCI changes directory
-- @return Boolean whether operation succeeded
--- Discard changes made to a config.
-- @class function
-- @name Cursor.unload
-- @param config UCI config
-- @return Boolean whether operation succeeded
-- @see Cursor.load
-- @see Cursor.save

165
1_1.mi_Lua/luci/sauth.lua Normal file
View File

@ -0,0 +1,165 @@
--[[
Session authentication
(c) 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: sauth.lua 8900 2012-08-08 09:48:47Z jow $
]]--
--- LuCI session library.
module("luci.sauth", package.seeall)
require("luci.util")
require("luci.sys")
require("luci.config")
local nixio = require "nixio", require "nixio.util"
local fs = require "nixio.fs"
local XQLog = require("xiaoqiang.XQLog")
luci.config.sauth = luci.config.sauth or {}
sessionpath = luci.config.sauth.sessionpath
sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
--- Prepare session storage by creating the session directory.
function prepare()
fs.mkdir(sessionpath, 700)
if not sane() then
error("Security Exception: Session path is not sane!")
end
end
local function _read(id)
local blob = fs.readfile(sessionpath .. "/" .. id)
return blob
end
local function _write(id, data)
local tempid = luci.sys.uniqueid(16)
local tempfile = sessionpath .. "/" .. tempid
local sessfile = sessionpath .. "/" .. id
local f = nixio.open(tempfile, "w", 600)
f:writeall(data)
f:close()
fs.rename(tempfile, sessfile)
end
local function _checkid(id)
return not not (id and #id == 32 and id:match("^[a-fA-F0-9]+$"))
end
--- Write session data to a session file.
-- @param id Session identifier
-- @param data Session data table
function write(id, data)
if not sane() then
prepare()
end
if not _checkid(id) then
XQLog.log(3,"Security Exception: Session ID is invalid! sauth.write")
return
end
if type(data) ~= "table" then
XQLog.log(3,"Security Exception: Session data invalid! sauth.write")
return
end
data.atime = luci.sys.uptime()
_write(id, luci.util.get_bytecode(data))
end
--- Read a session and return its content.
-- @param id Session identifier
-- @return Session data table or nil if the given id is not found
function read(id)
if not id or #id == 0 then
return nil
end
if not _checkid(id) then
XQLog.log(3,"Security Exception: Session ID is invalid! sauth.read")
return nil
end
if not sane(sessionpath .. "/" .. id) then
return nil
end
local blob = _read(id)
local func = loadstring(blob)
setfenv(func, {})
local sess = func()
if type(sess) ~= "table" then
XQLog.log(3,"Security Exception: Session data invalid! sauth.read")
return nil
end
if sess.atime and sess.atime + sessiontime < luci.sys.uptime() then
kill(id)
return nil
end
-- refresh atime in session
write(id, sess)
return sess
end
--- Check whether Session environment is sane.
-- @return Boolean status
function sane(file)
return luci.sys.process.info("uid")
== fs.stat(file or sessionpath, "uid")
and fs.stat(file or sessionpath, "modestr")
== (file and "rw-------" or "rwx------")
end
--- Kills a session
-- @param id Session identifier
function kill(id)
if not _checkid(id) then
XQLog.log(3,"Security Exception: Session ID is invalid! sauth.kill")
else
fs.unlink(sessionpath .. "/" .. id)
end
end
--- Remove all expired session data files
function reap()
if sane() then
local id
for id in nixio.fs.dir(sessionpath) do
if _checkid(id) then
-- reading the session will kill it if it is expired
read(id)
end
end
end
end
--- Get available session data
function available()
if sane() then
local id
for id in nixio.fs.dir(sessionpath) do
if _checkid(id) then
-- reading the session will kill it if it is expired
local available = read(id)
if available then
return available
end
end
end
end
return nil
end

View File

@ -0,0 +1,95 @@
--[[
LuCI - SGI-Module for CGI
Description:
Server Gateway Interface for CGI
FileId:
$Id: cgi.lua 6535 2010-11-23 01:02:21Z soma $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
exectime = os.clock()
module("luci.sgi.cgi", package.seeall)
local ltn12 = require("luci.ltn12")
require("nixio.util")
require("luci.http")
require("luci.sys")
require("luci.dispatcher")
-- Limited source to avoid endless blocking
local function limitsource(handle, limit)
limit = limit or 0
local BLOCKSIZE = ltn12.BLOCKSIZE
return function()
if limit < 1 then
handle:close()
return nil
else
local read = (limit > BLOCKSIZE) and BLOCKSIZE or limit
limit = limit - read
local chunk = handle:read(read)
if not chunk then handle:close() end
return chunk
end
end
end
function run()
local r = luci.http.Request(
luci.sys.getenv(),
limitsource(io.stdin, tonumber(luci.sys.getenv("CONTENT_LENGTH"))),
ltn12.sink.file(io.stderr)
)
local x = coroutine.create(luci.dispatcher.httpdispatch)
local hcache = ""
local active = true
while coroutine.status(x) ~= "dead" do
local res, id, data1, data2 = coroutine.resume(x, r)
if not res then
print("Status: 500 Internal Server Error")
print("Content-Type: text/plain\n")
print(id)
break;
end
if active then
if id == 1 then
io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
elseif id == 2 then
hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"
elseif id == 3 then
io.write(hcache)
io.write("\r\n")
elseif id == 4 then
io.write(tostring(data1 or ""))
elseif id == 5 then
io.flush()
io.close()
active = false
elseif id == 6 then
data1:copyz(nixio.stdout, data2)
data1:close()
end
end
end
end

View File

@ -0,0 +1,121 @@
--[[
LuCI - Server Gateway Interface for the uHTTPd server
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
require "nixio.util"
require "luci.http"
require "luci.sys"
require "luci.dispatcher"
require "luci.ltn12"
function handle_request(env)
exectime = os.clock()
local renv = {
CONTENT_LENGTH = env.CONTENT_LENGTH,
CONTENT_TYPE = env.CONTENT_TYPE,
REQUEST_METHOD = env.REQUEST_METHOD,
REQUEST_URI = env.REQUEST_URI,
PATH_INFO = env.PATH_INFO,
SCRIPT_NAME = env.SCRIPT_NAME:gsub("/+$", ""),
SCRIPT_FILENAME = env.SCRIPT_NAME,
SERVER_PROTOCOL = env.SERVER_PROTOCOL,
QUERY_STRING = env.QUERY_STRING
}
local k, v
for k, v in pairs(env.headers) do
k = k:upper():gsub("%-", "_")
renv["HTTP_" .. k] = v
end
local len = env.CONTENT_LENGTH or 0
local function recv()
if len > 0 then
local rlen, rbuf = uhttpd.recv(4096)
if rlen >= 0 then
len = len - rlen
return rbuf
end
end
return nil
end
local function send(...)
return uhttpd.send(...)
end
local function sendc(...)
if env.HTTP_VERSION > 1.0 then
return uhttpd.sendc(...)
else
return uhttpd.send(...)
end
end
local req = luci.http.Request(
renv, recv, luci.ltn12.sink.file(io.stderr)
)
local x = coroutine.create(luci.dispatcher.httpdispatch)
local hcache = { }
local active = true
if env.HTTP_VERSION > 1.0 then
hcache["Transfer-Encoding"] = "chunked"
end
while coroutine.status(x) ~= "dead" do
local res, id, data1, data2 = coroutine.resume(x, req)
if not res then
send(env.SERVER_PROTOCOL)
send(" 500 Internal Server Error\r\n")
send("Content-Type: text/plain\r\n\r\n")
send(tostring(id))
break
end
if active then
if id == 1 then
send(env.SERVER_PROTOCOL)
send(" ")
send(tostring(data1))
send(" ")
send(tostring(data2))
send("\r\n")
elseif id == 2 then
hcache[data1] = data2
elseif id == 3 then
for k, v in pairs(hcache) do
send(tostring(k))
send(": ")
send(tostring(v))
send("\r\n")
end
send("\r\n")
elseif id == 4 then
sendc(tostring(data1 or ""))
elseif id == 5 then
active = false
elseif id == 6 then
data1:copyz(nixio.stdout, data2)
end
end
end
end

16
1_1.mi_Lua/luci/store.lua Normal file
View File

@ -0,0 +1,16 @@
--[[
LuCI - Lua Development Framework
(c) 2009 Steven Barth <steven@midlink.org>
(c) 2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local util = require "luci.util"
module("luci.store", util.threadlocal)

966
1_1.mi_Lua/luci/sys.lua Normal file
View File

@ -0,0 +1,966 @@
--[[
LuCI - System library
Description:
Utilities for interaction with the Linux system
FileId:
$Id: sys.lua 9623 2013-01-18 14:08:37Z jow $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local io = require "io"
local os = require "os"
local table = require "table"
local nixio = require "nixio"
local fs = require "nixio.fs"
local uci = require "luci.model.uci"
local luci = {}
luci.util = require "luci.util"
luci.ip = require "luci.ip"
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
--- LuCI Linux and POSIX system utilities.
module "luci.sys"
--- Execute a given shell command and return the error code
-- @class function
-- @name call
-- @param ... Command to call
-- @return Error code of the command
function call(...)
return os.execute(...) / 256
end
--- Execute a given shell command and capture its standard output
-- @class function
-- @name exec
-- @param command Command to call
-- @return String containg the return the output of the command
exec = luci.util.exec
--- Retrieve information about currently mounted file systems.
-- @return Table containing mount information
function mounts()
local data = {}
local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
local ps = luci.util.execi("df")
if not ps then
return
else
ps()
end
for line in ps do
local row = {}
local j = 1
for value in line:gmatch("[^%s]+") do
row[k[j]] = value
j = j + 1
end
if row[k[1]] then
-- this is a rather ugly workaround to cope with wrapped lines in
-- the df output:
--
-- /dev/scsi/host0/bus0/target0/lun0/part3
-- 114382024 93566472 15005244 86% /mnt/usb
--
if not row[k[2]] then
j = 2
line = ps()
for value in line:gmatch("[^%s]+") do
row[k[j]] = value
j = j + 1
end
end
table.insert(data, row)
end
end
return data
end
--- Retrieve environment variables. If no variable is given then a table
-- containing the whole environment is returned otherwise this function returns
-- the corresponding string value for the given name or nil if no such variable
-- exists.
-- @class function
-- @name getenv
-- @param var Name of the environment variable to retrieve (optional)
-- @return String containg the value of the specified variable
-- @return Table containing all variables if no variable name is given
getenv = nixio.getenv
--- Get or set the current hostname.
-- @param String containing a new hostname to set (optional)
-- @return String containing the system hostname
function hostname(newname)
if type(newname) == "string" and #newname > 0 then
fs.writefile( "/proc/sys/kernel/hostname", newname )
return newname
else
return nixio.uname().nodename
end
end
--- Returns the contents of a documented referred by an URL.
-- @param url The URL to retrieve
-- @param stream Return a stream instead of a buffer
-- @param target Directly write to target file name
-- @return String containing the contents of given the URL
function httpget(url, stream, target)
if not target then
local source = stream and io.popen or luci.util.exec
return source("wget -qO- '"..url:gsub("'", "").."'")
else
return os.execute("wget -qO '%s' '%s'" %
{target:gsub("'", ""), url:gsub("'", "")})
end
end
--- Returns the system load average values.
-- @return String containing the average load value 1 minute ago
-- @return String containing the average load value 5 minutes ago
-- @return String containing the average load value 15 minutes ago
function loadavg()
local info = nixio.sysinfo()
return info.loads[1], info.loads[2], info.loads[3]
end
--- Initiate a system reboot.
-- @return Return value of os.execute()
function reboot()
return os.execute("reboot >/dev/null 2>&1")
end
--- Returns the system type, cpu name and installed physical memory.
-- @return String containing the system or platform identifier
-- @return String containing hardware model information
-- @return String containing the total memory amount in kB
-- @return String containing the memory used for caching in kB
-- @return String containing the memory used for buffering in kB
-- @return String containing the free memory amount in kB
-- @return String containing the cpu bogomips (number)
function sysinfo()
local cpuinfo = fs.readfile("/proc/cpuinfo")
local meminfo = fs.readfile("/proc/meminfo")
local memtotal = tonumber(meminfo:match("MemTotal:%s*(%d+)"))
local memcached = tonumber(meminfo:match("\nCached:%s*(%d+)"))
local memfree = tonumber(meminfo:match("MemFree:%s*(%d+)"))
local membuffers = tonumber(meminfo:match("Buffers:%s*(%d+)"))
local bogomips = tonumber(cpuinfo:match("[Bb]ogo[Mm][Ii][Pp][Ss].-: ([^\n]+)")) or 0
local system =
cpuinfo:match("system type\t+: ([^\n]+)") or
cpuinfo:match("Processor\t+: ([^\n]+)") or
cpuinfo:match("model name\t+: ([^\n]+)")
local model =
luci.util.pcdata(fs.readfile("/tmp/sysinfo/model")) or
cpuinfo:match("machine\t+: ([^\n]+)") or
cpuinfo:match("Hardware\t+: ([^\n]+)") or
luci.util.pcdata(fs.readfile("/proc/diag/model")) or
nixio.uname().machine or
system
return system, model, memtotal, memcached, membuffers, memfree, bogomips
end
--- Retrieves the output of the "logread" command.
-- @return String containing the current log buffer
function syslog()
return luci.util.exec("logread")
end
--- Retrieves the output of the "dmesg" command.
-- @return String containing the current log buffer
function dmesg()
return luci.util.exec("dmesg")
end
--- Generates a random id with specified length.
-- @param bytes Number of bytes for the unique id
-- @return String containing hex encoded id
function uniqueid(bytes)
local rand = fs.readfile("/dev/urandom", bytes)
return rand and nixio.bin.hexlify(rand)
end
--- Returns the current system uptime stats.
-- @return String containing total uptime in seconds
function uptime()
return nixio.sysinfo().uptime
end
--- LuCI system utilities / network related functions.
-- @class module
-- @name luci.sys.net
net = {}
--- Returns the current arp-table entries as two-dimensional table.
-- @return Table of table containing the current arp entries.
-- The following fields are defined for arp entry objects:
-- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
function net.arptable(callback)
local arp, e, r, v
if fs.access("/proc/net/arp") then
for e in io.lines("/proc/net/arp") do
local r = { }, v
for v in e:gmatch("%S+") do
r[#r+1] = v
end
if r[1] ~= "IP" then
local x = {
["IP address"] = r[1],
["HW type"] = r[2],
["Flags"] = r[3],
["HW address"] = r[4],
["Mask"] = r[5],
["Device"] = r[6]
}
if callback then
callback(x)
else
arp = arp or { }
arp[#arp+1] = x
end
end
end
end
return arp
end
local function _nethints(what, callback)
local _, k, e, mac, ip, name
local cur = uci.cursor()
local ifn = { }
local hosts = { }
local function _add(i, ...)
local k = select(i, ...)
if k then
if not hosts[k] then hosts[k] = { } end
hosts[k][1] = select(1, ...) or hosts[k][1]
hosts[k][2] = select(2, ...) or hosts[k][2]
hosts[k][3] = select(3, ...) or hosts[k][3]
hosts[k][4] = select(4, ...) or hosts[k][4]
end
end
if fs.access("/proc/net/arp") then
for e in io.lines("/proc/net/arp") do
ip, mac = e:match("^([%d%.]+)%s+%S+%s+%S+%s+([a-fA-F0-9:]+)%s+")
if ip and mac then
_add(what, mac:upper(), ip, nil, nil)
end
end
end
if fs.access("/etc/ethers") then
for e in io.lines("/etc/ethers") do
mac, ip = e:match("^([a-f0-9]%S+) (%S+)")
if mac and ip then
_add(what, mac:upper(), ip, nil, nil)
end
end
end
if fs.access("/var/dhcp.leases") then
for e in io.lines("/var/dhcp.leases") do
mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
if mac and ip then
_add(what, mac:upper(), ip, nil, name ~= "*" and name)
end
end
end
cur:foreach("dhcp", "host",
function(s)
for mac in luci.util.imatch(s.mac) do
_add(what, mac:upper(), s.ip, nil, s.name)
end
end)
for _, e in ipairs(nixio.getifaddrs()) do
if e.name ~= "lo" then
ifn[e.name] = ifn[e.name] or { }
if e.family == "packet" and e.addr and #e.addr == 17 then
ifn[e.name][1] = e.addr:upper()
elseif e.family == "inet" then
ifn[e.name][2] = e.addr
elseif e.family == "inet6" then
ifn[e.name][3] = e.addr
end
end
end
for _, e in pairs(ifn) do
if e[what] and (e[2] or e[3]) then
_add(what, e[1], e[2], e[3], e[4])
end
end
for _, e in luci.util.kspairs(hosts) do
callback(e[1], e[2], e[3], e[4])
end
end
--- Returns a two-dimensional table of mac address hints.
-- @return Table of table containing known hosts from various sources.
-- Each entry contains the values in the following order:
-- [ "mac", "name" ]
function net.mac_hints(callback)
if callback then
_nethints(1, function(mac, v4, v6, name)
name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
if name and name ~= mac then
callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4)
end
end)
else
local rv = { }
_nethints(1, function(mac, v4, v6, name)
name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
if name and name ~= mac then
rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 }
end
end)
return rv
end
end
--- Returns a two-dimensional table of IPv4 address hints.
-- @return Table of table containing known hosts from various sources.
-- Each entry contains the values in the following order:
-- [ "ip", "name" ]
function net.ipv4_hints(callback)
if callback then
_nethints(2, function(mac, v4, v6, name)
name = name or nixio.getnameinfo(v4, nil, 100) or mac
if name and name ~= v4 then
callback(v4, name)
end
end)
else
local rv = { }
_nethints(2, function(mac, v4, v6, name)
name = name or nixio.getnameinfo(v4, nil, 100) or mac
if name and name ~= v4 then
rv[#rv+1] = { v4, name }
end
end)
return rv
end
end
--- Returns a two-dimensional table of IPv6 address hints.
-- @return Table of table containing known hosts from various sources.
-- Each entry contains the values in the following order:
-- [ "ip", "name" ]
function net.ipv6_hints(callback)
if callback then
_nethints(3, function(mac, v4, v6, name)
name = name or nixio.getnameinfo(v6, nil, 100) or mac
if name and name ~= v6 then
callback(v6, name)
end
end)
else
local rv = { }
_nethints(3, function(mac, v4, v6, name)
name = name or nixio.getnameinfo(v6, nil, 100) or mac
if name and name ~= v6 then
rv[#rv+1] = { v6, name }
end
end)
return rv
end
end
--- Returns conntrack information
-- @return Table with the currently tracked IP connections
function net.conntrack(callback)
local connt = {}
if fs.access("/proc/net/nf_conntrack", "r") then
for line in io.lines("/proc/net/nf_conntrack") do
line = line:match "^(.-( [^ =]+=).-)%2"
local entry, flags = _parse_mixed_record(line, " +")
if flags[6] ~= "TIME_WAIT" then
entry.layer3 = flags[1]
entry.layer4 = flags[3]
for i=1, #entry do
entry[i] = nil
end
if callback then
callback(entry)
else
connt[#connt+1] = entry
end
end
end
elseif fs.access("/proc/net/ip_conntrack", "r") then
for line in io.lines("/proc/net/ip_conntrack") do
line = line:match "^(.-( [^ =]+=).-)%2"
local entry, flags = _parse_mixed_record(line, " +")
if flags[4] ~= "TIME_WAIT" then
entry.layer3 = "ipv4"
entry.layer4 = flags[1]
for i=1, #entry do
entry[i] = nil
end
if callback then
callback(entry)
else
connt[#connt+1] = entry
end
end
end
else
return nil
end
return connt
end
--- Determine the current IPv4 default route. If multiple default routes exist,
-- return the one with the lowest metric.
-- @return Table with the properties of the current default route.
-- The following fields are defined:
-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
-- "flags", "device" }
function net.defaultroute()
local route
net.routes(function(rt)
if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then
route = rt
end
end)
return route
end
--- Determine the current IPv6 default route. If multiple default routes exist,
-- return the one with the lowest metric.
-- @return Table with the properties of the current default route.
-- The following fields are defined:
-- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
-- "flags", "device" }
function net.defaultroute6()
local route
net.routes6(function(rt)
if rt.dest:prefix() == 0 and rt.device ~= "lo" and
(not route or route.metric > rt.metric)
then
route = rt
end
end)
if not route then
local global_unicast = luci.ip.IPv6("2000::/3")
net.routes6(function(rt)
if rt.dest:equal(global_unicast) and
(not route or route.metric > rt.metric)
then
route = rt
end
end)
end
return route
end
--- Determine the names of available network interfaces.
-- @return Table containing all current interface names
function net.devices()
local devs = {}
for k, v in ipairs(nixio.getifaddrs()) do
if v.family == "packet" then
devs[#devs+1] = v.name
end
end
return devs
end
--- Return information about available network interfaces.
-- @return Table containing all current interface names and their information
function net.deviceinfo()
local devs = {}
for k, v in ipairs(nixio.getifaddrs()) do
if v.family == "packet" then
local d = v.data
d[1] = d.rx_bytes
d[2] = d.rx_packets
d[3] = d.rx_errors
d[4] = d.rx_dropped
d[5] = 0
d[6] = 0
d[7] = 0
d[8] = d.multicast
d[9] = d.tx_bytes
d[10] = d.tx_packets
d[11] = d.tx_errors
d[12] = d.tx_dropped
d[13] = 0
d[14] = d.collisions
d[15] = 0
d[16] = 0
devs[v.name] = d
end
end
return devs
end
-- Determine the MAC address belonging to the given IP address.
-- @param ip IPv4 address
-- @return String containing the MAC address or nil if it cannot be found
function net.ip4mac(ip)
local mac = nil
net.arptable(function(e)
if e["IP address"] == ip then
mac = e["HW address"]
end
end)
return mac
end
--- Returns the current kernel routing table entries.
-- @return Table of tables with properties of the corresponding routes.
-- The following fields are defined for route entry tables:
-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
-- "flags", "device" }
function net.routes(callback)
local routes = { }
for line in io.lines("/proc/net/route") do
local dev, dst_ip, gateway, flags, refcnt, usecnt, metric,
dst_mask, mtu, win, irtt = line:match(
"([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" ..
"(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)"
)
if dev then
gateway = luci.ip.Hex( gateway, 32, luci.ip.FAMILY_INET4 )
dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 )
dst_ip = luci.ip.Hex(
dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
)
local rt = {
dest = dst_ip,
gateway = gateway,
metric = tonumber(metric),
refcount = tonumber(refcnt),
usecount = tonumber(usecnt),
mtu = tonumber(mtu),
window = tonumber(window),
irtt = tonumber(irtt),
flags = tonumber(flags, 16),
device = dev
}
if callback then
callback(rt)
else
routes[#routes+1] = rt
end
end
end
return routes
end
--- Returns the current ipv6 kernel routing table entries.
-- @return Table of tables with properties of the corresponding routes.
-- The following fields are defined for route entry tables:
-- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
-- "flags", "device" }
function net.routes6(callback)
if fs.access("/proc/net/ipv6_route", "r") then
local routes = { }
for line in io.lines("/proc/net/ipv6_route") do
local dst_ip, dst_prefix, src_ip, src_prefix, nexthop,
metric, refcnt, usecnt, flags, dev = line:match(
"([a-f0-9]+) ([a-f0-9]+) " ..
"([a-f0-9]+) ([a-f0-9]+) " ..
"([a-f0-9]+) ([a-f0-9]+) " ..
"([a-f0-9]+) ([a-f0-9]+) " ..
"([a-f0-9]+) +([^%s]+)"
)
if dst_ip and dst_prefix and
src_ip and src_prefix and
nexthop and metric and
refcnt and usecnt and
flags and dev
then
src_ip = luci.ip.Hex(
src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false
)
dst_ip = luci.ip.Hex(
dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false
)
nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
local rt = {
source = src_ip,
dest = dst_ip,
nexthop = nexthop,
metric = tonumber(metric, 16),
refcount = tonumber(refcnt, 16),
usecount = tonumber(usecnt, 16),
flags = tonumber(flags, 16),
device = dev,
-- lua number is too small for storing the metric
-- add a metric_raw field with the original content
metric_raw = metric
}
if callback then
callback(rt)
else
routes[#routes+1] = rt
end
end
end
return routes
end
end
--- Tests whether the given host responds to ping probes.
-- @param host String containing a hostname or IPv4 address
-- @return Number containing 0 on success and >= 1 on error
function net.pingtest(host)
return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
end
--- LuCI system utilities / process related functions.
-- @class module
-- @name luci.sys.process
process = {}
--- Get the current process id.
-- @class function
-- @name process.info
-- @return Number containing the current pid
function process.info(key)
local s = {uid = nixio.getuid(), gid = nixio.getgid()}
return not key and s or s[key]
end
--- Retrieve information about currently running processes.
-- @return Table containing process information
function process.list()
local data = {}
local k
local ps = luci.util.execi("/bin/busybox top -bn1")
if not ps then
return
end
for line in ps do
local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
)
local idx = tonumber(pid)
if idx then
data[idx] = {
['PID'] = pid,
['PPID'] = ppid,
['USER'] = user,
['STAT'] = stat,
['VSZ'] = vsz,
['%MEM'] = mem,
['%CPU'] = cpu,
['COMMAND'] = cmd
}
end
end
return data
end
--- Set the gid of a process identified by given pid.
-- @param gid Number containing the Unix group id
-- @return Boolean indicating successful operation
-- @return String containing the error message if failed
-- @return Number containing the error code if failed
function process.setgroup(gid)
return nixio.setgid(gid)
end
--- Set the uid of a process identified by given pid.
-- @param uid Number containing the Unix user id
-- @return Boolean indicating successful operation
-- @return String containing the error message if failed
-- @return Number containing the error code if failed
function process.setuser(uid)
return nixio.setuid(uid)
end
--- Send a signal to a process identified by given pid.
-- @class function
-- @name process.signal
-- @param pid Number containing the process id
-- @param sig Signal to send (default: 15 [SIGTERM])
-- @return Boolean indicating successful operation
-- @return Number containing the error code if failed
process.signal = nixio.kill
--- LuCI system utilities / user related functions.
-- @class module
-- @name luci.sys.user
user = {}
--- Retrieve user informations for given uid.
-- @class function
-- @name getuser
-- @param uid Number containing the Unix user id
-- @return Table containing the following fields:
-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
user.getuser = nixio.getpw
--- Retrieve the current user password hash.
-- @param username String containing the username to retrieve the password for
-- @return String containing the hash or nil if no password is set.
-- @return Password database entry
function user.getpasswd(username)
if username and username:lower()=="admin" then
username = "root"
end
local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username)
local pwh = pwe and (pwe.pwdp or pwe.passwd)
if not pwh or #pwh < 1 or pwh == "!" or pwh == "x" then
return nil, pwe
else
return pwh, pwe
end
end
--- Test whether given string matches the password of a given system user.
-- @param username String containing the Unix user name
-- @param pass String containing the password to compare
-- @return Boolean indicating wheather the passwords are equal
function user.checkpasswd(username, pass)
if username and username:lower()=="admin" then
username = "root"
end
local pwh, pwe = user.getpasswd(username)
if pwe then
return (pwh == nil or nixio.crypt(pass, pwh) == pwh)
end
return false
end
--- Change the password of given user.
-- @param username String containing the Unix user name
-- @param password String containing the password to compare
-- @return Number containing 0 on success and >= 1 on error
function user.setpasswd(username, password)
if username and username:lower()=="admin" then
username = "root"
end
if password then
password = password:gsub("'", [['"'"']])
end
if username then
username = username:gsub("'", [['"'"']])
end
return os.execute(
"(echo '" .. password .. "'; sleep 1; echo '" .. password .. "') | " ..
"passwd '" .. username .. "' >/dev/null 2>&1"
)
end
--- LuCI system utilities / wifi related functions.
-- @class module
-- @name luci.sys.wifi
wifi = {}
--- Get wireless information for given interface.
-- @param ifname String containing the interface name
-- @return A wrapped iwinfo object instance
function wifi.getiwinfo(ifname)
local stat, iwinfo = pcall(require, "iwinfo")
if ifname then
local c = 0
local u = uci.cursor_state()
local d, n = ifname:match("^(%w+)%.network(%d+)")
if d and n then
ifname = d
n = tonumber(n)
u:foreach("wireless", "wifi-iface",
function(s)
if s.device == d then
c = c + 1
if c == n then
ifname = s.ifname or s.device
return false
end
end
end)
elseif u:get("wireless", ifname) == "wifi-device" then
u:foreach("wireless", "wifi-iface",
function(s)
if s.device == ifname and s.ifname then
ifname = s.ifname
return false
end
end)
end
local t = stat and iwinfo.type(ifname)
local x = t and iwinfo[t] or { }
return setmetatable({}, {
__index = function(t, k)
if k == "ifname" then
return ifname
elseif x[k] then
return x[k](ifname)
end
end
})
end
end
--- LuCI system utilities / init related functions.
-- @class module
-- @name luci.sys.init
init = {}
init.dir = "/etc/init.d/"
--- Get the names of all installed init scripts
-- @return Table containing the names of all inistalled init scripts
function init.names()
local names = { }
for name in fs.glob(init.dir.."*") do
names[#names+1] = fs.basename(name)
end
return names
end
--- Get the index of he given init script
-- @param name Name of the init script
-- @return Numeric index value
function init.index(name)
if fs.access(init.dir..name) then
return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null"
%{ init.dir, name })
end
end
local function init_action(action, name)
if fs.access(init.dir..name) then
return call("env -i %s%s %s >/dev/null" %{ init.dir, name, action })
end
end
--- Test whether the given init script is enabled
-- @param name Name of the init script
-- @return Boolean indicating whether init is enabled
function init.enabled(name)
return (init_action("enabled", name) == 0)
end
--- Enable the given init script
-- @param name Name of the init script
-- @return Boolean indicating success
function init.enable(name)
return (init_action("enable", name) == 1)
end
--- Disable the given init script
-- @param name Name of the init script
-- @return Boolean indicating success
function init.disable(name)
return (init_action("disable", name) == 0)
end
--- Start the given init script
-- @param name Name of the init script
-- @return Boolean indicating success
function init.start(name)
return (init_action("start", name) == 0)
end
--- Stop the given init script
-- @param name Name of the init script
-- @return Boolean indicating success
function init.stop(name)
return (init_action("stop", name) == 0)
end
-- Internal functions
function _parse_mixed_record(cnt, delimiter)
delimiter = delimiter or " "
local data = {}
local flags = {}
for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
if k then
if x == "" then
table.insert(flags, k)
else
data[k] = v
end
end
end
end
return data, flags
end

View File

@ -0,0 +1,371 @@
--[[
Iptables parser and query library
(c) 2008-2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
(c) 2008-2009 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id: iptparser.lua 6826 2011-01-29 22:47:40Z jow $
]]--
local luci = {}
luci.util = require "luci.util"
luci.sys = require "luci.sys"
luci.ip = require "luci.ip"
local tonumber, ipairs, table = tonumber, ipairs, table
--- LuCI iptables parser and query library
-- @cstyle instance
module("luci.sys.iptparser")
--- Create a new iptables parser object.
-- @class function
-- @name IptParser
-- @param family Number specifying the address family. 4 for IPv4, 6 for IPv6
-- @return IptParser instance
IptParser = luci.util.class()
function IptParser.__init__( self, family )
self._family = (tonumber(family) == 6) and 6 or 4
self._rules = { }
self._chains = { }
if self._family == 4 then
self._nulladdr = "0.0.0.0/0"
self._tables = { "filter", "nat", "mangle", "raw" }
self._command = "iptables -t %s --line-numbers -nxvL"
else
self._nulladdr = "::/0"
self._tables = { "filter", "mangle", "raw" }
self._command = "ip6tables -t %s --line-numbers -nxvL"
end
self:_parse_rules()
end
--- Find all firewall rules that match the given criteria. Expects a table with
-- search criteria as only argument. If args is nil or an empty table then all
-- rules will be returned.
--
-- The following keys in the args table are recognized:
-- <ul>
-- <li> table - Match rules that are located within the given table
-- <li> chain - Match rules that are located within the given chain
-- <li> target - Match rules with the given target
-- <li> protocol - Match rules that match the given protocol, rules with
-- protocol "all" are always matched
-- <li> source - Match rules with the given source, rules with source
-- "0.0.0.0/0" (::/0) are always matched
-- <li> destination - Match rules with the given destination, rules with
-- destination "0.0.0.0/0" (::/0) are always matched
-- <li> inputif - Match rules with the given input interface, rules
-- with input interface "*" (=all) are always matched
-- <li> outputif - Match rules with the given output interface, rules
-- with output interface "*" (=all) are always matched
-- <li> flags - Match rules that match the given flags, current
-- supported values are "-f" (--fragment)
-- and "!f" (! --fragment)
-- <li> options - Match rules containing all given options
-- </ul>
-- The return value is a list of tables representing the matched rules.
-- Each rule table contains the following fields:
-- <ul>
-- <li> index - The index number of the rule
-- <li> table - The table where the rule is located, can be one
-- of "filter", "nat" or "mangle"
-- <li> chain - The chain where the rule is located, e.g. "INPUT"
-- or "postrouting_wan"
-- <li> target - The rule target, e.g. "REJECT" or "DROP"
-- <li> protocol The matching protocols, e.g. "all" or "tcp"
-- <li> flags - Special rule options ("--", "-f" or "!f")
-- <li> inputif - Input interface of the rule, e.g. "eth0.0"
-- or "*" for all interfaces
-- <li> outputif - Output interface of the rule,e.g. "eth0.0"
-- or "*" for all interfaces
-- <li> source - The source ip range, e.g. "0.0.0.0/0" (::/0)
-- <li> destination - The destination ip range, e.g. "0.0.0.0/0" (::/0)
-- <li> options - A list of specific options of the rule,
-- e.g. { "reject-with", "tcp-reset" }
-- <li> packets - The number of packets matched by the rule
-- <li> bytes - The number of total bytes matched by the rule
-- </ul>
-- Example:
-- <pre>
-- ip = luci.sys.iptparser.IptParser()
-- result = ip.find( {
-- target="REJECT",
-- protocol="tcp",
-- options={ "reject-with", "tcp-reset" }
-- } )
-- </pre>
-- This will match all rules with target "-j REJECT",
-- protocol "-p tcp" (or "-p all")
-- and the option "--reject-with tcp-reset".
-- @params args Table containing the search arguments (optional)
-- @return Table of matching rule tables
function IptParser.find( self, args )
local args = args or { }
local rv = { }
args.source = args.source and self:_parse_addr(args.source)
args.destination = args.destination and self:_parse_addr(args.destination)
for i, rule in ipairs(self._rules) do
local match = true
-- match table
if not ( not args.table or args.table:lower() == rule.table ) then
match = false
end
-- match chain
if not ( match == true and (
not args.chain or args.chain == rule.chain
) ) then
match = false
end
-- match target
if not ( match == true and (
not args.target or args.target == rule.target
) ) then
match = false
end
-- match protocol
if not ( match == true and (
not args.protocol or rule.protocol == "all" or
args.protocol:lower() == rule.protocol
) ) then
match = false
end
-- match source
if not ( match == true and (
not args.source or rule.source == self._nulladdr or
self:_parse_addr(rule.source):contains(args.source)
) ) then
match = false
end
-- match destination
if not ( match == true and (
not args.destination or rule.destination == self._nulladdr or
self:_parse_addr(rule.destination):contains(args.destination)
) ) then
match = false
end
-- match input interface
if not ( match == true and (
not args.inputif or rule.inputif == "*" or
args.inputif == rule.inputif
) ) then
match = false
end
-- match output interface
if not ( match == true and (
not args.outputif or rule.outputif == "*" or
args.outputif == rule.outputif
) ) then
match = false
end
-- match flags (the "opt" column)
if not ( match == true and (
not args.flags or rule.flags == args.flags
) ) then
match = false
end
-- match specific options
if not ( match == true and (
not args.options or
self:_match_options( rule.options, args.options )
) ) then
match = false
end
-- insert match
if match == true then
rv[#rv+1] = rule
end
end
return rv
end
--- Rebuild the internal lookup table, for example when rules have changed
-- through external commands.
-- @return nothing
function IptParser.resync( self )
self._rules = { }
self._chain = nil
self:_parse_rules()
end
--- Find the names of all tables.
-- @return Table of table names.
function IptParser.tables( self )
return self._tables
end
--- Find the names of all chains within the given table name.
-- @param table String containing the table name
-- @return Table of chain names in the order they occur.
function IptParser.chains( self, table )
local lookup = { }
local chains = { }
for _, r in ipairs(self:find({table=table})) do
if not lookup[r.chain] then
lookup[r.chain] = true
chains[#chains+1] = r.chain
end
end
return chains
end
--- Return the given firewall chain within the given table name.
-- @param table String containing the table name
-- @param chain String containing the chain name
-- @return Table containing the fields "policy", "packets", "bytes"
-- and "rules". The "rules" field is a table of rule tables.
function IptParser.chain( self, table, chain )
return self._chains[table:lower()] and self._chains[table:lower()][chain]
end
--- Test whether the given target points to a custom chain.
-- @param target String containing the target action
-- @return Boolean indicating whether target is a custom chain.
function IptParser.is_custom_target( self, target )
for _, r in ipairs(self._rules) do
if r.chain == target then
return true
end
end
return false
end
-- [internal] Parse address according to family.
function IptParser._parse_addr( self, addr )
if self._family == 4 then
return luci.ip.IPv4(addr)
else
return luci.ip.IPv6(addr)
end
end
-- [internal] Parse iptables output from all tables.
function IptParser._parse_rules( self )
for i, tbl in ipairs(self._tables) do
self._chains[tbl] = { }
for i, rule in ipairs(luci.util.execl(self._command % tbl)) do
if rule:find( "^Chain " ) == 1 then
local crefs
local cname, cpol, cpkt, cbytes = rule:match(
"^Chain ([^%s]*) %(policy (%w+) " ..
"(%d+) packets, (%d+) bytes%)"
)
if not cname then
cname, crefs = rule:match(
"^Chain ([^%s]*) %((%d+) references%)"
)
end
self._chain = cname
self._chains[tbl][cname] = {
policy = cpol,
packets = tonumber(cpkt or 0),
bytes = tonumber(cbytes or 0),
references = tonumber(crefs or 0),
rules = { }
}
else
if rule:find("%d") == 1 then
local rule_parts = luci.util.split( rule, "%s+", nil, true )
local rule_details = { }
-- cope with rules that have no target assigned
if rule:match("^%d+%s+%d+%s+%d+%s%s") then
table.insert(rule_parts, 4, nil)
end
-- ip6tables opt column is usually zero-width
if self._family == 6 then
table.insert(rule_parts, 6, "--")
end
rule_details["table"] = tbl
rule_details["chain"] = self._chain
rule_details["index"] = tonumber(rule_parts[1])
rule_details["packets"] = tonumber(rule_parts[2])
rule_details["bytes"] = tonumber(rule_parts[3])
rule_details["target"] = rule_parts[4]
rule_details["protocol"] = rule_parts[5]
rule_details["flags"] = rule_parts[6]
rule_details["inputif"] = rule_parts[7]
rule_details["outputif"] = rule_parts[8]
rule_details["source"] = rule_parts[9]
rule_details["destination"] = rule_parts[10]
rule_details["options"] = { }
for i = 11, #rule_parts - 1 do
rule_details["options"][i-10] = rule_parts[i]
end
self._rules[#self._rules+1] = rule_details
self._chains[tbl][self._chain].rules[
#self._chains[tbl][self._chain].rules + 1
] = rule_details
end
end
end
end
self._chain = nil
end
-- [internal] Return true if optlist1 contains all elements of optlist 2.
-- Return false in all other cases.
function IptParser._match_options( self, o1, o2 )
-- construct a hashtable of first options list to speed up lookups
local oh = { }
for i, opt in ipairs( o1 ) do oh[opt] = true end
-- iterate over second options list
-- each string in o2 must be also present in o1
-- if o2 contains a string which is not found in o1 then return false
for i, opt in ipairs( o2 ) do
if not oh[opt] then
return false
end
end
return true
end

View File

@ -0,0 +1,28 @@
--[[
LuCI - Autogenerated Zoneinfo Module
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local setmetatable, require, rawget, rawset = setmetatable, require, rawget, rawset
module "luci.sys.zoneinfo"
setmetatable(_M, {
__index = function(t, k)
if k == "TZ" and not rawget(t, k) then
local m = require "luci.sys.zoneinfo.tzdata"
rawset(t, k, rawget(m, k))
elseif k == "OFFSET" and not rawget(t, k) then
local m = require "luci.sys.zoneinfo.tzoffset"
rawset(t, k, rawget(m, k))
end
return rawget(t, k)
end
})

View File

@ -0,0 +1,420 @@
--[[
LuCI - Autogenerated Zoneinfo Module
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
module "luci.sys.zoneinfo.tzdata"
TZ = {
{ 'Africa/Abidjan', 'GMT0' },
{ 'Africa/Accra', 'GMT0' },
{ 'Africa/Addis Ababa', 'EAT-3' },
{ 'Africa/Algiers', 'CET-1' },
{ 'Africa/Asmara', 'EAT-3' },
{ 'Africa/Bamako', 'GMT0' },
{ 'Africa/Bangui', 'WAT-1' },
{ 'Africa/Banjul', 'GMT0' },
{ 'Africa/Bissau', 'GMT0' },
{ 'Africa/Blantyre', 'CAT-2' },
{ 'Africa/Brazzaville', 'WAT-1' },
{ 'Africa/Bujumbura', 'CAT-2' },
{ 'Africa/Casablanca', 'WET0' },
{ 'Africa/Ceuta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Africa/Conakry', 'GMT0' },
{ 'Africa/Dakar', 'GMT0' },
{ 'Africa/Dar es Salaam', 'EAT-3' },
{ 'Africa/Djibouti', 'EAT-3' },
{ 'Africa/Douala', 'WAT-1' },
{ 'Africa/El Aaiun', 'WET0' },
{ 'Africa/Freetown', 'GMT0' },
{ 'Africa/Gaborone', 'CAT-2' },
{ 'Africa/Harare', 'CAT-2' },
{ 'Africa/Johannesburg', 'SAST-2' },
{ 'Africa/Juba', 'EAT-3' },
{ 'Africa/Kampala', 'EAT-3' },
{ 'Africa/Khartoum', 'EAT-3' },
{ 'Africa/Kigali', 'CAT-2' },
{ 'Africa/Kinshasa', 'WAT-1' },
{ 'Africa/Lagos', 'WAT-1' },
{ 'Africa/Libreville', 'WAT-1' },
{ 'Africa/Lome', 'GMT0' },
{ 'Africa/Luanda', 'WAT-1' },
{ 'Africa/Lubumbashi', 'CAT-2' },
{ 'Africa/Lusaka', 'CAT-2' },
{ 'Africa/Malabo', 'WAT-1' },
{ 'Africa/Maputo', 'CAT-2' },
{ 'Africa/Maseru', 'SAST-2' },
{ 'Africa/Mbabane', 'SAST-2' },
{ 'Africa/Mogadishu', 'EAT-3' },
{ 'Africa/Monrovia', 'GMT0' },
{ 'Africa/Nairobi', 'EAT-3' },
{ 'Africa/Ndjamena', 'WAT-1' },
{ 'Africa/Niamey', 'WAT-1' },
{ 'Africa/Nouakchott', 'GMT0' },
{ 'Africa/Ouagadougou', 'GMT0' },
{ 'Africa/Porto-Novo', 'WAT-1' },
{ 'Africa/Sao Tome', 'GMT0' },
{ 'Africa/Tripoli', 'EET-2' },
{ 'Africa/Tunis', 'CET-1' },
{ 'Africa/Windhoek', 'WAT-1WAST,M9.1.0,M4.1.0' },
{ 'America/Adak', 'HAST10HADT,M3.2.0,M11.1.0' },
{ 'America/Anchorage', 'AKST9AKDT,M3.2.0,M11.1.0' },
{ 'America/Anguilla', 'AST4' },
{ 'America/Antigua', 'AST4' },
{ 'America/Araguaina', 'BRT3' },
{ 'America/Argentina/Buenos Aires', 'ART3' },
{ 'America/Argentina/Catamarca', 'ART3' },
{ 'America/Argentina/Cordoba', 'ART3' },
{ 'America/Argentina/Jujuy', 'ART3' },
{ 'America/Argentina/La Rioja', 'ART3' },
{ 'America/Argentina/Mendoza', 'ART3' },
{ 'America/Argentina/Rio Gallegos', 'ART3' },
{ 'America/Argentina/Salta', 'ART3' },
{ 'America/Argentina/San Juan', 'ART3' },
{ 'America/Argentina/Tucuman', 'ART3' },
{ 'America/Argentina/Ushuaia', 'ART3' },
{ 'America/Aruba', 'AST4' },
{ 'America/Asuncion', 'PYT4PYST,M10.1.0/0,M4.2.0/0' },
{ 'America/Atikokan', 'EST5' },
{ 'America/Bahia', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
{ 'America/Bahia Banderas', 'CST6CDT,M4.1.0,M10.5.0' },
{ 'America/Barbados', 'AST4' },
{ 'America/Belem', 'BRT3' },
{ 'America/Belize', 'CST6' },
{ 'America/Blanc-Sablon', 'AST4' },
{ 'America/Boa Vista', 'AMT4' },
{ 'America/Bogota', 'COT5' },
{ 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
{ 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' },
{ 'America/Caracas', 'VET4:30' },
{ 'America/Cayenne', 'GFT3' },
{ 'America/Cayman', 'EST5' },
{ 'America/Chicago', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Chihuahua', 'MST7MDT,M4.1.0,M10.5.0' },
{ 'America/Costa Rica', 'CST6' },
{ 'America/Cuiaba', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
{ 'America/Curacao', 'AST4' },
{ 'America/Danmarkshavn', 'GMT0' },
{ 'America/Dawson', 'PST8PDT,M3.2.0,M11.1.0' },
{ 'America/Dawson Creek', 'MST7' },
{ 'America/Denver', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Detroit', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Dominica', 'AST4' },
{ 'America/Edmonton', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Eirunepe', 'AMT4' },
{ 'America/El Salvador', 'CST6' },
{ 'America/Fortaleza', 'BRT3' },
{ 'America/Glace Bay', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'America/Goose Bay', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'America/Grand Turk', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Grenada', 'AST4' },
{ 'America/Guadeloupe', 'AST4' },
{ 'America/Guatemala', 'CST6' },
{ 'America/Guayaquil', 'ECT5' },
{ 'America/Guyana', 'GYT4' },
{ 'America/Halifax', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'America/Havana', 'CST5CDT,M3.2.0/0,M10.5.0/1' },
{ 'America/Hermosillo', 'MST7' },
{ 'America/Indiana/Indianapolis', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Knox', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Marengo', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Petersburg', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Tell City', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Vevay', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Vincennes', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Indiana/Winamac', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Inuvik', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Iqaluit', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Jamaica', 'EST5' },
{ 'America/Juneau', 'AKST9AKDT,M3.2.0,M11.1.0' },
{ 'America/Kentucky/Louisville', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Kentucky/Monticello', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Kralendijk', 'AST4' },
{ 'America/La Paz', 'BOT4' },
{ 'America/Lima', 'PET5' },
{ 'America/Los Angeles', 'PST8PDT,M3.2.0,M11.1.0' },
{ 'America/Lower Princes', 'AST4' },
{ 'America/Maceio', 'BRT3' },
{ 'America/Managua', 'CST6' },
{ 'America/Manaus', 'AMT4' },
{ 'America/Marigot', 'AST4' },
{ 'America/Martinique', 'AST4' },
{ 'America/Matamoros', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Mazatlan', 'MST7MDT,M4.1.0,M10.5.0' },
{ 'America/Menominee', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Merida', 'CST6CDT,M4.1.0,M10.5.0' },
{ 'America/Metlakatla', 'MeST8' },
{ 'America/Mexico City', 'CST6CDT,M4.1.0,M10.5.0' },
{ 'America/Miquelon', 'PMST3PMDT,M3.2.0,M11.1.0' },
{ 'America/Moncton', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'America/Monterrey', 'CST6CDT,M4.1.0,M10.5.0' },
{ 'America/Montevideo', 'UYT3UYST,M10.1.0,M3.2.0' },
{ 'America/Montreal', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Montserrat', 'AST4' },
{ 'America/Nassau', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/New York', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Nipigon', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Nome', 'AKST9AKDT,M3.2.0,M11.1.0' },
{ 'America/Noronha', 'FNT2' },
{ 'America/North Dakota/Beulah', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/North Dakota/Center', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/North Dakota/New Salem', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Ojinaga', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Panama', 'EST5' },
{ 'America/Pangnirtung', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Paramaribo', 'SRT3' },
{ 'America/Phoenix', 'MST7' },
{ 'America/Port of Spain', 'AST4' },
{ 'America/Port-au-Prince', 'EST5' },
{ 'America/Porto Velho', 'AMT4' },
{ 'America/Puerto Rico', 'AST4' },
{ 'America/Rainy River', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Rankin Inlet', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Recife', 'BRT3' },
{ 'America/Regina', 'CST6' },
{ 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Rio Branco', 'AMT4' },
{ 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' },
{ 'America/Santarem', 'BRT3' },
{ 'America/Santo Domingo', 'AST4' },
{ 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
{ 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' },
{ 'America/Shiprock', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'America/Sitka', 'AKST9AKDT,M3.2.0,M11.1.0' },
{ 'America/St Barthelemy', 'AST4' },
{ 'America/St Johns', 'NST3:30NDT,M3.2.0,M11.1.0' },
{ 'America/St Kitts', 'AST4' },
{ 'America/St Lucia', 'AST4' },
{ 'America/St Thomas', 'AST4' },
{ 'America/St Vincent', 'AST4' },
{ 'America/Swift Current', 'CST6' },
{ 'America/Tegucigalpa', 'CST6' },
{ 'America/Thule', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'America/Thunder Bay', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Tijuana', 'PST8PDT,M3.2.0,M11.1.0' },
{ 'America/Toronto', 'EST5EDT,M3.2.0,M11.1.0' },
{ 'America/Tortola', 'AST4' },
{ 'America/Vancouver', 'PST8PDT,M3.2.0,M11.1.0' },
{ 'America/Whitehorse', 'PST8PDT,M3.2.0,M11.1.0' },
{ 'America/Winnipeg', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Yakutat', 'AKST9AKDT,M3.2.0,M11.1.0' },
{ 'America/Yellowknife', 'MST7MDT,M3.2.0,M11.1.0' },
{ 'Antarctica/Casey', 'WST-8' },
{ 'Antarctica/Davis', 'DAVT-7' },
{ 'Antarctica/DumontDUrville', 'DDUT-10' },
{ 'Antarctica/Macquarie', 'MIST-11' },
{ 'Antarctica/Mawson', 'MAWT-5' },
{ 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
{ 'Antarctica/Rothera', 'ROTT3' },
{ 'Antarctica/South Pole', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
{ 'Antarctica/Syowa', 'SYOT-3' },
{ 'Antarctica/Vostok', 'VOST-6' },
{ 'Arctic/Longyearbyen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Asia/Aden', 'AST-3' },
{ 'Asia/Almaty', 'ALMT-6' },
{ 'Asia/Anadyr', 'ANAT-12' },
{ 'Asia/Aqtau', 'AQTT-5' },
{ 'Asia/Aqtobe', 'AQTT-5' },
{ 'Asia/Ashgabat', 'TMT-5' },
{ 'Asia/Baghdad', 'AST-3' },
{ 'Asia/Bahrain', 'AST-3' },
{ 'Asia/Baku', 'AZT-4AZST,M3.5.0/4,M10.5.0/5' },
{ 'Asia/Bangkok', 'ICT-7' },
{ 'Asia/Beirut', 'EET-2EEST,M3.5.0/0,M10.5.0/0' },
{ 'Asia/Bishkek', 'KGT-6' },
{ 'Asia/Brunei', 'BNT-8' },
{ 'Asia/Choibalsan', 'CHOT-8' },
{ 'Asia/Chongqing', 'CST-8' },
{ 'Asia/Colombo', 'IST-5:30' },
{ 'Asia/Damascus', 'EET-2EEST,M4.1.5/0,M10.5.5/0' },
{ 'Asia/Dhaka', 'BDT-6' },
{ 'Asia/Dili', 'TLT-9' },
{ 'Asia/Dubai', 'GST-4' },
{ 'Asia/Dushanbe', 'TJT-5' },
{ 'Asia/Gaza', 'EET-2' },
{ 'Asia/Harbin', 'CST-8' },
{ 'Asia/Hebron', 'EET-2' },
{ 'Asia/Ho Chi Minh', 'ICT-7' },
{ 'Asia/Hong Kong', 'HKT-8' },
{ 'Asia/Hovd', 'HOVT-7' },
{ 'Asia/Irkutsk', 'IRKT-9' },
{ 'Asia/Jakarta', 'WIT-7' },
{ 'Asia/Jayapura', 'EIT-9' },
{ 'Asia/Kabul', 'AFT-4:30' },
{ 'Asia/Kamchatka', 'PETT-12' },
{ 'Asia/Karachi', 'PKT-5' },
{ 'Asia/Kashgar', 'CST-8' },
{ 'Asia/Kathmandu', 'NPT-5:45' },
{ 'Asia/Kolkata', 'IST-5:30' },
{ 'Asia/Krasnoyarsk', 'KRAT-8' },
{ 'Asia/Kuala Lumpur', 'MYT-8' },
{ 'Asia/Kuching', 'MYT-8' },
{ 'Asia/Kuwait', 'AST-3' },
{ 'Asia/Macau', 'CST-8' },
{ 'Asia/Magadan', 'MAGT-12' },
{ 'Asia/Makassar', 'CIT-8' },
{ 'Asia/Manila', 'PHT-8' },
{ 'Asia/Muscat', 'GST-4' },
{ 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Asia/Novokuznetsk', 'NOVT-7' },
{ 'Asia/Novosibirsk', 'NOVT-7' },
{ 'Asia/Omsk', 'OMST-7' },
{ 'Asia/Oral', 'ORAT-5' },
{ 'Asia/Phnom Penh', 'ICT-7' },
{ 'Asia/Pontianak', 'WIT-7' },
{ 'Asia/Pyongyang', 'KST-9' },
{ 'Asia/Qatar', 'AST-3' },
{ 'Asia/Qyzylorda', 'QYZT-6' },
{ 'Asia/Rangoon', 'MMT-6:30' },
{ 'Asia/Riyadh', 'AST-3' },
{ 'Asia/Sakhalin', 'SAKT-11' },
{ 'Asia/Samarkand', 'UZT-5' },
{ 'Asia/Seoul', 'KST-9' },
{ 'Asia/Shanghai', 'CST-8' },
{ 'Asia/Singapore', 'SGT-8' },
{ 'Asia/Taipei', 'CST-8' },
{ 'Asia/Tashkent', 'UZT-5' },
{ 'Asia/Tbilisi', 'GET-4' },
{ 'Asia/Thimphu', 'BTT-6' },
{ 'Asia/Tokyo', 'JST-9' },
{ 'Asia/Ulaanbaatar', 'ULAT-8' },
{ 'Asia/Urumqi', 'CST-8' },
{ 'Asia/Vientiane', 'ICT-7' },
{ 'Asia/Vladivostok', 'VLAT-11' },
{ 'Asia/Yakutsk', 'YAKT-10' },
{ 'Asia/Yekaterinburg', 'YEKT-6' },
{ 'Asia/Yerevan', 'AMT-4AMST,M3.5.0,M10.5.0/3' },
{ 'Atlantic/Azores', 'AZOT1AZOST,M3.5.0/0,M10.5.0/1' },
{ 'Atlantic/Bermuda', 'AST4ADT,M3.2.0,M11.1.0' },
{ 'Atlantic/Canary', 'WET0WEST,M3.5.0/1,M10.5.0' },
{ 'Atlantic/Cape Verde', 'CVT1' },
{ 'Atlantic/Faroe', 'WET0WEST,M3.5.0/1,M10.5.0' },
{ 'Atlantic/Madeira', 'WET0WEST,M3.5.0/1,M10.5.0' },
{ 'Atlantic/Reykjavik', 'GMT0' },
{ 'Atlantic/South Georgia', 'GST2' },
{ 'Atlantic/St Helena', 'GMT0' },
{ 'Atlantic/Stanley', 'FKT4FKST,M9.1.0,M4.3.0' },
{ 'Australia/Adelaide', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
{ 'Australia/Brisbane', 'EST-10' },
{ 'Australia/Broken Hill', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
{ 'Australia/Currie', 'EST-10EST,M10.1.0,M4.1.0/3' },
{ 'Australia/Darwin', 'CST-9:30' },
{ 'Australia/Eucla', 'CWST-8:45' },
{ 'Australia/Hobart', 'EST-10EST,M10.1.0,M4.1.0/3' },
{ 'Australia/Lindeman', 'EST-10' },
{ 'Australia/Lord Howe', 'LHST-10:30LHST-11,M10.1.0,M4.1.0' },
{ 'Australia/Melbourne', 'EST-10EST,M10.1.0,M4.1.0/3' },
{ 'Australia/Perth', 'WST-8' },
{ 'Australia/Sydney', 'EST-10EST,M10.1.0,M4.1.0/3' },
{ 'Europe/Amsterdam', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Andorra', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Athens', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Belgrade', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Berlin', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Bratislava', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Brussels', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Bucharest', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Budapest', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Chisinau', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Dublin', 'GMT0IST,M3.5.0/1,M10.5.0' },
{ 'Europe/Gibraltar', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Guernsey', 'GMT0BST,M3.5.0/1,M10.5.0' },
{ 'Europe/Helsinki', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Isle of Man', 'GMT0BST,M3.5.0/1,M10.5.0' },
{ 'Europe/Istanbul', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Jersey', 'GMT0BST,M3.5.0/1,M10.5.0' },
{ 'Europe/Kaliningrad', 'FET-3' },
{ 'Europe/Kiev', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Lisbon', 'WET0WEST,M3.5.0/1,M10.5.0' },
{ 'Europe/Ljubljana', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/London', 'GMT0BST,M3.5.0/1,M10.5.0' },
{ 'Europe/Luxembourg', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Madrid', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Malta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Mariehamn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Minsk', 'FET-3' },
{ 'Europe/Monaco', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Moscow', 'MSK-4' },
{ 'Europe/Oslo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Paris', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Podgorica', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Prague', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Riga', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Rome', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Samara', 'SAMT-4' },
{ 'Europe/San Marino', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Sarajevo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Simferopol', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Skopje', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Sofia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Stockholm', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Tallinn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Tirane', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Uzhgorod', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Vaduz', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Vatican', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Vienna', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Vilnius', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Volgograd', 'VOLT-4' },
{ 'Europe/Warsaw', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Zagreb', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Zaporozhye', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Zurich', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Indian/Antananarivo', 'EAT-3' },
{ 'Indian/Chagos', 'IOT-6' },
{ 'Indian/Christmas', 'CXT-7' },
{ 'Indian/Cocos', 'CCT-6:30' },
{ 'Indian/Comoro', 'EAT-3' },
{ 'Indian/Kerguelen', 'TFT-5' },
{ 'Indian/Mahe', 'SCT-4' },
{ 'Indian/Maldives', 'MVT-5' },
{ 'Indian/Mauritius', 'MUT-4' },
{ 'Indian/Mayotte', 'EAT-3' },
{ 'Indian/Reunion', 'RET-4' },
{ 'Pacific/Apia', 'WST-13' },
{ 'Pacific/Auckland', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
{ 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' },
{ 'Pacific/Chuuk', 'CHUT-10' },
{ 'Pacific/Efate', 'VUT-11' },
{ 'Pacific/Enderbury', 'PHOT-13' },
{ 'Pacific/Fakaofo', 'TKT10' },
{ 'Pacific/Fiji', 'FJT-12' },
{ 'Pacific/Funafuti', 'TVT-12' },
{ 'Pacific/Galapagos', 'GALT6' },
{ 'Pacific/Gambier', 'GAMT9' },
{ 'Pacific/Guadalcanal', 'SBT-11' },
{ 'Pacific/Guam', 'ChST-10' },
{ 'Pacific/Honolulu', 'HST10' },
{ 'Pacific/Johnston', 'HST10' },
{ 'Pacific/Kiritimati', 'LINT-14' },
{ 'Pacific/Kosrae', 'KOST-11' },
{ 'Pacific/Kwajalein', 'MHT-12' },
{ 'Pacific/Majuro', 'MHT-12' },
{ 'Pacific/Marquesas', 'MART9:30' },
{ 'Pacific/Midway', 'SST11' },
{ 'Pacific/Nauru', 'NRT-12' },
{ 'Pacific/Niue', 'NUT11' },
{ 'Pacific/Norfolk', 'NFT-11:30' },
{ 'Pacific/Noumea', 'NCT-11' },
{ 'Pacific/Pago Pago', 'SST11' },
{ 'Pacific/Palau', 'PWT-9' },
{ 'Pacific/Pitcairn', 'PST8' },
{ 'Pacific/Pohnpei', 'PONT-11' },
{ 'Pacific/Port Moresby', 'PGT-10' },
{ 'Pacific/Rarotonga', 'CKT10' },
{ 'Pacific/Saipan', 'ChST-10' },
{ 'Pacific/Tahiti', 'TAHT10' },
{ 'Pacific/Tarawa', 'GILT-12' },
{ 'Pacific/Tongatapu', 'TOT-13' },
{ 'Pacific/Wake', 'WAKT-12' },
{ 'Pacific/Wallis', 'WFT-12' },
}

View File

@ -0,0 +1,162 @@
--[[
LuCI - Autogenerated Zoneinfo Module
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
module "luci.sys.zoneinfo.tzoffset"
OFFSET = {
gmt = 0, -- GMT
eat = 10800, -- EAT
cet = 3600, -- CET
wat = 3600, -- WAT
cat = 7200, -- CAT
wet = 0, -- WET
sast = 7200, -- SAST
eet = 7200, -- EET
hast = -36000, -- HAST
hadt = -32400, -- HADT
akst = -32400, -- AKST
akdt = -28800, -- AKDT
ast = -14400, -- AST
brt = -10800, -- BRT
art = -10800, -- ART
pyt = -14400, -- PYT
pyst = -10800, -- PYST
est = -18000, -- EST
cst = -21600, -- CST
cdt = -18000, -- CDT
amt = -14400, -- AMT
cot = -18000, -- COT
mst = -25200, -- MST
mdt = -21600, -- MDT
vet = -16200, -- VET
gft = -10800, -- GFT
pst = -28800, -- PST
pdt = -25200, -- PDT
ect = -18000, -- ECT
gyt = -14400, -- GYT
bot = -14400, -- BOT
pet = -18000, -- PET
pmst = -10800, -- PMST
pmdt = -7200, -- PMDT
uyt = -10800, -- UYT
uyst = -7200, -- UYST
fnt = -7200, -- FNT
srt = -10800, -- SRT
egt = -3600, -- EGT
egst = 0, -- EGST
nst = -12600, -- NST
ndt = -9000, -- NDT
wst = 28800, -- WST
davt = 25200, -- DAVT
ddut = 36000, -- DDUT
mist = 39600, -- MIST
mawt = 18000, -- MAWT
nzst = 43200, -- NZST
nzdt = 46800, -- NZDT
rott = -10800, -- ROTT
syot = 10800, -- SYOT
vost = 21600, -- VOST
almt = 21600, -- ALMT
anat = 43200, -- ANAT
aqtt = 18000, -- AQTT
tmt = 18000, -- TMT
azt = 14400, -- AZT
azst = 18000, -- AZST
ict = 25200, -- ICT
kgt = 21600, -- KGT
bnt = 28800, -- BNT
chot = 28800, -- CHOT
ist = 19800, -- IST
bdt = 21600, -- BDT
tlt = 32400, -- TLT
gst = 14400, -- GST
tjt = 18000, -- TJT
hkt = 28800, -- HKT
hovt = 25200, -- HOVT
irkt = 32400, -- IRKT
wit = 25200, -- WIT
eit = 32400, -- EIT
aft = 16200, -- AFT
pett = 43200, -- PETT
pkt = 18000, -- PKT
npt = 20700, -- NPT
krat = 28800, -- KRAT
myt = 28800, -- MYT
magt = 43200, -- MAGT
cit = 28800, -- CIT
pht = 28800, -- PHT
novt = 25200, -- NOVT
omst = 25200, -- OMST
orat = 18000, -- ORAT
kst = 32400, -- KST
qyzt = 21600, -- QYZT
mmt = 23400, -- MMT
sakt = 39600, -- SAKT
uzt = 18000, -- UZT
sgt = 28800, -- SGT
get = 14400, -- GET
btt = 21600, -- BTT
jst = 32400, -- JST
ulat = 28800, -- ULAT
vlat = 39600, -- VLAT
yakt = 36000, -- YAKT
yekt = 21600, -- YEKT
azot = -3600, -- AZOT
azost = 0, -- AZOST
cvt = -3600, -- CVT
fkt = -14400, -- FKT
fkst = -10800, -- FKST
cwst = 31500, -- CWST
lhst = 37800, -- LHST
lhst = 39600, -- LHST
fet = 10800, -- FET
msk = 14400, -- MSK
samt = 14400, -- SAMT
volt = 14400, -- VOLT
iot = 21600, -- IOT
cxt = 25200, -- CXT
cct = 23400, -- CCT
tft = 18000, -- TFT
sct = 14400, -- SCT
mvt = 18000, -- MVT
mut = 14400, -- MUT
ret = 14400, -- RET
chast = 45900, -- CHAST
chadt = 49500, -- CHADT
chut = 36000, -- CHUT
vut = 39600, -- VUT
phot = 46800, -- PHOT
tkt = -36000, -- TKT
fjt = 43200, -- FJT
tvt = 43200, -- TVT
galt = -21600, -- GALT
gamt = -32400, -- GAMT
sbt = 39600, -- SBT
hst = -36000, -- HST
lint = 50400, -- LINT
kost = 39600, -- KOST
mht = 43200, -- MHT
mart = -34200, -- MART
sst = -39600, -- SST
nrt = 43200, -- NRT
nut = -39600, -- NUT
nft = 41400, -- NFT
nct = 39600, -- NCT
pwt = 32400, -- PWT
pont = 39600, -- PONT
pgt = 36000, -- PGT
ckt = -36000, -- CKT
taht = -36000, -- TAHT
gilt = 43200, -- GILT
tot = 46800, -- TOT
wakt = 43200, -- WAKT
wft = 43200, -- WFT
}

View File

@ -0,0 +1,107 @@
--[[
LuCI - Template Parser
Description:
A template parser supporting includes, translations, Lua code blocks
and more. It can be used either as a compiler or as an interpreter.
FileId: $Id: template.lua 9558 2012-12-18 13:58:22Z jow $
License:
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
local util = require "luci.util"
local config = require "luci.config"
local tparser = require "luci.template.parser"
local tostring, pairs, loadstring = tostring, pairs, loadstring
local setmetatable, loadfile = setmetatable, loadfile
local getfenv, setfenv, rawget = getfenv, setfenv, rawget
local assert, type, error = assert, type, error
--- LuCI template library.
module "luci.template"
config.template = config.template or {}
viewdir = config.template.viewdir or util.libpath() .. "/view"
-- Define the namespace for template modules
context = util.threadlocal()
--- Render a certain template.
-- @param name Template name
-- @param scope Scope to assign to template (optional)
function render(name, scope)
return Template(name):render(scope or getfenv(2))
end
-- Template class
Template = util.class()
-- Shared template cache to store templates in to avoid unnecessary reloading
Template.cache = setmetatable({}, {__mode = "v"})
-- Constructor - Reads and compiles the template on-demand
function Template.__init__(self, name)
self.template = self.cache[name]
self.name = name
-- Create a new namespace for this template
self.viewns = context.viewns
-- If we have a cached template, skip compiling and loading
if not self.template then
-- Compile template
local err
local sourcefile = viewdir .. "/" .. name .. ".htm"
self.template, _, err = tparser.parse(sourcefile)
-- If we have no valid template throw error, otherwise cache the template
if not self.template then
error("Failed to load template '" .. name .. "'.\n" ..
"Error while parsing template '" .. sourcefile .. "':\n" ..
(err or "Unknown syntax error"))
else
self.cache[name] = self.template
end
end
end
-- Renders a template
function Template.render(self, scope)
scope = scope or getfenv(2)
-- Put our predefined objects in the scope of the template
setfenv(self.template, setmetatable({}, {__index =
function(tbl, key)
return rawget(tbl, key) or self.viewns[key] or scope[key]
end}))
-- Now finally render the thing
local stat, err = util.copcall(self.template)
if not stat then
error("Failed to execute template '" .. self.name .. "'.\n" ..
"A runtime error occured: " .. tostring(err or "(nil)"))
end
end

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More