lede-packages-rs

git clone git://archive.git.mtrnord.blog/MTRNord/lede-packages-rs.git
Log | Files | Refs | README | LICENSE

index.lua (12089B)


      1 --[[
      2 This file is part of YunWebUI.
      3 
      4 YunWebUI is free software; you can redistribute it and/or modify
      5 it under the terms of the GNU General Public License as published by
      6 the Free Software Foundation; either version 2 of the License, or
      7 (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 GNU General Public License for more details.
     13 
     14 You should have received a copy of the GNU General Public License
     15 along with this program; if not, write to the Free Software
     16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     17 
     18 As a special exception, you may use this file as part of a free software
     19 library without restriction.  Specifically, if other files instantiate
     20 templates or use macros or inline functions from this file, or you compile
     21 this file and link it with other files to produce an executable, this
     22 file does not by itself cause the resulting executable to be covered by
     23 the GNU General Public License.  This exception does not however
     24 invalidate any other reasons why the executable file might be covered by
     25 the GNU General Public License.
     26 
     27 Copyright 2013 Arduino LLC (http://www.arduino.cc/)
     28 ]]
     29 
     30 module("luci.controller.arduino.index", package.seeall)
     31 
     32 local function not_nil_or_empty(value)
     33   return value and value ~= ""
     34 end
     35 
     36 local function get_first(cursor, config, type, option)
     37   return cursor:get_first(config, type, option)
     38 end
     39 
     40 local function set_first(cursor, config, type, option, value)
     41   cursor:foreach(config, type, function(s)
     42     if s[".type"] == type then
     43       cursor:set(config, s[".name"], option, value)
     44     end
     45   end)
     46 end
     47 
     48 
     49 local function to_key_value(s)
     50   local parts = luci.util.split(s, ":")
     51   parts[1] = luci.util.trim(parts[1])
     52   parts[2] = luci.util.trim(parts[2])
     53   return parts[1], parts[2]
     54 end
     55 
     56 function http_error(code, text)
     57   luci.http.prepare_content("text/plain")
     58   luci.http.status(code)
     59   if text then
     60     luci.http.write(text)
     61   end
     62 end
     63 
     64 function index()
     65   function luci.dispatcher.authenticator.arduinoauth(validator, accs, default)
     66     require("luci.controller.arduino.index")
     67 
     68     local user = luci.http.formvalue("username")
     69     local pass = luci.http.formvalue("password")
     70     local basic_auth = luci.http.getenv("HTTP_AUTHORIZATION")
     71 
     72     if user and validator(user, pass) then
     73       return user
     74     end
     75 
     76     if basic_auth and basic_auth ~= "" then
     77       local decoded_basic_auth = nixio.bin.b64decode(string.sub(basic_auth, 7))
     78       user = string.sub(decoded_basic_auth, 0, string.find(decoded_basic_auth, ":") - 1)
     79       pass = string.sub(decoded_basic_auth, string.find(decoded_basic_auth, ":") + 1)
     80     end
     81 
     82     if user then
     83       if #pass ~= 64 and validator(user, pass) then
     84         return user
     85       elseif #pass == 64 then
     86         local uci = luci.model.uci.cursor()
     87         uci:load("yunbridge")
     88         local stored_encrypted_pass = uci:get_first("yunbridge", "bridge", "password")
     89         if pass == stored_encrypted_pass then
     90           return user
     91         end
     92       end
     93     end
     94 
     95     luci.http.header("WWW-Authenticate", "Basic realm=\"yunbridge\"")
     96     luci.http.status(401)
     97 
     98     return false
     99   end
    100 
    101   local function make_entry(path, target, title, order)
    102     local page = entry(path, target, title, order)
    103     page.leaf = true
    104     return page
    105   end
    106 
    107   -- web panel
    108   local webpanel = entry({ "webpanel" }, alias("webpanel", "go_to_homepage"), _("%s Web Panel") % luci.sys.hostname(), 10)
    109   webpanel.sysauth = "root"
    110   webpanel.sysauth_authenticator = "arduinoauth"
    111 
    112   make_entry({ "webpanel", "go_to_homepage" }, call("go_to_homepage"), nil)
    113 
    114   --api security level
    115   local uci = luci.model.uci.cursor()
    116   uci:load("yunbridge")
    117   local secure_rest_api = uci:get_first("yunbridge", "bridge", "secure_rest_api")
    118   local rest_api_sysauth = false
    119   if secure_rest_api == "true" then
    120     rest_api_sysauth = webpanel.sysauth
    121   end
    122 
    123   --storage api
    124   local data_api = node("data")
    125   data_api.sysauth = rest_api_sysauth
    126   data_api.sysauth_authenticator = webpanel.sysauth_authenticator
    127   make_entry({ "data", "get" }, call("storage_send_request"), nil).sysauth = rest_api_sysauth
    128   make_entry({ "data", "put" }, call("storage_send_request"), nil).sysauth = rest_api_sysauth
    129   make_entry({ "data", "delete" }, call("storage_send_request"), nil).sysauth = rest_api_sysauth
    130   local mailbox_api = node("mailbox")
    131   mailbox_api.sysauth = rest_api_sysauth
    132   mailbox_api.sysauth_authenticator = webpanel.sysauth_authenticator
    133   make_entry({ "mailbox" }, call("build_bridge_mailbox_request"), nil).sysauth = rest_api_sysauth
    134 
    135   --plain socket endpoint
    136   local plain_socket_endpoint = make_entry({ "arduino" }, call("board_plain_socket"), nil)
    137   plain_socket_endpoint.sysauth = rest_api_sysauth
    138   plain_socket_endpoint.sysauth_authenticator = webpanel.sysauth_authenticator
    139 end
    140 
    141 function go_to_homepage()
    142   luci.http.redirect("/index.html")
    143 end
    144 
    145 local function build_bridge_request(command, params)
    146 
    147   local bridge_request = {
    148     command = command
    149   }
    150 
    151   if command == "raw" then
    152     params = table.concat(params, "/")
    153     if not_nil_or_empty(params) then
    154       bridge_request["data"] = params
    155     end
    156     return bridge_request
    157   end
    158 
    159   if command == "get" then
    160     if not_nil_or_empty(params[1]) then
    161       bridge_request["key"] = params[1]
    162     end
    163     return bridge_request
    164   end
    165 
    166   if command == "put" and not_nil_or_empty(params[1]) and params[2] then
    167     bridge_request["key"] = params[1]
    168     bridge_request["value"] = params[2]
    169     return bridge_request
    170   end
    171 
    172   if command == "delete" and not_nil_or_empty(params[1]) then
    173     bridge_request["key"] = params[1]
    174     return bridge_request
    175   end
    176 
    177   return nil
    178 end
    179 
    180 local function extract_jsonp_param(query_string)
    181   if not not_nil_or_empty(query_string) then
    182     return nil
    183   end
    184 
    185   local qs_parts = string.split(query_string, "&")
    186   for idx, value in ipairs(qs_parts) do
    187     if string.find(value, "jsonp") == 1 or string.find(value, "callback") == 1 then
    188       return string.sub(value, string.find(value, "=") + 1)
    189     end
    190   end
    191 end
    192 
    193 local function parts_after(url_part)
    194   local url = luci.http.getenv("PATH_INFO")
    195   local url_after_part = string.find(url, "/", string.find(url, url_part) + 1)
    196   if not url_after_part then
    197     return {}
    198   end
    199   return luci.util.split(string.sub(url, url_after_part + 1), "/")
    200 end
    201 
    202 function storage_send_request()
    203   local method = luci.http.getenv("REQUEST_METHOD")
    204   local jsonp_callback = extract_jsonp_param(luci.http.getenv("QUERY_STRING"))
    205   local parts = parts_after("data")
    206   local command = parts[1]
    207   if not command or command == "" then
    208     luci.http.status(404)
    209     return
    210   end
    211   local params = {}
    212   for idx, param in ipairs(parts) do
    213     if idx > 1 and not_nil_or_empty(param) then
    214       table.insert(params, param)
    215     end
    216   end
    217 
    218   -- TODO check method?
    219   local bridge_request = build_bridge_request(command, params)
    220   if not bridge_request then
    221     luci.http.status(403)
    222     return
    223   end
    224 
    225   local uci = luci.model.uci.cursor()
    226   uci:load("yunbridge")
    227   local socket_timeout = uci:get_first("yunbridge", "bridge", "socket_timeout", 5)
    228 
    229   local sock, code, msg = nixio.connect("127.0.0.1", 5700)
    230   if not sock then
    231     code = code or ""
    232     msg = msg or ""
    233     http_error(500, "nil socket, " .. code .. " " .. msg)
    234     return
    235   end
    236 
    237   sock:setopt("socket", "sndtimeo", socket_timeout)
    238   sock:setopt("socket", "rcvtimeo", socket_timeout)
    239   sock:setopt("tcp", "nodelay", 1)
    240 
    241   local json = require("luci.json")
    242 
    243   sock:write(json.encode(bridge_request))
    244   sock:writeall("\n")
    245 
    246   local response_text = {}
    247   while true do
    248     local bytes = sock:recv(4096)
    249     if bytes and #bytes > 0 then
    250       table.insert(response_text, bytes)
    251     end
    252 
    253     local json_response = json.decode(table.concat(response_text))
    254     if json_response then
    255       sock:close()
    256       luci.http.status(200)
    257       if jsonp_callback then
    258         luci.http.prepare_content("application/javascript")
    259         luci.http.write(jsonp_callback)
    260         luci.http.write("(")
    261         luci.http.write_json(json_response)
    262         luci.http.write(");")
    263       else
    264         luci.http.prepare_content("application/json")
    265         luci.http.write(json.encode(json_response))
    266       end
    267       return
    268     end
    269 
    270     if not bytes or #response_text == 0 then
    271       sock:close()
    272       http_error(500, "Empty response")
    273       return
    274     end
    275   end
    276 
    277   sock:close()
    278 end
    279 
    280 function board_plain_socket()
    281   local function send_response(response_text, jsonp_callback)
    282     if not response_text then
    283       luci.http.status(500)
    284       return
    285     end
    286 
    287     local rows = luci.util.split(response_text, "\r\n")
    288     if #rows == 1 or string.find(rows[1], "Status") ~= 1 then
    289       luci.http.prepare_content("text/plain")
    290       luci.http.status(200)
    291       luci.http.write(response_text)
    292       return
    293     end
    294 
    295     local body_start_at_idx = -1
    296     local content_type = "text/plain"
    297     for idx, row in ipairs(rows) do
    298       if row == "" then
    299         body_start_at_idx = idx
    300         break
    301       end
    302 
    303       local key, value = to_key_value(row)
    304       if string.lower(key) == "status" then
    305         luci.http.status(tonumber(value))
    306       elseif string.lower(key) == "content-type" then
    307         content_type = value
    308       else
    309         luci.http.header(key, value)
    310       end
    311     end
    312 
    313     local response_body = table.concat(rows, "\r\n", body_start_at_idx + 1)
    314     if content_type == "application/json" and jsonp_callback then
    315       local json = require("luci.json")
    316       luci.http.prepare_content("application/javascript")
    317       luci.http.write(jsonp_callback)
    318       luci.http.write("(")
    319       luci.http.write_json(json.decode(response_body))
    320       luci.http.write(");")
    321     else
    322       luci.http.prepare_content(content_type)
    323       luci.http.write(response_body)
    324     end
    325   end
    326 
    327   local method = luci.http.getenv("REQUEST_METHOD")
    328   local jsonp_callback = extract_jsonp_param(luci.http.getenv("QUERY_STRING"))
    329   local parts = parts_after("arduino")
    330   local params = {}
    331   for idx, param in ipairs(parts) do
    332     if not_nil_or_empty(param) then
    333       table.insert(params, param)
    334     end
    335   end
    336 
    337   if #params == 0 then
    338     luci.http.status(404)
    339     return
    340   end
    341 
    342   params = table.concat(params, "/")
    343 
    344   local uci = luci.model.uci.cursor()
    345   uci:load("yunbridge")
    346   local socket_timeout = uci:get_first("yunbridge", "bridge", "socket_timeout", 5)
    347 
    348   local sock, code, msg = nixio.connect("127.0.0.1", 5555)
    349   if not sock then
    350     code = code or ""
    351     msg = msg or ""
    352     http_error(500, "Could not connect to YunServer " .. code .. " " .. msg)
    353     return
    354   end
    355 
    356   sock:setopt("socket", "sndtimeo", socket_timeout)
    357   sock:setopt("socket", "rcvtimeo", socket_timeout)
    358   sock:setopt("tcp", "nodelay", 1)
    359 
    360   sock:write(params)
    361   sock:writeall("\r\n")
    362 
    363   local response_text = sock:readall()
    364   sock:close()
    365 
    366   send_response(response_text, jsonp_callback)
    367 end
    368 
    369 function build_bridge_mailbox_request()
    370   local method = luci.http.getenv("REQUEST_METHOD")
    371   local jsonp_callback = extract_jsonp_param(luci.http.getenv("QUERY_STRING"))
    372   local parts = parts_after("mailbox")
    373   local params = {}
    374   for idx, param in ipairs(parts) do
    375     if not_nil_or_empty(param) then
    376       table.insert(params, param)
    377     end
    378   end
    379 
    380   if #params == 0 then
    381     luci.http.status(400)
    382     return
    383   end
    384 
    385   local bridge_request = build_bridge_request("raw", params)
    386   if not bridge_request then
    387     luci.http.status(403)
    388     return
    389   end
    390 
    391   local uci = luci.model.uci.cursor()
    392   uci:load("yunbridge")
    393   local socket_timeout = uci:get_first("yunbridge", "bridge", "socket_timeout", 5)
    394 
    395   local sock, code, msg = nixio.connect("127.0.0.1", 5700)
    396   if not sock then
    397     code = code or ""
    398     msg = msg or ""
    399     http_error(500, "nil socket, " .. code .. " " .. msg)
    400     return
    401   end
    402 
    403   sock:setopt("socket", "sndtimeo", socket_timeout)
    404   sock:setopt("socket", "rcvtimeo", socket_timeout)
    405   sock:setopt("tcp", "nodelay", 1)
    406 
    407   local json = require("luci.json")
    408 
    409   sock:write(json.encode(bridge_request))
    410   sock:writeall("\n")
    411   sock:close()
    412 
    413   luci.http.status(200)
    414 end