Fixed several issues and updated backend to escape all shell commands to avoid code...
authorMartin Schröder <mkschreder.uk@gmail.com>
Mon, 17 Aug 2015 11:22:21 +0000 (13:22 +0200)
committerMartin Schröder <mkschreder.uk@gmail.com>
Mon, 17 Aug 2015 11:22:21 +0000 (13:22 +0200)
22 files changed:
Makefile
backend/juci-core/Makefile
backend/juci-core/lua/JSON.lua [deleted file]
backend/juci-core/plugin/juci.c
juci/Makefile
juci/lualibs/JSON.lua [new file with mode: 0644]
juci/lualibs/JUCI.lua [new file with mode: 0644]
juci/src/js/rpc.js
plugins/juci-broadcom-dsl/backend/juci.broadcom.dsl
plugins/juci-diagnostics/access.json
plugins/juci-diagnostics/backend/juci.diagnostics
plugins/juci-macdb/backend/juci.macdb
plugins/juci-mod-status/src/pages/status.diagnostics.html [deleted file]
plugins/juci-mod-status/src/pages/status.diagnostics.js [deleted file]
plugins/juci-mod-status/src/pages/status.events.html
plugins/juci-mod-status/src/pages/status.events.js
plugins/juci-mod-system/backend/juci.system
plugins/juci-mod-system/backend/juci.system.conf
plugins/juci-mod-system/backend/juci.system.process
plugins/juci-mod-system/backend/juci.system.user
plugins/juci-network-netifd/src/widgets/overview.net.js
plugins/juci-sysupgrade/backend/juci.system.upgrade

index 5f3ad55..b09c74c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -72,12 +72,6 @@ all: prepare node_modules $(UBUS_MODS) $(DIRS-y)
        -chmod +x $(BIN)/usr/lib/rpcd/cgi/*
        ./juci-compile 
        ./juci-update $(BIN)/www RELEASE
-       #closure-compiler --warning_level QUIET --language_in ECMASCRIPT5 --compilation_level ADVANCED_OPTIMIZATIONS --js htdocs/__all.js --js_output_file htdocs/__compiled.js
-       #yui-compressor htdocs/__all.css > htdocs/__compiled.css
-       #mv htdocs/__compiled.css htdocs/__all.css
-       #mv htdocs/__compiled.js htdocs/__all.js
-       #rm -rf htdocs/js
-       #rm -rf htdocs/css
 
 prepare:       
        @echo "======= JUCI Buliding ========="
index ed7a7cb..8ae8206 100644 (file)
@@ -24,8 +24,6 @@ $(SHARED_OBJECT): $(SO_OBJECT_FILES)
        $(INSTALL_DIR) $(BUILD_DIR)/usr/share/rpcd/acl.d/
        $(CP) access.json $(ACL_LIST)
        $(CP) etc $(BUILD_DIR)/
-       $(INSTALL_DIR) $(BUILD_DIR)/usr/share/lua/
-       $(CP) lua/* $(BUILD_DIR)/usr/share/lua/
        
 $(CGI_SCRIPT): $(IO_OBJECT_FILES)
        $(INSTALL_DIR) $(BUILD_DIR)/usr/libexec/rpcd/
diff --git a/backend/juci-core/lua/JSON.lua b/backend/juci-core/lua/JSON.lua
deleted file mode 100644 (file)
index e0b3d17..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
------------------------------------------------------------------------------
--- JSON4Lua: JSON encoding / decoding support for the Lua language.
--- json Module.
--- Author: Craig Mason-Jones
--- Homepage: http://json.luaforge.net/
--- Version: 0.9.40
--- This module is released under the MIT License (MIT).
--- Please see LICENCE.txt for details.
---
--- USAGE:
--- This module exposes two functions:
---   encode(o)
---     Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string.
---   decode(json_string)
---     Returns a Lua object populated with the data encoded in the JSON string json_string.
---
--- REQUIREMENTS:
---   compat-5.1 if using Lua 5.0
---
--- CHANGELOG
---   0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). 
---          Fixed Lua 5.1 compatibility issues.
---             Introduced json.null to have null values in associative arrays.
---          encode() performance improvement (more than 50%) through table.concat rather than ..
---          Introduced decode ability to ignore /**/ comments in the JSON string.
---   0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays.
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Imports and dependencies
------------------------------------------------------------------------------
-local math = require('math')
-local string = require("string")
-local table = require("table")
-
-local base = _G
-
------------------------------------------------------------------------------
--- Module declaration
------------------------------------------------------------------------------
-module("json")
-
--- Public functions
-
--- Private functions
-local decode_scanArray
-local decode_scanComment
-local decode_scanConstant
-local decode_scanNumber
-local decode_scanObject
-local decode_scanString
-local decode_scanWhitespace
-local encodeString
-local isArray
-local isEncodable
-
------------------------------------------------------------------------------
--- PUBLIC FUNCTIONS
------------------------------------------------------------------------------
---- Encodes an arbitrary Lua object / variable.
--- @param v The Lua object / variable to be JSON encoded.
--- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode)
-function encode (v)
-  -- Handle nil values
-  if v==nil then
-    return "null"
-  end
-  
-  local vtype = base.type(v)  
-
-  -- Handle strings
-  if vtype=='string' then    
-    return '"' .. encodeString(v) .. '"'           -- Need to handle encoding in string
-  end
-  
-  -- Handle booleans
-  if vtype=='number' or vtype=='boolean' then
-    return base.tostring(v)
-  end
-  
-  -- Handle tables
-  if vtype=='table' then
-    local rval = {}
-    -- Consider arrays separately
-    local bArray, maxCount = isArray(v)
-    if bArray then
-      for i = 1,maxCount do
-        table.insert(rval, encode(v[i]))
-      end
-    else       -- An object, not an array
-      for i,j in base.pairs(v) do
-        if isEncodable(i) and isEncodable(j) then
-          table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j))
-        end
-      end
-    end
-    if bArray then
-      return '[' .. table.concat(rval,',') ..']'
-    else
-      return '{' .. table.concat(rval,',') .. '}'
-    end
-  end
-  
-  -- Handle null values
-  if vtype=='function' and v==null then
-    return 'null'
-  end
-  
-  base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
-end
-
-
---- Decodes a JSON string and returns the decoded value as a Lua data structure / value.
--- @param s The string to scan.
--- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1.
--- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil,
--- and the position of the first character after
--- the scanned JSON object.
-function decode(s, startPos)
-  startPos = startPos and startPos or 1
-  startPos = decode_scanWhitespace(s,startPos)
-  base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
-  local curChar = string.sub(s,startPos,startPos)
-  -- Object
-  if curChar=='{' then
-    return decode_scanObject(s,startPos)
-  end
-  -- Array
-  if curChar=='[' then
-    return decode_scanArray(s,startPos)
-  end
-  -- Number
-  if string.find("+-0123456789.e", curChar, 1, true) then
-    return decode_scanNumber(s,startPos)
-  end
-  -- String
-  if curChar==[["]] or curChar==[[']] then
-    return decode_scanString(s,startPos)
-  end
-  if string.sub(s,startPos,startPos+1)=='/*' then
-    return decode(s, decode_scanComment(s,startPos))
-  end
-  -- Otherwise, it must be a constant
-  return decode_scanConstant(s,startPos)
-end
-
---- The null function allows one to specify a null value in an associative array (which is otherwise
--- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }
-function null()
-  return null -- so json.null() will also return null ;-)
-end
------------------------------------------------------------------------------
--- Internal, PRIVATE functions.
--- Following a Python-like convention, I have prefixed all these 'PRIVATE'
--- functions with an underscore.
------------------------------------------------------------------------------
-
---- Scans an array from JSON into a Lua object
--- startPos begins at the start of the array.
--- Returns the array and the next starting position
--- @param s The string being scanned.
--- @param startPos The starting position for the scan.
--- @return table, int The scanned array as a table, and the position of the next character to scan.
-function decode_scanArray(s,startPos)
-  local array = {}     -- The return value
-  local stringLen = string.len(s)
-  base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
-  startPos = startPos + 1
-  -- Infinite loop for array elements
-  repeat
-    startPos = decode_scanWhitespace(s,startPos)
-    base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
-    local curChar = string.sub(s,startPos,startPos)
-    if (curChar==']') then
-      return array, startPos+1
-    end
-    if (curChar==',') then
-      startPos = decode_scanWhitespace(s,startPos+1)
-    end
-    base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
-    object, startPos = decode(s,startPos)
-    table.insert(array,object)
-  until false
-end
-
---- Scans a comment and discards the comment.
--- Returns the position of the next character following the comment.
--- @param string s The JSON string to scan.
--- @param int startPos The starting position of the comment
-function decode_scanComment(s, startPos)
-  base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos)
-  local endPos = string.find(s,'*/',startPos+2)
-  base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
-  return endPos+2  
-end
-
---- Scans for given constants: true, false or null
--- Returns the appropriate Lua type, and the position of the next character to read.
--- @param s The string being scanned.
--- @param startPos The position in the string at which to start scanning.
--- @return object, int The object (true, false or nil) and the position at which the next character should be 
--- scanned.
-function decode_scanConstant(s, startPos)
-  local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
-  local constNames = {"true","false","null"}
-
-  for i,k in base.pairs(constNames) do
-    --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
-    if string.sub(s,startPos, startPos + string.len(k) -1 )==k then
-      return consts[k], startPos + string.len(k)
-    end
-  end
-  base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
-end
-
---- Scans a number from the JSON encoded string.
--- (in fact, also is able to scan numeric +- eqns, which is not
--- in the JSON spec.)
--- Returns the number, and the position of the next character
--- after the number.
--- @param s The string being scanned.
--- @param startPos The position at which to start scanning.
--- @return number, int The extracted number and the position of the next character to scan.
-function decode_scanNumber(s,startPos)
-  local endPos = startPos+1
-  local stringLen = string.len(s)
-  local acceptableChars = "+-0123456789.e"
-  while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
-       and endPos<=stringLen
-       ) do
-    endPos = endPos + 1
-  end
-  local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
-  local stringEval = base.loadstring(stringValue)
-  base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
-  return stringEval(), endPos
-end
-
---- Scans a JSON object into a Lua object.
--- startPos begins at the start of the object.
--- Returns the object and the next starting position.
--- @param s The string being scanned.
--- @param startPos The starting position of the scan.
--- @return table, int The scanned object as a table and the position of the next character to scan.
-function decode_scanObject(s,startPos)
-  local object = {}
-  local stringLen = string.len(s)
-  local key, value
-  base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
-  startPos = startPos + 1
-  repeat
-    startPos = decode_scanWhitespace(s,startPos)
-    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
-    local curChar = string.sub(s,startPos,startPos)
-    if (curChar=='}') then
-      return object,startPos+1
-    end
-    if (curChar==',') then
-      startPos = decode_scanWhitespace(s,startPos+1)
-    end
-    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
-    -- Scan the key
-    key, startPos = decode(s,startPos)
-    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
-    startPos = decode_scanWhitespace(s,startPos)
-    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
-    base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)
-    startPos = decode_scanWhitespace(s,startPos+1)
-    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
-    value, startPos = decode(s,startPos)
-    object[key]=value
-  until false  -- infinite loop while key-value pairs are found
-end
-
---- Scans a JSON string from the opening inverted comma or single quote to the
--- end of the string.
--- Returns the string extracted as a Lua string,
--- and the position of the next non-string character
--- (after the closing inverted comma or single quote).
--- @param s The string being scanned.
--- @param startPos The starting position of the scan.
--- @return string, int The extracted string as a Lua string, and the next character to parse.
-function decode_scanString(s,startPos)
-  base.assert(startPos, 'decode_scanString(..) called without start position')
-  local startChar = string.sub(s,startPos,startPos)
-  base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
-  local escaped = false
-  local endPos = startPos + 1
-  local bEnded = false
-  local stringLen = string.len(s)
-  repeat
-    local curChar = string.sub(s,endPos,endPos)
-    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
-
index e44241e..ba36f08 100644 (file)
@@ -582,29 +582,6 @@ rpc_juci_ui_acls(struct ubus_context *ctx, struct ubus_object *obj,
        ubus_send_reply(ctx, req, buf.head);
        return 0;
 }
-/*
-static int
-rpc_juci_ui_crypt(struct ubus_context *ctx, struct ubus_object *obj,
-                   struct ubus_request_data *req, const char *method,
-                   struct blob_attr *msg)
-{
-       char *hash;
-       struct blob_attr *tb[__RPC_D_MAX];
-
-       blobmsg_parse(rpc_data_policy, __RPC_D_MAX, tb,
-                     blob_data(msg), blob_len(msg));
-
-       if (!tb[RPC_D_DATA] || blobmsg_data_len(tb[RPC_D_DATA]) >= 128)
-               return UBUS_STATUS_INVALID_ARGUMENT;
-
-       hash = crypt(blobmsg_get_string(tb[RPC_D_DATA]), "$1$");
-
-       blob_buf_init(&buf, 0);
-       blobmsg_add_string(&buf, "crypt", hash);
-
-       ubus_send_reply(ctx, req, buf.head);
-       return 0;
-}*/
 
 static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
                          const char *type, struct blob_attr *msg)
@@ -670,18 +647,10 @@ run_command(const char *pFmt, int *exit_code, ...)
 {
        va_list ap;
        char cmd[256] = {0};
-       int len=0, maxLen;
-
-       maxLen = sizeof(cmd);
-
+       
+       
        va_start(ap, pFmt);
-
-       if (len < maxLen)
-       {
-               maxLen -= len;
-               vsnprintf(&cmd[len], maxLen, pFmt, ap);
-       }
-
+       vsnprintf(cmd, sizeof(cmd), pFmt, ap);
        va_end(ap);
 
        FILE *pipe = 0;
@@ -693,17 +662,18 @@ run_command(const char *pFmt, int *exit_code, ...)
                size_t size = 0; 
                while(size = fgets(ptr, sizeof(buffer) - (ptr - buffer), pipe)){
                        ptr+=size; 
+                       //*ptr = '\0'; 
                }
                
                *exit_code = WEXITSTATUS(pclose(pipe));
 
                remove_newline(buffer);
-               if (strlen(buffer))
+               if (ptr != buffer)
                        return (const char*)buffer;
                else
-                       return "";
+                       return "{}";
        } else {
-               return ""; 
+               return "{}"; 
        }
 }
 
@@ -720,7 +690,7 @@ static int rpc_shell_script(struct ubus_context *ctx, struct ubus_object *obj,
        
        if(stat(fname, &st) == 0){
                const char *resp = run_command("%s %s '%s'", &exit_code, fname, method, blobmsg_format_json(msg, true)); 
-               if(strlen(resp) && !blobmsg_add_json_from_string(&buf, resp))
+               if(!blobmsg_add_json_from_string(&buf, resp))
                        return UBUS_STATUS_NO_DATA; 
        }
        
index 3f12691..aa1e418 100644 (file)
@@ -1,4 +1,5 @@
-OUTDIR:=htdocs
+BUILD_DIR:=build
+OUTDIR:=$(BUILD_DIR)/www/
 CSS:=$(OUTDIR)/css/01-juci.css
 JS:=$(OUTDIR)/js/01-juci.js
 JSLIB:=$(OUTDIR)/js/00-juci-libs.js
@@ -69,7 +70,7 @@ all: $(OUTDIR) files $(CSS) $(JSLIB) $(JS) $(HTML)
        rm -f $(HTML)
 
 $(OUTDIR): 
-       mkdir $(OUTDIR)
+       mkdir -p $(OUTDIR)
 
 .PHONY: files
 
@@ -81,6 +82,8 @@ files:
        #$(CP) ./src/index.html $(OUTDIR)/
        # TODO: this needs to be replaced later with /etc/config/juci with proper acl permissions
        $(CP) ./juci.config.json $(OUTDIR)/config.json
+       $(INSTALL_DIR) $(BUILD_DIR)/usr/share/lua/
+       $(CP) lualibs/* $(BUILD_DIR)/usr/share/lua/
        
        
 $(CSS): $(STYLES)
diff --git a/juci/lualibs/JSON.lua b/juci/lualibs/JSON.lua
new file mode 100644 (file)
index 0000000..e0b3d17
--- /dev/null
@@ -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)
+    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
+
diff --git a/juci/lualibs/JUCI.lua b/juci/lualibs/JUCI.lua
new file mode 100644 (file)
index 0000000..dfb124e
--- /dev/null
@@ -0,0 +1,38 @@
+local table = require("table"); 
+local string = require("string"); 
+local io = require("io"); 
+require("JSON"); 
+
+local base = _G
+
+module("juci"); 
+
+function shell(fmt, ...)
+       for k,v in base.pairs(arg) do
+               -- escape all arguments to prevent code injection!
+               if base.type(v) == "string" then 
+                       arg[k] = "\""..v:gsub("\"", "\\\"").."\""; 
+               end
+       end
+       local p = base.assert(io.popen(string.format(fmt, base.unpack(arg)))); 
+       local s = p:read("*a"); 
+       p:close(); 
+       return s; 
+end
+
+function ubus(_calls, arg) 
+       local call_list = ""; 
+       for k,v in base.pairs(_calls) do 
+               if call_list ~= "" then call_list = call_list..","; end
+               call_list = call_list..k; 
+       end
+       if arg[1] == ".methods" then 
+               base.print(call_list); 
+       elseif _calls[arg[1]] then 
+               local params = {}; 
+               if arg[2] then params = base.json.decode(arg[2]); end
+               _calls[arg[1]](params); 
+       else 
+               io.write("Unknown method!\n"); 
+       end
+end
index f4496e8..eed472b 100644 (file)
                                                if(result.result[0] != 0){ // || result.result[1] == undefined) {
                                                        function _errstr(error){
                                                                switch(error){
-                                                                       case 0: return gettext("Parse error"); 
-                                                                       case 1: return gettext("Invalid request"); 
+                                                                       case 0: return gettext("OK"); 
+                                                                       case 1: return gettext("Invalid command"); 
                                                                        case 2: return gettext("Invalid parameters"); 
-                                                                       case 3: return gettext("Internal error"); 
+                                                                       case 3: return gettext("Method not found"); 
                                                                        case 4: return gettext("Object not found"); 
-                                                                       case 5: return gettext("No changes to commit"); // or session not found? 
+                                                                       case 5: return gettext("No data"); 
                                                                        case 6: return gettext("Access denied"); 
                                                                        case 7: return gettext("Timed out"); 
+                                                                       case 8: return gettext("Not supported"); 
+                                                                       case 9: return gettext("Unknown error"); 
+                                                                       case 10: return gettext("Connection failed"); 
                                                                        default: return gettext("RPC error #")+result.result[0]+": "+result.result[1]; 
                                                                }
                                                        }
index 19fbdce..36a6ce0 100644 (file)
@@ -1,40 +1,12 @@
 #!/usr/bin/lua
 
-require("JSON"); 
+require("JUCI"); 
 
 function dsl_stats()
        local res = {}; 
-       local tbl = {}; 
-       local obj = {}; 
-       local field_names = {"bridge", "device", "srcdev", "tags", "lantci", "wantci", "group", "mode", "rx_group", "source", "reporter", "timeout", "index", "excludpt"}; 
-       
-       local f = assert(io.popen("igmpinfo", "r")); 
-       local line = f:read("*l"); 
-       while line do
-               local fields = {}
-               local obj = {}; 
-               for w in s:gmatch("[^\t]+") do table.insert(fields, w) end
-               for i,v in ipairs(fields) do
-                       obj[field_names[i]] = v; 
-               end
-               table.insert(tbl, obj); 
-               line = f:read("*l"); 
-       end
-       f:close(); 
-       res["dslstats"] = {}; 
        print(json.encode(res)); 
 end
 
-local _calls = {
-       ["dslstats"] = dsl_stats
-}; 
-
-if arg[1] == ".methods" then 
-       print("dslstats");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
+juci.ubus({
+       ["status"] = dsl_stats
+}, arg); 
index 781d853..6ec1be0 100644 (file)
@@ -3,7 +3,7 @@
                "description": "JUCI Diagnostics module",
                "read": {
                        "ubus": {
-                               "juci.tools": [
+                               "juci.diagnostics": [
                                        "ping", 
                                        "traceroute"
                                ]
index 352ce78..fef65f1 100644 (file)
@@ -1,55 +1,38 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function diag_ping(opts)
        local res = {}; 
        if(not opts["host"]) then return; end; 
-       res["stdout"] = shell("ping -c 5 -W 1 "..opts["host"]); 
+       res["stdout"] = juci.shell("ping -c 5 -W 1 %s", opts["host"]); 
        print(json.encode(res)); 
 end
 
 function diag_ping6(opts)
        local res = {}; 
        if(not opts["host"]) then return; end; 
-       res["stdout"] = shell("ping6 -c 5 -W 1 "..opts["host"]); 
+       res["stdout"] = juci.shell("ping6 -c 5 -W 1 %s", opts["host"]); 
        print(json.encode(res)); 
 end
 
 function diag_traceroute(opts)
        local res = {}; 
        if(not opts["host"]) then return; end; 
-       res["stdout"] = shell("traceroute -q 1 -w 1 -n "..opts["host"]); 
+       res["stdout"] = juci.shell("traceroute -q 1 -w 1 -n %s", opts["host"]); 
        print(json.encode(res)); 
 end
 
 function diag_traceroute6(opts)
        local res = {}; 
        if(not opts["host"]) then return; end; 
-       res["stdout"] = shell("traceroute6 -q 1 -w 2 -n "..opts["host"]); 
+       res["stdout"] = juci.shell("traceroute6 -q 1 -w 2 -n %s", opts["host"]); 
        print(json.encode(res)); 
 end
 
-local _calls = {
+juci.ubus({
        ["ping"] = diag_ping, 
        ["ping6"] = diag_ping6, 
        ["traceroute"] = diag_traceroute,
        ["traceroute6"] = diag_traceroute6
-}; 
-
-if arg[1] == ".methods" then 
-       print("ping,ping6,traceroute,traceroute6");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
+}, arg); 
index 90ddd26..b61446e 100644 (file)
@@ -1,13 +1,6 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function macdb_lookup(opts)
        local res = {}; 
@@ -25,16 +18,6 @@ function macdb_lookup(opts)
        print(json.encode(res)); 
 end
 
-local _calls = {
+juci.ubus({
        ["lookup"] = macdb_lookup
-}; 
-
-if arg[1] == ".methods" then 
-       print("lookup");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
+}, arg); 
diff --git a/plugins/juci-mod-status/src/pages/status.diagnostics.html b/plugins/juci-mod-status/src/pages/status.diagnostics.html
deleted file mode 100644 (file)
index 1be29bb..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<juci-layout-with-sidebar>
-       <div ng-controller="StatusDiagnostics">
-               <juci-config-heading>{{ 'Diagnostic Utility' | translate }}</juci-config-heading>
-               <juci-config-info>{{ 'status.diagnostics.info' | translate }}</juci-config-info>
-               
-               <!--<juci-config-section>
-                       <h3>{{'Automated Diagnostics'|translate}}</h3>
-                       <p>{{'status.diagnostics.auto.info'|translate}}</p>
-                       <juci-config-lines>
-                               <juci-config-line title="">
-                                       <button class="btn btn-lg btn-default">{{'Diagnose'|translate}}</button>
-                               </juci-config-line>
-                       </juci-config-lines>
-               </juci-config-section>-->
-               <juci-config-section>
-                       <h3>{{'Ping Test'|translate}}</h3>
-                       <p>{{'status.diagnostics.ping.info'|translate}}</p>
-                       <div class="row">
-                               <form class="form-inline form-group  pull-right">
-                                       <input type="text" class="form-control input-lg" placeholder="{{'Host to ping'|translate}}" ng-model="data.pingHost"></input>
-                                       <button type="submit" class="btn btn-lg btn-default" ng-click="onPingTest()">{{'Ping'|translate}}</button>
-                               </form>
-                       </div>
-                       <div class="alert alert-default" ng-show="data.pingResults">{{'Ping results'|translate}}: <br/><pre>{{data.pingResults}}</pre></div>
-                       <div class="alert alert-danger" ng-show="data.pingError">{{'Error'|translate}}: <br/><pre>{{data.pingError}}</pre></div>
-               </juci-config-section>
-               <juci-config-section>
-                       <h3>{{'Tracing Tool'|translate}}</h3>
-                       <p>{{'status.diagnostics.trace.info'|translate}}</p>
-                       <div class="row">
-                               <form class="form-inline form-group  pull-right">
-                                       <input type="text" class="form-control input-lg" placeholder="{{'Host to trace'|translate}}" ng-model="data.traceHost"></input>
-                                       <button type="submit" class="btn btn-lg btn-default" ng-click="onTraceTest()">{{'Trace'|translate}}</button>
-                               </form>
-                       </div>
-                       <div class="alert alert-default" ng-show="data.traceResults">{{'Trace results'|translate}}: <br/><pre>{{data.traceResults}}</pre></div>
-                       <div class="alert alert-danger" ng-show="data.traceError">{{'Error'|translate}}: <br/><pre>{{data.traceError}}</pre></div>
-               </juci-config-section>
-       </div>
-</juci-layout-with-sidebar>
diff --git a/plugins/juci-mod-status/src/pages/status.diagnostics.js b/plugins/juci-mod-status/src/pages/status.diagnostics.js
deleted file mode 100644 (file)
index 14c7677..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-//! Author: Martin K. Schröder <mkschreder.uk@gmail.com>
-
-JUCI.app
-.controller("StatusDiagnostics", function($scope, $rpc){
-       $scope.data = {}; 
-       $rpc.router.networks().done(function(result){
-               if(result){
-                       $scope.data.allInterfaces = Object.keys(result).map(function(x){return {label: x, value: x};}); 
-                       $scope.$apply(); 
-               }
-       }); 
-       $scope.onTraceTest = function(){
-               $rpc.juci.network.traceroute({ data: $scope.data.traceHost }).done(function(result){
-                       if(result.stderr) $scope.data.traceError = result.stderr; 
-                       $scope.data.traceResults = result.stdout; 
-                       $scope.$apply(); 
-               }).fail(function(error){
-                       $scope.data.traceResults = ""; 
-                       $scope.data.traceError = JSON.stringify(error); 
-                       $scope.$apply(); 
-               }); 
-       }
-       $scope.onPingTest = function(){
-               $scope.data.pingResults = "..."; 
-               $scope.data.error = "";
-               $rpc.juci.network.ping({ data: $scope.data.pingHost }).done(function(result){
-                       if(result.stderr) $scope.data.pingError = result.stderr; 
-                       $scope.data.pingResults = result.stdout; 
-                       $scope.$apply(); 
-               }).fail(function(error){
-                       $scope.data.pingResults = ""; 
-                       $scope.data.pingError = JSON.stringify(error); 
-                       $scope.$apply(); 
-               }); 
-       }
-}); 
index f847da6..f3a0153 100644 (file)
                                        <th translate>Message</th>
                                </thead>
                                <tr ng-repeat="line in logs track by $index" class="{{lineClass(line)}}">
-                                       <td nowrap>{{line[0]}}</td>
-                                       <td>{{line[1]}}</td>
-                                       <td>{{line[2]}}</td>
-                                       <td>{{line[3]}}</td>
+                                       <td nowrap>{{line.date}}</td>
+                                       <td>{{line.type}}</td>
+                                       <td>{{line.source}}</td>
+                                       <td>{{line.message}}</td>
                                </tr>
                        </table>
                </juci-config-section>
index 7ef385e..8feaffd 100644 (file)
@@ -15,41 +15,6 @@ JUCI.app
                $rpc.juci.system.log().done(function(result){
                        if(result && result.lines){
                                $scope.logs = result.lines; 
-                               /*result.log.split("\n").map(function(line){
-                                       var fields = line.match(/(\w* \w* \w* \d{2}:\d{2}:\d{2} \d{4}) ([^\s]*) ([^\s:]*): (.*)/); 
-                                       if(fields){
-                                               // remove first one because it is the whole line
-                                               fields.shift(); 
-                                               fields[0] = new Date(fields[0]); 
-                                       }
-                                       return fields; 
-                               })
-                               .filter(function(x){ 
-                                       // Epic ugliness
-                                       // TODO: fix up the log parsing code
-                                       if(x == null || x[2] == "kernel" || x[2] == "syslog") return false; 
-                                       x[2] = x[2].replace(/\[.*\]/gi, ""); 
-                                       var visible = false; 
-                                       var error = false, warning = false, info = false; 
-                                       if(x[1].indexOf("error") >= 0) error = true; 
-                                       if(x[1].indexOf("warn") >= 0) warning = true; 
-                                       if(x[1].indexOf("notice") >= 0) info = true; 
-                                       if(error && $scope.selectedShowType.indexOf("error") == -1) return false; 
-                                       if(warning && $scope.selectedShowType.indexOf("warning") == -1) return false; 
-                                       if(info && $scope.selectedShowType.indexOf("info") == -1) return false; 
-                                       if(!error && !warning && !info && $scope.selectedLogTypes.indexOf("other") >= 0) visible = true; 
-                                       $scope.selectedLogTypes.map(function(t){
-                                               if(groups[t] && groups[t].indexOf(x[2]) >= 0) visible = true; 
-                                       }); 
-                                       return visible; 
-                               }) // filter out all invalid matches 
-                               .reverse() // sort by date in descending order
-                               .map(function(x){ // convert date back to string and shorten it's format
-                                       var d = x[0]; 
-                                       x[0] = d.getFullYear()+"-"+("00"+(d.getMonth()+1)).slice(-2)+"-"+d.getDate()+" "+d.getHours()+":"+d.getMinutes()+":"+d.getSeconds();
-                                       //x[0] = x[0].toLocaleFormat("%d-%b-%Y %H:%M:%S"); 
-                                       return x; 
-                               }); */
                                $scope.$apply(); 
                                done(); 
                        }
@@ -72,9 +37,9 @@ JUCI.app
                { label: "All Events", value: ["error", "warning", "info"] }
        ];
        $scope.lineClass = function(line){
-               if(line[1].indexOf("error") >= 0) return "label-danger"; 
-               if(line[1].indexOf("warn") >= 0) return "label-warning";  
-               if(line[1].indexOf("notice") >= 0) return "label-info"; 
+               if(line.type.indexOf("error") >= 0) return "label-danger"; 
+               if(line.type.indexOf("warn") >= 0) return "label-warning";  
+               if(line.type.indexOf("notice") >= 0) return "label-info"; 
                return ""; 
        }
        
index 7477b8f..8cc8c33 100755 (executable)
@@ -1,19 +1,12 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function system_filesystems(opts)
        local res = {}; 
        local lines = {}; 
        res["filesystems"] = lines; 
-       local stdout = shell("df | tail -n+2"); 
+       local stdout = juci.shell("df | tail -n+2"); 
        for line in stdout:gmatch("[^\r\n]+") do 
                local filesystem,total,used,free,percent,path = line:match("([^%s]*)%s*([^%s]*)%s*([^%s]*)%s*([^%s]*)%s*([^%s]*)%s*([^%s]*)%s*"); 
                local obj = {
@@ -32,8 +25,7 @@ function system_logread(opts)
        local res = {}; 
        local lines = {}; 
        res["lines"] = lines; 
-       local stdout = shell("logread"); 
-       local n = 0; 
+       local stdout = juci.shell("logread -l 20"); 
        for line in stdout:gmatch("[^\r\n]+") do 
                local date,type,source,message = line:match("([^%s]*%s[^%s]*%s[^%s]*%s[^%s]*%s[^%s]*)%s([^%s]*)%s([^%s:]*):%s(.*)"); 
                string.gsub(message, "\n", ""); 
@@ -43,34 +35,23 @@ function system_logread(opts)
                        ["source"] = source, 
                        ["message"] = message
                }; 
-               n = n+1; 
-               if n == 20 then break; end
                table.insert(lines, obj); 
        end
        print(json.encode(res)); 
 end
 
 function system_reboot()
-       shell("/sbin/reboot"); 
+       juci.shell("/sbin/reboot"); 
 end
 
 function system_defaultreset()
-       shell("/sbin/reboot"); 
+       juci.shell("/sbin/reboot"); 
 end
 
-local _calls = {
+juci.ubus({
        ["log"] = system_logread, 
        ["defaultreset"] = system_defaultreset,
        ["filesystems"] = system_filesystems, 
        ["reboot"] = system_reboot
-}; 
+}, arg)
 
-if arg[1] == ".methods" then 
-       print("log,defaultreset,filesystems,reboot");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
index 1c6e31e..3ec144b 100644 (file)
@@ -1,41 +1,24 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function backup_restore(opts)
        local res = {}; 
        local pass = ""; 
-       if(opts["password"]) then pass = "--password "..opts["password"]; end
-       res["stdout"] = shell("sysupgrade --restore-backup /tmp/backup.tar.gz "..pass); 
+       if(opts["password"]) then pass = "--password"; end
+       res["stdout"] = juci.shell("sysupgrade --restore-backup /tmp/backup.tar.gz %s %s", pass, opts["password"]); 
        print(json.encode(res)); 
 end
 
 function backup_clean()
        local res = {}; 
-       res["stdout"] = shell("sysupgrade --clean"); 
+       res["stdout"] = juci.shell("sysupgrade --clean"); 
        print(json.encode(res)); 
 end
 
-local _calls = {
+juci.ubus({
        ["restore"] = backup_restore, 
        ["clean"] = backup_clean, 
        ["list"] = backup_list
-}; 
-
-if arg[1] == ".methods" then 
-       print("restore,clean,list");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
+}, arg); 
 
index fce5409..2b53916 100755 (executable)
@@ -1,13 +1,6 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function fields(inputstr, sep)
        if sep == nil then
@@ -25,7 +18,7 @@ function process_list()
        local res = {}; 
        local lines = {}; 
        res["list"] = lines; 
-       local stdout = shell("top -bn1"); 
+       local stdout = juci.shell("top -bn1"); 
        for line in stdout:gmatch("[^\r\n]+") do 
                local arr = fields(line, "%s"); 
                local obj = {
@@ -42,16 +35,6 @@ function process_list()
        print(json.encode(res)); 
 end
 
-local _calls = {
+juci.ubus({
        ["list"] = process_list
-}; 
-
-if arg[1] == ".methods" then 
-       print("list");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
+}, arg); 
index 3b09390..72d7e30 100644 (file)
@@ -1,31 +1,15 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function user_password(opts)
-       local stdout = shell("echo -e \""..opts["password"].."\\n"..opts["password"].."\" | passwd admin"); 
+       if not opts["password"] then return 5; end
+       local stdout = juci.shell("echo -e %s | passwd admin", opts["password"].."\\n"..opts["password"]); 
        print(json.encode({
                ["stdout"] = stdout
        })); 
 end
 
-local _calls = {
+juci.ubus({
        ["password"] = user_password
-}; 
-
-if arg[1] == ".methods" then 
-       print("password");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end
+}, arg); 
index 5216065..d141929 100644 (file)
@@ -56,12 +56,8 @@ JUCI.app
                }).always(function(){ next(); }); 
        }, 
        function(next){
-               $rpc.router.clients().done(function(clients){
-                       //alert(JSON.stringify(Object.keys(clients).map(function(x) { return clients[x]; }))); 
-                       var all = Object.keys(clients).map(function(x) { return clients[x]; }); 
-                       $scope.clients = all.filter(function(x){
-                               return x.connected && x.wireless == false; 
-                       }); 
+               $rpc.juci.network.clients().done(function(result){
+                       $scope.clients = result.clients; 
                        next(); 
                }).fail(function(){
                        next();
index a162cbc..fb9a7c6 100755 (executable)
@@ -1,19 +1,12 @@
 #!/usr/bin/lua
 
-require("JSON"); 
-
-function shell(cmd)
-       local p = assert(io.popen(cmd)); 
-       local s = p:read("*a"); 
-       p:close(); 
-       return s; 
-end
+require("JUCI"); 
 
 function sysupgrade_check(params)
        local type = "usb"; 
        if params["type"] == "online" then type = "online"; end;
        local res = {}; 
-       res["stdout"] = shell("sysupgrade --"..type:gsub("\";", ""));  
+       res["stdout"] = juci.shell("sysupgrade %s".."--"..type);  
        print(json.encode(res)); 
 end
 
@@ -21,7 +14,7 @@ function sysupgrade_test(params)
        local res = {}; 
        local path = "/tmp/firmware.bin"; 
        if params["path"] then path = params["path"] end
-       res["stdout"] = shell("sysupgrade --test "..path:gsub("\"", "")); 
+       res["stdout"] = juci.shell("sysupgrade --test %s", path); 
        print(json.encode(res)); 
 end
 
@@ -31,29 +24,20 @@ function sysupgrade_start(params)
        local keep = ""; 
        if params["keep"] ~= 1 then keep = "-n" end
        if params["path"] then path = params["path"] end
-       res["stdout"] = shell("sysupgrade "..keep.." \""..path:gsub("\"", "").."\""); 
+       res["stdout"] = juci.shell("sysupgrade "..keep.." %s", path); 
        print(json.encode(res)); 
 end
 
 function sysupgrade_clean()
        local res = {}; 
-       res["stdout"] = shell("sysupgrade --clean"); 
+       res["stdout"] = juci.shell("sysupgrade --clean"); 
        print(json.encode(res)); 
 end
 
-local _calls = {
+juci.ubus({
        ["check"] = sysupgrade_check, 
        ["test"] = sysupgrade_test, 
        ["start"] = sysupgrade_start,
        ["clean"] = sysupgrade_clean
-}; 
+}, arg)
 
-if arg[1] == ".methods" then 
-       print("check,test,start,clean");
-elseif _calls[arg[1]] then 
-       local params = {}; 
-       if arg[2] then params = json.decode(arg[2]); end
-       _calls[arg[1]](params); 
-else 
-       io.write("Unknown method!\n"); 
-end