diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55d6ad1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +\#* \ No newline at end of file diff --git a/files/etc/config/nodogsplash b/files/etc/config/nodogsplash new file mode 100644 index 0000000..45504c1 --- /dev/null +++ b/files/etc/config/nodogsplash @@ -0,0 +1,7 @@ +config settings settings + option enable '1' + option splashtime '1' + option splashunit 'hours' + +config interfaces interfaces + option interface 'br-lan' diff --git a/files/etc/config/serval b/files/etc/config/serval new file mode 100644 index 0000000..4f9aba2 --- /dev/null +++ b/files/etc/config/serval @@ -0,0 +1,7 @@ +config settings settings + option enabled 'true' + option primary_keyring '/etc/serval' + option olsrd_mdp_keyring '/etc/commotion/keys.d/mdp' + option update_primary_keyring 'false' + option update_mdp_keyring 'false' + option new_mdp_keyring 'false' \ No newline at end of file diff --git a/files/etc/config/setup_wizard b/files/etc/config/setup_wizard new file mode 100644 index 0000000..2ba59c0 --- /dev/null +++ b/files/etc/config/setup_wizard @@ -0,0 +1,5 @@ +config settings settings + option enabled '1' + +config uci passwords + option admin_pass 'false' \ No newline at end of file diff --git a/files/etc/init.d/ucidog b/files/etc/init.d/ucidog new file mode 100755 index 0000000..9e2f662 --- /dev/null +++ b/files/etc/init.d/ucidog @@ -0,0 +1,44 @@ +#!/bin/sh /etc/rc.common + +#simple barely functional nodogsplash uci-track script to set nodogsplash uci values that were set. + +. /lib/functions/commotion.sh +. /lib/config/uci.sh + +START=19 +STOP=91 + +reload() { + #nodogsplash configuration file. + local conffile=/etc/nodogsplash/nodogsplash.conf + + #convert splash page time into minues + local splash_time=$(uci_get nodogsplash settings splashtime) + local splash_unit=$(uci_get nodogsplash settings splashunit) + if [ "$splash_unit" == "days" ]; then + splash_time=$(($splash_time*60)) + elif [ "$splash_unit" == "hours" ]; then + splash_time=$(($splash_time*24*60)) + elif [ "$splash_unit" == "seconds" ]; then + splash_time=$(($splash_time/60)) + fi + #set splashpage time + sed -i "s/^\(ClientIdleTimeout \).*/\1 $splash_time/" $conffile || return 0 + sed -i "s/^\(ClientForceTimeout \).*/\1 $splash_time/" $conffile || return 0 + + #get and set interface name + local interface=$(uci_get nodogsplash interfaces interface) + local network=$(uci_get wireless $interface network) + sed -i "s/^\(GatewayInterface \).*/\1 $interface/" $conffile || return 0 + /etc/init.d/nodogsplash stop && sleep 1 + + #disable/enable nodogsplash + local enable=$(uci_get nodogsplash settings enable) + echo "$enable" + if [ "$enable" == '1' ]; then #this might come back as a string and not as a intiger. Check and fix that. + /etc/init.d/nodogsplash enable || return 0 + /etc/init.d/nodogsplash start || return 0 + else + /etc/init.d/nodogsplash disable || return 0 + fi +} diff --git a/files/etc/uci-defaults/luci-setup-wizard b/files/etc/uci-defaults/luci-setup-wizard new file mode 100755 index 0000000..0e1cacc --- /dev/null +++ b/files/etc/uci-defaults/luci-setup-wizard @@ -0,0 +1,17 @@ +#!/bin/sh + +. /etc/functions.sh + +#setup ucitrack +uci add ucitrack nodogsplash +uci set ucitrack.@nodogsplash[0].init=ucidog +uci add_list ucitrack.@system[0].affects=avahi_daemon +uci add ucitrack avahi_daemon +uci set ucitrack.@avahi_daemon[0].init=avahi-daemon +uci add_list ucitrack.@setup_wizard[0].affects=nodogsplash +uci commit ucitrack + +#setup sysupgrade saves +echo /etc/config/setup_wizard >> /etc/sysupgrade.conf + +exit 0 diff --git a/luasrc/controller/commotion/basic_config.lua b/luasrc/controller/commotion/basic_config.lua new file mode 100644 index 0000000..42eea8f --- /dev/null +++ b/luasrc/controller/commotion/basic_config.lua @@ -0,0 +1,174 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . + +HUGE Thanks to the niu suite!!! +]]-- +--[[ + Copyright (C) 2008 Steven Barth + +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.controller.commotion.basic_config", package.seeall) +local db = require "luci.commotion.debugger" + +function index() + local SW = require "luci.commotion.setup_wizard" + + entry({"admin", "commotion"}, alias("admin", "commotion", "status"), translate("Commotion"), 20) + + local page = node() + page.lock = true + page.target = alias("commotion") + page.subindex = true + page.index = false + + local root = node() + if not root.lock then + root.target = alias("commotion") + root.index = true + end + + local redir = luci.http.formvalue("redir", true) or + luci.dispatcher.build_url(unpack(luci.dispatcher.context.request)) + + cnfm = entry({"admin", "commotion", "confirm"}, call("action_changes"), translate("Confirm"), 40) + cnfm.query = {redir=redir} + cnfm.hidden = true + + rvt = entry({"admin", "commotion", "revert"}, call("action_revert")) + rvt.query = {redir=redir} + rvt.hidden = true + + sva = entry({"admin", "commotion", "saveapply"}, call("action_apply")) + sva.query = {redir=redir} + sva.hidden = true + + --IF Setup Wizard is Active + if SW.status() then + local sw_page = luci.http.formvalue("sw_page") or nil + + entry({"commotion"}, alias("commotion", "welcome")) + entry({"commotion", "welcome"}, template("commotion/welcome"), translate("Welcome to Commotion")).hidden = true + entry({"commotion", "advanced"}, call("advanced")).hidden = true + entry({"commotion", "setup_wizard", "start"}, call("start_setup")).hidden = true + + local confirm = {on_success_to={"commotion", "confirm"}} + --Setup Wizard Delegator + entry({"commotion", "setup_wizard"}, cbi("commotion/setup_wizard", {on_success_to={"commotion", "confirm"}, sw_page=sw_page}), translate("Setup Wizard"), 15).hidden = true + + --Confirmation Pages + sw_cnfm = entry({"commotion", "confirm"}, call("action_changes"), translate("Confirm"), 40) + sw_cnfm.query = {redir=redir} + sw_cnfm.hidden = true + + sw_rvt = entry({"commotion", "revert"}, call("action_revert")) + sw_rvt.query = {redir=redir} + sw_rvt.hidden = true + + sw_sva = entry({"commotion", "saveapply"}, call("action_apply")) + sw_sva.query = {redir=redir} + sw_sva.hidden = true + else + entry({"commotion"}, alias("apps")) + --Create regular "Basic Config" menu. + entry({"admin", "commotion", "basic"}, alias("admin", "commotion", "basic", "node_settings"), translate("Basic Configuration"), 25).index = true + + --No Subsection for Node Settings? + entry({"admin", "commotion", "basic", "node_settings"}, cbi("commotion/basic_ns", {hideapplybtn=true, hideresetbtn=true}), translate("Node Settings"), 25).subsection=true + + --Subsection Network Settings + entry({"admin", "commotion", "basic", "network_settings"}, alias("admin", "commotion", "basic", "mesh_network"), translate("Network Settings"), 30).subsection=true + entry({"admin", "commotion", "basic", "mesh_network"}, cbi("commotion/basic_mn", {hideapplybtn=true, hideresetbtn=true}), translate("Mesh Network"), 40) + entry({"admin", "commotion", "basic", "wireless_network"}, cbi("commotion/basic_wn", {hideapplybtn=true, hideresetbtn=true}), translate("Wireless Network"), 50) + entry({"admin", "commotion", "basic", "addtl_net_ifaces"}, cbi("commotion/basic_ani", {hideapplybtn=true, hideresetbtn=true}), translate("Additional Network Interfaces"), 60) + end +end + +function advanced() + local uci = require "luci.model.uci".cursor() + local disp = require "luci.dispatcher" + local http = require "luci.http" + + uci:set("setup_wizard", "settings", "enabled", "0") + uci:save("setup_wizard") + uci:commit("setup_wizard") + adv = disp.build_url("admin", "commotion") + http.redirect(adv) +end + +function start_setup() + local uci = require "luci.model.uci".cursor() + local disp = require "luci.dispatcher" + local http = require "luci.http" + uci:section("wireless", "wifi-iface", "commotionAP", {mode="ap", network="lan"}) + uci:section("wireless", "wifi-iface", "commotionMesh", {mode="adhoc"}) + uci:save("wireless") + uci:commit("wireless") + setup = disp.build_url("commotion", "setup_wizard") + http.redirect(setup) +end + + +function action_changes() + local uci = require "luci.model.uci".cursor() + local changes = uci:changes() + + luci.template.render("commotion/confirm", { + changes = next(changes) and changes + }) +end + +function action_apply() + local path = luci.dispatcher.context.path + local uci = luci.model.uci.cursor() + local changes = uci:changes() + local reload = {} + + -- Collect files to be applied and commit changes + for r, tbl in pairs(changes) do + table.insert(reload, r) + if path[#path] ~= "apply" then + uci:load(r) + uci:commit(r) + uci:unload(r) + end + end + + luci.template.render("commotion/apply", { + changes = next(changes) and changes, + configs = reload}) +end + +function action_revert() + local uci = luci.model.uci.cursor() + local changes = uci:changes() + + -- Collect files to be reverted + for r, tbl in pairs(changes) do + uci:load(r) + uci:revert(r) + uci:unload(r) + end + + luci.template.render("commotion/revert", { + changes = next(changes) and changes + }) +end diff --git a/luasrc/controller/commotion/client_config.lua b/luasrc/controller/commotion/client_config.lua new file mode 100644 index 0000000..b34d2dc --- /dev/null +++ b/luasrc/controller/commotion/client_config.lua @@ -0,0 +1,20 @@ +--[[ +LuCI - Lua Development Framework + +Copyright 2013 - Seamus Tuohy + +With Thanks to the niu suite built by Steven Barth + +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.controller.commotion.client_config" + +function index() + entry({"admin", "commotion", "client"}, alias("admin", "commotion", "client", "welcome_page"), translate("Client Controls"), 30) + entry({"admin", "commotion", "client", "welcome_page"}, cbi("commotion/client_wp", {hideapplybtn=true, hideresetbtn=true}), translate("Welcome Page"), 40).index = true +end diff --git a/luasrc/controller/commotion/commotion.lua b/luasrc/controller/commotion/commotion.lua deleted file mode 100644 index 11bedb5..0000000 --- a/luasrc/controller/commotion/commotion.lua +++ /dev/null @@ -1,45 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - -Copyright 2011 Josh King - -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.controller.commotion.commotion", package.seeall) - -function index() - require("luci.i18n").loadc("commotion") - local i18n = luci.i18n.translate - - local page = node() - page.lock = true - page.target = alias("commotion") - page.subindex = true - page.index = false - - - local root = node() - if not root.lock then - root.target = alias("commotion") - root.index = true - end - - entry({"commotion"}, alias("commotion", "index"), i18n("Commotion"), 10) - entry({"commotion", "index"}, alias("commotion", "index", "index"), i18n("Overview"), 10).index = true - entry({"commotion", "index", "index"}, template("commotion/splash"), i18n("Front Page"), 10).ignoreindex = true - entry({"commotion", "index", "olsr-viz"}, template("olsr-viz/olsr-viz"), _("OLSR-Viz"), 90) - - local config = entry({"admin", "commotion"}, alias("admin", "commotion", "meshconfig"), i18n("Commotion"), 10) - config.sysauth = "root" - config.sysauth_authenticator = "htmlauth" - config.index = true - - entry({"admin", "commotion", "meshconfig"}, cbi("commotion/meshconfig"), i18n("Mesh Configuration (Manual)"), 20) -end - diff --git a/luasrc/controller/commotion/info.lua b/luasrc/controller/commotion/info.lua new file mode 100644 index 0000000..a43c137 --- /dev/null +++ b/luasrc/controller/commotion/info.lua @@ -0,0 +1,21 @@ +--[[ +LuCI - Lua Development Framework + +Copyright 2013 - Seamus Tuohy + +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.controller.commotion.info" + +function index() + entry({"admin", "commotion", "client"}, alias("admin", "commotion", "client", "welcome_page"), translate("Client Controls"), 30) + + entry({"commotion", "about"}, template("commotion/about"), translate("About")).hidden = true + entry({"commotion", "help"}, template("commotion/help"), translate("Help")).hidden = true + entry({"commotion", "license"}, template("commotion/license"), translate("License")).hidden = true +end diff --git a/luasrc/controller/commotion/meshprofile.lua b/luasrc/controller/commotion/meshprofile.lua deleted file mode 100644 index 2dde528..0000000 --- a/luasrc/controller/commotion/meshprofile.lua +++ /dev/null @@ -1,376 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - -Copyright 2011 Josh King -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.controller.commotion.meshprofile", package.seeall) - -require "luci.model.uci" -require "luci.fs" -require "luci.sys" -local fs = require "nixio.fs" - -local profileDir = "/etc/commotion/profiles.d/" - -function index() - require("luci.i18n").loadc("commotion") - local i18n = luci.i18n.translate - - entry({"admin", "commotion", "meshprofile_submit"}, call("ifprocess")) - entry({"admin", "commotion", "meshprofile_down"}, call("down")) - entry({"admin", "commotion", "meshprofile_up"}, call("up")) - entry({"admin", "commotion", "meshprofile"}, call("main"), i18n("Mesh Profile"), 20).dependent=false -end - -function main(ERR, isDuplicate, ul) - local debug = require "luci.commotion.debugger" - debug.log("main started") - if not ERR then - ERR = nil - end - local uci = luci.model.uci.cursor() - local rawProfiles = luci.fs.dir(profileDir) - local available = {} - uci:get_all('network') - uci:foreach('network', 'interface', - function(s) - if s['.name'] and s.profile then - table.insert(available, {s['.name'], s.profile}) - --debug.log(s['.name'] .. " uses " .. s.profile) - end - end) - local profiles = {} - for i,p in ipairs(rawProfiles) do - if not string.find(p, "^%.*$") then - table.insert(profiles, p) - end - end - luci.http.prepare_content("text/html") - luci.template.render("commotion/meshprofile", {available = available, profiles = profiles, ERR = ERR, isDuplicate = isDuplicate, ul = ul}) - -end - -function ifprocess() - local debug = require "luci.commotion.debugger" - debug.log("Processing profile application...") - local error = nil - local values = luci.http.formvalue() - local tif = values["interfaces"] - local p = values["profiles"] - local uci = luci.model.uci.cursor() - debug.log("Applying " .. p .. " to " .. tif) - old_prof = uci:get('network', tif, "profile") - local wireless = false - uci:foreach('wireless','wifi-iface', - function(iface) - if iface.network == tif then - wireless = true - end - end - ) - if wireless then - error = flush_wireless_profile(old_prof, p, tif) - end - uci:set('network', tif, "profile", p) - uci:commit('network') - uci:save('network') - if error ~= nil then - main(error) - else - finish() - end -end - - -function finish() - luci.template.render("QS/module/applyreboot", {redirect_location=("http://"..luci.http.getenv("SERVER_NAME").."/cgi-bin/luci/admin/commotion/meshprofile")}) - luci.http.close() - --luci.sys.call("/etc/init.d/commotiond restart") - --luci.sys.call("sleep 2; /etc/init.d/network restart") - --In order to ensure that everything works cleanly a restart is required - p = luci.sys.reboot() - return({'complete'}) -end - -function checkFile(file) - local debug = require "luci.commotion.debugger" - --[=[ - Checks the uploaded profile to ensure the required settings are there. - --]=] - debug.log("file check started") - local error = nil - if luci.fs.isfile(file) then - --required fields for a commotion profile - required = { - "ip", - "ipgenerate", - "netmask", - "dns", - "type", - "mode"} - --Parse uploaded file for settings - fields = {} - for line in io.lines(file) do - setting = line:split("=") - if setting[2] and setting[1] ~= "" and setting[1] ~= nil then - table.insert(fields, setting[1]) - end - end - --on line 113, the if statement that if the file is empty does not have an else statement that says (if the file is empty, pass an error) - if fields ~= {} then - --Check to see if there are missing fields - missing = {} - for _,x in pairs(required) do - contained = nil - for _,m in pairs(fields) do - if x == m then - contained = 1 - end - end - if not contained then - table.insert(missing, x) - debug.log("Field "..x.." is missing.") - end - end - end - --If missing fields create error - if next(missing) ~= nil then - misStr = table.concat(missing, ", ") - error = "Your uploaded profile seem incomplete. You are missing at LEAST the values for "..misStr..". Please edit your profile and re-upload." - --remove file because it is BAD - removed = luci.sys.call('rm ' .. file) - else - debug.log("Profile seems to be correctly formatted.") - end - else - error = "There does not seem to be a file here..." - debug.log("File is missing") - end - if error then - return error - else - return nil - end - return nil -end - -function string:split(sep) - local sep, fields = sep or ":", {} - local pattern = string.format("([^%s]+)", sep) - self:gsub(pattern, function(c) fields[#fields+1] = c end) - return fields -end - - -function up() - --[=[calls the file uploader and checks if the file is a correct config. - --]=] - local debug = require "luci.commotion.debugger" - debug.log("up started") - local error = nil - local isDuplicate = false - setFileHandler("/tmp/", "config") - local values = luci.http.formvalue() - - local overwrite = values["overwrite"] - - local ul = values["config"] - - --TODO add logging to checkfile to identify why it does not work - file = "/tmp/" .. ul - error = checkFile(file) - - if error ~= nil then - main(error) - - else - - -- check for extant profiles with the same name - iterator, is_match = fs.glob("/etc/commotion/profiles.d/" .. ul) - - log(overwrite) - - -- if there is a conflict, inform the user - if is_match > 0 then - if overwrite == "yes" then - result = fs.copy("/tmp/" .. ul, "/etc/commotion/profiles.d/" .. ul) - main(nil) - end - if overwrite == "no" then - main(nil) - end - if overwrite == nil then - error = "There is a conflict: The profile "..ul.." already exists." - - isDuplicate = true - - main(error, isDuplicate, ul) - - log("There is a conflict: The profile "..ul.." already exists.") - end - -- if no conflict exists, copy the new profile to etc/commotion/profiles.d - else - result = fs.copy("/tmp/" .. ul, "/etc/commotion/profiles.d/" .. ul) - main(nil) - - end - - end -end - -function down() - local values = luci.http.formvalue() - local dl = values["dl_profile"] - if dl ~= '' then - download(dl) - end - main() -end - -function download(filename) - --TODO remove the luci.http.status calls and replace them with calls to main(error) with the appropriate text to inform the user of why they cannot download it. - debug.log("download started") - -- no file name provided - if not filename then - luci.http.status(403, "Forbidden") - return - end - -- no relative paths with backrefs - if filename:find("%.%.") then - luci.http.status(403, "Access denied") - return - end - -- no absolute paths - if # filename > 0 and filename:sub(1,1) == '/' then - luci.http.status(403, "Access denied") - return - end - local f = io.open(profileDir .. filename) - -- file does not exist - if not f then - luci.http.status(403, "Access denied") - return - end - -- send it - luci.http.prepare_content("application/force-download") - luci.http.header("Content-Disposition", "attachment; filename=" .. filename) - luci.ltn12.pump.all(luci.ltn12.source.file(f), luci.http.write) - - io.close(f) -end - -function setFileHandler(location, input_name, file_name) - --[=[Uploads a file to a specified location, and possible file name. - - Use: - add a call to this function within the index entry function called by an submit button on a luci page. - eg. - function index() - entry({"admin", "commotion", "submit_clicked"}, call("start_upload")) - end - function start_upload() - setFileHandler("/tmp/", "image", "tmp_image.jpg") - local values = luci.http.formvalue() - local dl = values["image"] reload_page() - end - - Inputs: - location: (string) The full path to where the file should be saved. - input_name: (string) The name specified by the input html field. - file_name: (string, optional) The optional name you would like the file to be saved as. If left blank the file keeps its uploaded name. - - --]=] - local debug = require "luci.commotion.debugger" - local sys = require "luci.sys" - local fs = require "luci.fs" - local configLoc = location - local fp - luci.http.setfilehandler( - function(meta, chunk, eof) - if not fp then - complete = nil - if meta and meta.name == input_name then - if file_name ~= nil then - debug.log("starting download") - fp = io.open(configLoc .. file_name, "w") - else - debug.log("starting download") - fp = io.open(configLoc .. meta.file, "w") - end - else - debug.log("file not of specified input type (input name variable)") - end - end - if chunk then - fp:write(chunk) - end - if eof then - fp:close() - debug.log("file downloaded") - end - end) -end - - -function flush_wireless_profile(old_profile, new_profile, interface) - --TODO need a userspace warning that channel settings will not take effect and need to be done in the settings page. - local debug = require "luci.commotion.debugger" - local uci = luci.model.uci.cursor() - local found = nil - local old_dev = nil - local name = nil - local error = nil - settings = get_commotion_settings(new_profile) - uci:foreach("wireless", "wifi-iface", - function(s) - if s['.name'] == old_profile then - found = true - old_dev = s.device - name = s['.name'] - elseif s['.name'] == new_profile then - error = luci.i18n.translate("Each profile must have a seperate name. Please try with a unique profile.") - elseif s['.name'] ~= old_profile and s.network == interface then - error = luci.i18n.translate("You have multiple wireless interfaces on a single network interface. This is not allowed.") - end - end) - --debug.log(tostring(conflict).." is the conflict level") - if error ~= nil then - return error - else - uci:delete("wireless", name) - uci:section('wireless', 'wifi-iface', new_profile, - {device=old_dev, - network=interface, - ssid=settings['ssid'], - mode=settings['mode']}) - uci:save("wireless") - uci:commit("wireless") - end -end - -function get_commotion_settings(file) - settings = {} - for line in io.lines("/etc/commotion/profiles.d/"..file) do - setting = line:split("=") - if setting[1] ~= nil and setting[2] ~= nil then - settings[setting[1]] = setting[2] - end - end - if next(settings) then - return settings - end -end - -function string:split(sep) - local sep, fields = sep or ":", {} - local pattern = string.format("([^%s]+)", sep) - self:gsub(pattern, function(c) fields[#fields+1] = c end) - return fields -end diff --git a/luasrc/controller/commotion/security_config.lua b/luasrc/controller/commotion/security_config.lua new file mode 100644 index 0000000..82cedf9 --- /dev/null +++ b/luasrc/controller/commotion/security_config.lua @@ -0,0 +1,27 @@ +--[[ + Copyright (C) 2013 Seamus Tuohy + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +]]-- + +module "luci.controller.commotion.security_config" + +function index() + entry({"admin", "commotion", "security"}, alias("admin", "commotion", "security", "passwords"), translate("Security"), 40).index = true + + entry({"admin", "commotion", "security", "passwords"}, cbi("commotion/security_pass", {hideapplybtn=true, hideresetbtn=true}), translate("Passwords"), 50).subsection=true + + entry({"admin", "commotion", "security", "shared_mesh_keychain"}, cbi("commotion/security_smk", {hideapplybtn=true, hideresetbtn=true}), translate("Shared Mesh Keychain"), 60).subsection=true +end + diff --git a/luasrc/controller/commotion/serval_keyring.lua b/luasrc/controller/commotion/serval_keyring.lua deleted file mode 100644 index cefa8ae..0000000 --- a/luasrc/controller/commotion/serval_keyring.lua +++ /dev/null @@ -1,163 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - -Copyright 2011 Josh King - -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.controller.commotion.serval_keyring", package.seeall) - -local key_file = "/etc/commotion/keys.d/mdp/" - -function index() - require("luci.i18n").loadc("commotion") - local i18n = luci.i18n.translate - - entry({"admin", "commotion", "serval_keyring_new"}, call("new_keyring")) - entry({"admin", "commotion", "serval_keyring_down"}, call("down")) - entry({"admin", "commotion", "serval_keyring_up"}, call("up")) - entry({"admin", "commotion", "serval_keyring"}, call("main"), "Serval Keyring", 20) -end - -function main(Err) - if not ERR then - ERR = nil - end - luci.http.prepare_content("text/html") - luci.template.render("commotion/serval_keyring", {Err = Err}) -end - -function new_keyring() - local debug = require "luci.commotion.debugger" - debug.log("Creating New Keyring...") - local values = luci.http.formvalue() - local new = values["new_keyring"] - local rm = luci.sys.call("rm "..key_file.."serval.keyring") - --Define the various serval code to run - local s_path = "SERVALINSTANCE_PATH=" - local s_start = s_path..key_file.." servald start" - local s_stop = s_path..key_file.." servald stop" - --local s_add_key = s_path..key_file.." servald keyring add" - --local s_list_key = s_path..key_file.." servald keyring list" - local AND = " && " - --Run the actual serval command to create a new keyring & key - local new_key = luci.sys.call(s_start..AND..s_stop) - --debug.log(luci.sys.exec(s_list_key)) - --If no errors occured in sys calls - if rm ~= 1 and new_key ~= 1 then - finish() - else - main("Serval process failed") - end -end - -function finish() - --TODO What kind of cleanup/setup do we need to do? - local olsrd = luci.sys.call("/etc/init.d/olsrd restart") - if olsrd == 0 then - main() - else - main("olsrd failed to restart") - end -end - ----calls the file uploader and checks if the file is a correct config. -function up() - local debug = require "luci.commotion.debugger" - debug.log("uploader started") - local error = nil - setFileHandler("/tmp/", "upload", "serval.keyring") - --debug.log(luci.sys.exec("md5sum /tmp/serval.keyring")) - local values = luci.http.formvalue() - local ul = values["upload"] - if ul ~= '' and ul ~= nil then - debug.log("checking file") - error = checkFile("/tmp/serval.keyring") - end - --remove file if errors, copy it to correct directory and finish if a keyring - if error ~= nil then - debug.log("error found") - local rm = luci.sys.call("rm /tmp/serval.keyring") - main(error) - else - local rm = luci.sys.call("rm "..key_file.."serval.keyring") - local cp = luci.sys.call("cp /tmp/serval.keyring "..key_file..".") - finish() - end -end - -function checkFile(file) - local keyring = luci.sys.exec("SERVALINSTANCE_PATH=/tmp/ servald keyring list") - local key = string.match(keyring, "^(%w*):%w*:") - if key == nil or string.len(key) ~= 64 then - return "The file supplied is not a proper keyring, or is password protected. Please upload another key." - end -end - - -function down() - local values = luci.http.formvalue() - download(key_file.."serval.keyring") - main() -end - -function download(filename) - local debug = require "luci.commotion.debugger" - --TODO remove the luci.http.status calls and replace them with calls to main(error) with the appropriate text to inform the user of why they cannot download it. - debug.log("download started") - local f = io.open(filename) - -- file does not exist - if not f then - debug.log("File Does Not Exist") - luci.http.status(403, "Access denied") - return - end - -- send it - luci.http.prepare_content("application/force-download") - luci.http.header("Content-Disposition", "attachment; filename=serval.keyring") - luci.ltn12.pump.all(luci.ltn12.source.file(f), luci.http.write) - io.close(f) -end - - ----Uploads a file to a specified location, and possible file name. ---@param location: (string) The full path to where the file should be saved. ---@param input_name: (string) The name specified by the input html field. ---@param file_name (string, optional) The optional name you would like the file to be saved as. If left blank the file keeps its uploaded name. -function setFileHandler(location, input_name, file_name) - local debug = require "luci.commotion.debugger" - local sys = require "luci.sys" - local fs = require "luci.fs" - local configLoc = location - local fp - luci.http.setfilehandler( - function(meta, chunk, eof) - if not fp then - complete = nil - if meta and meta.name == input_name then - if file_name ~= nil then - debug.log("starting download") - fp = io.open(configLoc .. file_name, "w") - else - debug.log("starting download") - fp = io.open(configLoc .. meta.file, "w") - end - else - debug.log("file not of specified input type (input name variable)") - end - end - if chunk then - fp:write(chunk) - end - if eof then - fp:close() - debug.log("file downloaded") - end - end) -end diff --git a/luasrc/controller/commotion/setup_ctl.lua b/luasrc/controller/commotion/setup_ctl.lua new file mode 100644 index 0000000..eb7ced6 --- /dev/null +++ b/luasrc/controller/commotion/setup_ctl.lua @@ -0,0 +1,82 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2010 Jo-Philipp Wich + +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: servicectl.lua 9558 2012-12-18 13:58:22Z jow $ +]]-- + +module("luci.controller.commotion.setup_ctl", package.seeall) + +function index() + SW = require "luci.commotion.setup_wizard" + if SW.status() then + entry({"setupctl"}, alias("setupctl", "status")) + else + entry({"setupctl"}, alias("setupctl", "status")).sysauth = "root" + end + entry({"setupctl", "status"}, call("action_status")).leaf = true + entry({"setupctl", "restart"}, call("action_restart")).leaf = true + entry({"setupctl", "complete"}, call("action_setup")).leaf = true +end + +function action_status() + local data = nixio.fs.readfile("/var/run/luci-reload-status") + if data then + luci.http.write("/etc/config/") + luci.http.write(data) + else + luci.http.write("finish") + end +end + +function action_restart(args) + local uci = require "luci.model.uci".cursor() + if args then + local service + local services = { } + + for service in args:gmatch("[%w_-]+") do + services[#services+1] = service + end + + local command = uci:apply(services, true) + if nixio.fork() == 0 then + local i = nixio.open("/dev/null", "r") + local o = nixio.open("/dev/null", "w") + + nixio.dup(i, nixio.stdin) + nixio.dup(o, nixio.stdout) + + i:close() + o:close() + + nixio.exec("/bin/sh", unpack(command)) + else + luci.http.write("OK") + os.exit(0) + end + end +end + +function action_setup() + local disp = require "luci.dispatcher" + local http = require "luci.http" + local uci = require "luci.model.uci".cursor() + local SW = require "luci.commotion.setup_wizard" + + if SW.status() then + uci:set("setup_wizard", "settings", "enabled", "0") + uci:set("setup_wizard", "passwords", "admin_pass", 'false') + uci:save("setup_wizard") + uci:commit("setup_wizard") + end + adv = disp.build_url("admin", "commotion") + http.redirect(adv) +end diff --git a/luasrc/controller/commotion/status_config.lua b/luasrc/controller/commotion/status_config.lua new file mode 100644 index 0000000..32f6451 --- /dev/null +++ b/luasrc/controller/commotion/status_config.lua @@ -0,0 +1,389 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local util = require "luci.util" +local db = require "luci.commotion.debugger" +local i18n = require "luci.i18n" + +module ("luci.controller.commotion.status_config", package.seeall) + +function index() + local sys = require "luci.sys" + + entry({"admin", "commotion", "status"}, alias("admin", "commotion", "status", "nearby_md"), translate("Status"), 10) + entry({"admin", "commotion", "status", "nearby_md"}, call("action_neigh")).hidden = true + entry({"admin", "commotion", "status", "mesh_viz"}, call("viz")).hidden = true + entry({"admin", "commotion", "status", "conn_clnts"}, call("conn_clnts")).hidden = true + if sys.exec("opkg list-installed | grep luci-commotion-debug") then + entry({"admin", "commotion", "status", "dbg_rpt"}, call("dbg_rpt")).hidden = true + end +end + + +function fallback_splash_info() + local cnet = require "luci.commotion.network" + local sys = require "luci.sys" + local clients = dhcp_lease_fallback() + local cif = {} + local ifaces = cnet.ifaces_list() + if #clients > 0 then + local arp = sys.net.arptable() + for _,addr in ipairs(arp) do + if not cif[addr.Device] then + cif[addr.Device] = {} + cif[addr.Device].connected = 0 + end + for client,dossier in ipairs(clients) do + if dossier.mac == addr["HW address"] then + cif[addr.Device].connected = cif[addr.Device].connected + 1 + end + end + end + end + return cif +end + + +function status_builder(page, assets, active_tab) + local uci = require "luci.model.uci".cursor() + local cnet = require "luci.commotion.network" + local sys = require "luci.sys" + local ifaces = {} + local internet = nil + local gw = false + local splash_info = nil + local zone_iface = cnet.ifaces_list() + if sys.call("/etc/init.d/nodogsplash enabled") ~= 0 then + splash_info = fallback_splash_info() + else + splash_info = get_splash_iface_info() + end + + uci:foreach("wireless", "wifi-iface", + function(s) + local name = s['.name'] + local device = s.device + local status = nil + local sec = nil + local conn = nil + local zone = zone_iface[s.network] + if device ~= nil then + if uci:get("wireless", "wifi-device", device, "disabled") == '1' then + status = "Off" + else + status = "On" + end + else + status = "Unknown" + end + if s.encryption == "psk2" then + sec = "Secured" + else + sec = "Unsecured" + end + + for i,x in pairs(splash_info) do + if zone == i then + conn = splash_info[zone].connected + end + end + if name then + table.insert(ifaces, {name=name, + status=status, + sec=sec or "Unsecured", + conn=conn or "0"}) + end + end) + for line in util.execi("route -n") do + string.gsub(line, "^0%.0%.0%.0[%s]+(%d+%.%d+)%.%d+%.%d+[%s].+eth0$", + function(x) + gw = true + if string.match(x, "^100%.64$") or string.match(x, "10%.%d+$") then + internet = "No" + end + end) + end + if gw == true and internet == nil then + internet = "Yes" + elseif internet == nil then + internet = "No" + end + luci.template.render("commotion/status", {ifaces=ifaces, gateway_provided=internet, page=page, assets=assets, active_tab=active_tab}) +end + +function viz() + status_builder("commotion/viz", nil, "mesh_visualizer") +end + +function conn_clnts() + clients, warning = get_client_splash_info() + local ifaces = {} + for i in util.execi("ifconfig -a") do + string.gsub(i, "^([%S].-)[%s]", + function(x) table.insert(ifaces, x) end) + end + status_builder("commotion/conn_clients", {clients=clients, ifaces=ifaces, warning=warning}, "connected_clients") +end + +--! @brief currently only gets number of connected clients... because that is what I needed +function get_splash_iface_info() + local splash = {} + local interface = nil + for line in util.execi("ndsctl status") do + string.gsub(line, "^(.-:%s.*)$", + function(str) + local sstr = util.split(str, ":%s", nil, true) + local key = sstr[1] + local val = sstr[2] + if key == "Managed interface" then + interface = val + splash[interface] = {} + end + if key == "Current clients" then + splash[interface].connected = val + end + end) + end + return splash +end + + +function dhcp_lease_fallback() + clients = {} + local i = 1 + for line in io.lines("/tmp/dhcp.leases") do + clients[i] = {} + clients[i].mac = string.match(line, "^[%d]*%s([%x%:]+)%s") + clients[i].ip = string.match(line, "^[%d]*%s[%x%:]+%s([%d%.]+)%s") + clients[i].curr_conn = "No" + i = i + 1 + end + return clients, true +end + +function get_client_splash_info() + local sys = require "luci.sys" + if sys.call("/etc/init.d/nodogsplash enabled") ~= 0 then + return dhcp_lease_fallback() + end + local convert = function(x) + return tostring(math.floor(tonumber(x)/60)).." "..i18n.translate("minutes") + end + local function total_kB(a, b) return tostring(math.floor(a+b)).." kByte" end + local function total_kb(a, b) return tostring(math.floor(a+b)).." kbit/s" end + local clients = {} + i = 0 + for line in util.execi("ndsctl clients") do + if string.match(line, "^client_id.*$") then + i = i + 1 + clients[i] = {} + end + string.gsub(line, "^(.+=.+)$", + function(str) + local sstr = util.split(str, "=") + local key = sstr[1] + local val = sstr[2] + clients[i][key] = val + end) + end + for i,x in ipairs(clients) do + if clients[i] ~= nil then + clients[i].curr_conn = "No" + clients[i].duration = convert(clients[i].duration) + clients[i].bnd_wdth = total_kB(clients[i].downloaded, clients[i].uploaded) + clients[i].avg_spd = total_kb(clients[i].avg_down_speed, clients[i].avg_up_speed) + end + end + return clients +end + + +function dbg_rpt() + status_builder("commotion/debug", nil, "debug_report") +end + +function action_neigh(json) + local data = fetch_txtinfo("links") + if not data or not data.Links then + status_builder("commotion/error_olsr", nil, "nearby_devices") + return nil + end + status_builder("commotion/nearby_md", {links=data.Links}, "nearby_devices") +end + +local function compare_links(a, b) + local c = tonumber(a.Cost) + local d = tonumber(b.Cost) + + if not c or c == 0 then + return false + end + + if not d or d == 0 then + return true + end + return c < d +end + +-- Internal +function fetch_txtinfo(otable) + require("luci.sys") + local uci = require "luci.model.uci".cursor_state() + local resolve = uci:get("luci_olsr", "general", "resolve") + otable = otable or "" + local rawdata = luci.sys.httpget("http://127.0.0.1:2006/"..otable) + local rawdatav6 = luci.sys.httpget("http://[::1]:2006/"..otable) + local data = {} + local dataindex = 0 + local name = "" + local defaultgw + + if #rawdata ~= 0 then + local tables = luci.util.split(luci.util.trim(rawdata), "\r?\n\r?\n", nil, true) + + if otable == "links" then + local route = {} + luci.sys.net.routes(function(r) if r.dest:prefix() == 0 then defaultgw = r.gateway:string() end end) + end + + for i, tbl in ipairs(tables) do + local lines = luci.util.split(tbl, "\r?\n", nil, true) + name = table.remove(lines, 1):sub(8) + local keys = luci.util.split(table.remove(lines, 1), "\t") + local split = #keys - 1 + if not data[name] then + data[name] = {} + end + + for j, line in ipairs(lines) do + dataindex = ( dataindex + 1 ) + di = dataindex + local fields = luci.util.split(line, "\t", split) + data[name][di] = {} + for k, key in pairs(keys) do + if key == "Remote IP" or key == "Dest. IP" or key == "Gateway IP" or key == "Gateway" then + data[name][di][key] = fields[k] + if resolve == "1" then + hostname = nixio.getnameinfo(fields[k], "inet") + if hostname then + data[name][di]["Hostname"] = hostname + end + end + if key == "Remote IP" and defaultgw then + if defaultgw == fields[k] then + data[name][di]["defaultgw"] = 1 + end + end + elseif key == "Local IP" then + data[name][di][key] = fields[k] + data[name][di]['Local Device'] = fields[k] + uci:foreach("network", "interface", + function(s) + localip = string.gsub(fields[k], ' ', '') + if s.ipaddr == localip then + data[name][di]['Local Device'] = s['.name'] or interface + end + end) + elseif key == "Interface" then + data[name][di][key] = fields[k] + uci:foreach("network", "interface", + function(s) + interface = string.gsub(fields[k], ' ', '') + if s.ifname == interface then + data[name][di][key] = s['.name'] or interface + end + end) + else + data[name][di][key] = fields[k] + end + end + if data[name][di].Linkcost then + data[name][di].LinkQuality, + data[name][di].NLQ, + data[name][di].ETX = + data[name][di].Linkcost:match("([%w.]+)/([%w.]+)[%s]+([%w.]+)") + end + end + end + end + + if #rawdatav6 ~= 0 then + local tables = luci.util.split(luci.util.trim(rawdatav6), "\r?\n\r?\n", nil, true) + for i, tbl in ipairs(tables) do + local lines = luci.util.split(tbl, "\r?\n", nil, true) + name = table.remove(lines, 1):sub(8) + local keys = luci.util.split(table.remove(lines, 1), "\t") + local split = #keys - 1 + if not data[name] then + data[name] = {} + end + for j, line in ipairs(lines) do + dataindex = ( dataindex + 1 ) + di = dataindex + local fields = luci.util.split(line, "\t", split) + data[name][di] = {} + for k, key in pairs(keys) do + if key == "Remote IP" then + data[name][di][key] = "[" .. fields[k] .. "]" + if resolve == "1" then + hostname = nixio.getnameinfo(fields[k], "inet6") + if hostname then + data[name][di]["Hostname"] = hostname + end + end + elseif key == "Local IP" then + data[name][di][key] = fields[k] + data[name][di]['Local Device'] = fields[k] + uci:foreach("network", "interface", + function(s) + local localip = string.gsub(fields[k], ' ', ''):upper() + if s.ip6addr then + s.ip6addr = luci.ip.IPv6(s.ip6addr):string() + local ip6addr = string.gsub(s.ip6addr, '\/.*', '') + if ip6addr == localip then + data[name][di]['Local Device'] = s['.name'] or s.interface + end + end + end) + elseif key == "Dest. IP" then + data[name][di][key] = "[" .. fields[k] .. "]" + elseif key == "Last hop IP" then + data[name][di][key] = "[" .. fields[k] .. "]" + elseif key == "IP address" then + data[name][di][key] = "[" .. fields[k] .. "]" + elseif key == "Gateway" then + data[name][di][key] = "[" .. fields[k] .. "]" + else + data[name][di][key] = fields[k] + end + end + + if data[name][di].Linkcost then + data[name][di].LinkQuality, + data[name][di].NLQ, + data[name][di].ETX = + data[name][di].Linkcost:match("([%w.]+)/([%w.]+)[%s]+([%w.]+)") + end + end + end + end + + + if data then + return data + end +end diff --git a/luasrc/model/cbi/commotion/basic_ani.lua b/luasrc/model/cbi/commotion/basic_ani.lua new file mode 100644 index 0000000..1a1cdce --- /dev/null +++ b/luasrc/model/cbi/commotion/basic_ani.lua @@ -0,0 +1,92 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- +local uci = require "luci.model.uci".cursor() +local utils = require "luci.util" +local cnw = require "luci.commotion.network" +local db = require "luci.commotion.debugger" +local http = require "luci.http" +local SW = require "luci.commotion.setup_wizard" +local ccbi = require "luci.commotion.ccbi" + +m = Map("network", translate("Internet Gateway"), translate("If desired, you can configure your gateway interface here.")) + +--redirect on saved and changed to check changes. +if not SW.status() then + m.on_after_save = ccbi.conf_page +end + +p = m:section(NamedSection, "wired") +p.anonymous = true + +config = p:option(ListValue, "dhcp", translate("Gateway Configuration")) +config:value("auto", translate("Automatically configure gateway on boot.")) +config:value("client", translate("This device should ALWAYS try and acquire a DHCP lease.")) +config:value("server", translate("This device should ALWAYS provide DHCP leases to clients.")) +config:value("none", translate("This device should not do anything with DHCP.")) + +msh = p:option(Flag, "meshed", translate("Will you be meshing with other Commotion devices over the ethernet interface?")) +msh.enabled = "true" +msh.disabled = "false" +msh.default = "false" +msh.addremove = false +msh.write = ccbi.flag_write + +ance = p:option(Flag, "_gateway", translate("Advertise your gateway to the mesh.")) +ance.addremove = true + +function dyn_exists() + local cvalue = nil + uci:foreach("olsrd", "LoadPlugin", + function(p) + if string.match(p.library, "^olsrd_dyn_gw_plain.*") then + cvalue = p[".name"] + end + end + ) + return cvalue +end + +function ance.cfgvalue(self, section) + if dyn_exists() ~= nil then + return '1' + else + return '0' + end +end + +function ance.write(self, section, fvalue) + if dyn_exists() == nil then + self.section.changed = true + --! @TODO Make this actually check the installed version and not just use 0.4. + uci:section("olsrd", "LoadPlugin", nil, {library="olsrd_dyn_gw_plain.so.0.4"}) + uci:save("olsrd") + return true + end +end + +function ance.remove(self, section) + local cvalue = dyn_exists() + if cvalue ~= nil then + self.section.changed = true + uci:delete("olsrd", cvalue) + uci:save("olsrd") + return true + end +end + + +return m diff --git a/luasrc/model/cbi/commotion/basic_done.lua b/luasrc/model/cbi/commotion/basic_done.lua new file mode 100644 index 0000000..55d9573 --- /dev/null +++ b/luasrc/model/cbi/commotion/basic_done.lua @@ -0,0 +1,25 @@ +--[[ + Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +m = Map("system", translate("Basic Configuration Complete!"), translate("You have completed all of the required steps to configure this mesh node.")) +m.skip_to_end = true + +s = m:section(SimpleSection, "stuff", translate("If you would like to configure additional network interfaces on this node, click the next button to continue to Additional Network Settings below. Otherwise click the Finish button.")) +s.anonymous = true +s.title = nil + +return m diff --git a/luasrc/model/cbi/commotion/basic_mn.lua b/luasrc/model/cbi/commotion/basic_mn.lua new file mode 100644 index 0000000..d3ffb99 --- /dev/null +++ b/luasrc/model/cbi/commotion/basic_mn.lua @@ -0,0 +1,272 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local uci = require "luci.model.uci".cursor() +local utils = require "luci.util" +local cnw = require "luci.commotion.network" +local db = require "luci.commotion.debugger" +local http = require "luci.http" +local SW = require "luci.commotion.setup_wizard" +local ccbi = require "luci.commotion.ccbi" + + +local m = Map("wireless", translate("Network Settings"), translate("Every Commotion node must have one mesh network connection or interface. Commotion can mesh over wireless or wired interfaces.")) + +--redirect on saved and changed to check changes. +if not SW.status() then + m.on_after_save = ccbi.conf_page +end + +s = m:section(TypedSection, "wifi-iface") +s.optional = false +s.anonymous = true + +if not SW.status() then --if not setup wizard then allow for adding and removal + s.addremove = true + + md = s:option(Value, "mode") + md.default = 'adhoc' + md.render = function() end + function md:parse(section) + local cvalue = self:cfgvalue(section) + if not cvalue then + self:write(section, self.default) + end + end + function s.remove(self, section) + m.changed = true + clean_network(name:formvalue(section)) + return self.map:del(section) + end +end + + +function s:filter(section) + mode = self.map:get(section, "mode") + return mode == "adhoc" or mode == nil +end + +s.valuefooter = "cbi/full_valuefooter" +s.template_addremove = "cbi/commotion/addMesh" --This template controls the addremove form for adding a new access point so that it has better wording. + +name = s:option(Value, "ssid", translate("Mesh Network Name"), translate("Commotion networks share a network-wide name. This must be the same across all devices on the same mesh. This name cannot be greater than 15 characters.")) +name.default = "Commotion" +name.datatype = "maxlength(15)" + +nwk = s:option(Value, "network") +--! @brief checks for invalid ssid values and rejects the name it they exist. +function nwk.datatype(val) + if val and val:match("\"%<%>%'%&") then + return true + else + return false + end +end + + --! @brief creates a network section and same named commotion profile when creating a mesh interface and assigns it to that mesh interface +function write_network(value) + local net_name = string.gsub(value, "[%p%s%z]", "_") + network_name = uci:section("network", "interface", net_name, {proto="commotion", class='mesh'}) + cnw.commotion_set(network_name) + uci:set("network", network_name, "profile", network_name) + uci:save("network") + if value ~= nil then + uci:foreach("firewall", "zone", + function(s) + if s.name and s.name == "mesh" then + local list = {net_name} + for _,x in ipairs(s.network) do + table.insert(list, x) + end + uci:set_list ("firewall", s[".name"], "network", list) + uci:save("firewall") + end + end + ) + return net_name + end +end + +function clean_network(value) + local fs = require "luci.fs" + local net_name = string.gsub(value, "[%p%s%z]", "_") + uci:delete("network", net_name) + fs.unlink("/etc/commotion/profiles.d/"..net_name) + uci:save("network") + if value ~= nil then + uci:foreach("firewall", "zone", + function(s) + if s.name and s.name == "mesh" then + local list = {} + for _,x in ipairs(s.network) do + if x ~= net_name then + table.insert(list, x) + end + end + uci:set_list ("firewall", s[".name"], "network", list) + uci:save("firewall") + end + end + ) + return net_name + end +end + +function check_name(self, section, value) + local uci = require "luci.model.uci".cursor() + local clean_name = string.gsub(value, "[%p%s%z]", "_") + local exist = uci:get("network", clean_name) + if exist ~= nil then + local current = self.map:get(section, "network") + if current == clean_name then + return true + else + m.message = "You cannot have multiple interfaces with the same name." + m.state = -1 + name:add_error(section, nil, "This section is named the same as an existing interface.") + db.log("errors set because of existing network") + return nil + end + else + return true + end +end + +nwk.render = function() end +function nwk:parse(section) + db.log("parsing network") + local cvalue = self:cfgvalue(section) + local name = name:formvalue(section) + if name ~= nil and not cvalue then + if check_name(self, section, name) ~= nil then + local net_name = write_network(name) + uci:set("wireless", section, "network", net_name) + uci:save("wireless") + else + db.log("failed to write the network.") + end + else + db.log("Already set or a nil value.") + end +end + +local wifi_dev = {} +uci.foreach("wireless", "wifi-device", + function(s) + local name = s[".name"] + local mode = s.hwmode + table.insert(wifi_dev, {name, mode}) + end +) + +--Check for more than one radio, and if not don't offer to change radio's. +if #wifi_dev > 1 then + radios = s:option(ListValue, "device", translate("wifi-device"), translate("The Setup Wizard has detected all of the network interfaces on this device. Select the network interface that will connect to the mesh.")) + for _,dev in ipairs(wifi_dev) do + local freq = cnw.get_channels(dev[2], true) + radios:value(dev[1], dev[1].." "..freq) + local channels = s:option(ListValue, "channel_"..dev[1], translate("Channel"), translate("The channel of this wireless interface.")) + channels:depends("device", dev[1]) + --adds the values to the list based on frequency + for _,x in pairs((cnw.get_channels(dev[2]))) do + channels:value(x[1], x[2]) + end + channels.default = uci:get("wireless", dev[1], "channel") + function channels:write(section, value) + local enable = self.map:set(dev[1], "disabled", "0") -- enable the radio + local set_chan = self.map:set(dev[1], "channel", value) + return set_chan and enable or false + end + end +else + + local channels = s:option(ListValue, "channel", translate("Channel"), translate("The channel of your wireless interface.")) + channels.default = uci:get("wireless", wifi_dev[1][1], "channel") + function channels.write(self, section, value) + local enable = self.map:set(wifi_dev[1][1], "disabled", "0") -- enable the radio + self.map:set(section, "device", wifi_dev[1][1]) --set iface to use this device. + return self.map:set(wifi_dev[1][1], "channel", value) + end + for _,x in pairs(cnw.get_channels(wifi_dev[1][2])) do + channels:value(x[1], x[2]) + end +end + +enc = s:option(Flag, "encryption", translate("Mesh Encryption"), translate("Choose whether or not to encrypt data sent between mesh devices for added security.")) +enc.disabled = "none" +enc.enabled = "psk2" +enc.rmempty = false +enc.default = "none" --default must == disabled value for rmempty to work + +enc.write = ccbi.flag_write +--Have enc flag also remove the encryption key when deleted +function enc.remove(self, section) + value = self.map:get(section, self.option) + if value ~= self.disabled then + local key = self.map:del(section, "key") + local enc = self.map:del(section, self.option) + self.section.changed = true + return key and enc or false + end +end + + + +--dummy value set to not reveal password +pw1 = s:option(Value, "_pw1", translate("Mesh Encryption Password"), translate("To encrypt data between devices, each device must share a common mesh encryption password.")) +pw1.password = true +pw1:depends("encryption", "psk2") +pw1.datatype = "wpakey" + +--password should write to the key, not to the dummy value +function pw1.write(self, section, value) + return self.map:set(section, "key", value) +end + +pw2 = s:option(Value, "_dummy", translate("Confirmation")) +pw2.password = true +pw2:depends("encryption", "psk2") + +--Don't actually write this value, just return success +function pw2.write(self, section, value) + return true +end + +--make sure passwords are equal +function pw1.validate(self, value, section) + local v1 = value + local v2 = pw2:formvalue(section) + --local v2 = http.formvalue(string.gsub(self:cbid(section), "%d$", "2")) + if v1 and v2 and #v1 > 0 and #v2 > 0 then + if v1 == v2 then + if m.message == nil then + m.message = translate("Password successfully changed!") + end + return value + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Given password confirmation did not match, password not changed!")) + return nil + end + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Unknown Error, password not changed!")) + return nil + end +end + +return m diff --git a/luasrc/model/cbi/commotion/basic_ns.lua b/luasrc/model/cbi/commotion/basic_ns.lua new file mode 100644 index 0000000..4d8507b --- /dev/null +++ b/luasrc/model/cbi/commotion/basic_ns.lua @@ -0,0 +1,172 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local SW = require "luci.commotion.setup_wizard" +local db = require "luci.commotion.debugger" +local uci = require "luci.model.uci".cursor() +local cnet = require "luci.commotion.network" +local http = require "luci.http" +local ccbi = require "luci.commotion.ccbi" + +--Main title and system config map for hostname value +local m = Map("system", translate("Node Settings"), translate("In this section you'll set the basic required settings for this device, and the basic network settings required to connect this device to a Commotion Mesh network. You will be prompted to save your settings along the way and apply them at the end.")) +--redirect on saved and changed to check changes. +if not SW.status() then + m.on_after_save = ccbi.conf_page +end + +--load up system section + +local shn = m:section(TypedSection, "system") +--Don't display it +shn.anonymous = true +--Sure as Sugar don't remove it +shn.addremove = false + +--Create a value field for hostname +local hname = shn:option(Value, "hostname", translate("Node Name"), translate("The node name (hostname) is a unique name for this device, visible to other devices and users on the network. Name this device in the field provided.")) +hname.datatype = "hostname" + +function hname.write(self, section, value) + local node_id = cnet.nodeid() + --check if the nodeid is the same and don't write hostname if it is. This means that if a person changes the hostname, but appends the old nodeif to the end of it the hostname will not change. But who in their right mind would do that. + hn, nid = value:match("(.-)%-([%d]*)$") + if nid and nid == node_id then + return true + else + local new_hn = value.."-"..string.sub(node_id, 1, 10) + luci.sys.hostname(new_hn) + return self.map:set(section, self.option, new_hn) + end +end + +local egg = shn:option(DummyValue, "_dummy1", translate("HI ANDREW!")) +egg:depends("hostname", "meat") +local egg2 = shn:option(DummyValue, "_dummy2", translate("Hail to the King!")) +egg2:depends("hostname", "jheretic") + + + +--PASSWORDS +local v0 = true -- track password success across maps + +-- CURRENT PASSWORD +-- Allow incorrect root password to prevent settings change +-- Don't prompt for password if none has been set +if luci.sys.user.getpasswd("root") then + s0 = m:section(TypedSection, "_dummy", translate("Current Node Administration Password"), translate("Current node administration password required to make changes on this page")) + s0.addremove = false + s0.anonymous = true + pw0 = s0:option(Value, "_pw0") + pw0.password = true + -- fail by default + v0 = false + function s0.cfgsections() + return { "_pass0" } + end +end + +if SW.status() then + pw_text = "This password will be used to make changes to this device after initial setup has been completed. The administration username is “root." +else + pw_text = "This password is used to make changes to this device. The administration username is “root." +end + +s = m:section(TypedSection, "_dummy", translate("Administration Password"), translate(pw_text)) +s.addremove = false +s.anonymous = true + +pw1 = s:option(Value, "_pw1", translate("Password")) +pw1.password = true + +pw2 = s:option(Value, "_pw2", translate("Confirmation")) +pw2.password = true + +--make sure passwords are equal +function pw1.validate(self, value, section) + local v1 = value + local v2 = pw2:formvalue(section) + if root_pass_check() == true then + if v1 and v2 and #v1 > 0 and #v2 > 0 then + if v1 == v2 then + if luci.sys.user.setpasswd('root', v1) == 0 then + uci:set("setup_wizard", "passwords", "admin_pass", 'changed') + uci:save("setup_wizard") + end + if m.message == nil then + m.message = translate("Password successfully changed!") + end + return value + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Given confirmation password did not match, password not changed!")) + m.save = false + return nil + end + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Unknown Error, password not changed!")) + m.save = false + return nil + end + end +end + +function s.cfgsections() + return { "_pass" } +end + +function root_pass_check() + if not SW.status() then + local form = http.formvaluetable("cbid") + local check = nil + local conf_pass = nil + for field,val in pairs(form) do + string.gsub(field, ".-_pw(%d)$", + function(num) + if tonumber(num) == 0 then + conf_pass = val + end + if val ~= nil and val ~= "" then + check = true + end + end) + end + if check ~= nil then + if conf_pass then + v0 = luci.sys.user.checkpasswd("root", conf_pass) + if v0 ~= true then + m.message = translate("Incorrect node administration password. Changes rejected!") + m.save = false + return false + else + return true + end + else + m.message = translate("Please enter your old node administration password. Changes rejected!") + m.save = false + return false + end + else + return true + end + else + return true + end +end + +return m diff --git a/luasrc/model/cbi/commotion/basic_wn.lua b/luasrc/model/cbi/commotion/basic_wn.lua new file mode 100644 index 0000000..ab05508 --- /dev/null +++ b/luasrc/model/cbi/commotion/basic_wn.lua @@ -0,0 +1,188 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local uci = require "luci.model.uci".cursor() +local utils = require "luci.util" +local cnw = require "luci.commotion.network" +local db = require "luci.commotion.debugger" +local http = require "luci.http" +local SW = require "luci.commotion.setup_wizard" +local ccbi = require "luci.commotion.ccbi" + +local m = Map("wireless", translate("Wireless Network"), translate("Turning on an Access Point provides a wireless network for people to connect to using a laptop or other wireless devices.")) + +--redirect on saved and changed to check changes. +if not SW.status() then + m.on_after_save = ccbi.conf_page +end + +s = m:section(TypedSection, "wifi-iface", translate("Access Point"), translate("Turning on an Access Point provides a wireless network for people to connect to using a laptop or other wireless devices.")) +s.optional = false +s.anonymous = true + --if not setup wizard then allow for adding and removal and default addition +if not SW.status() then + s.addremove = true + + md = s:option(Value, "mode") + md.default = 'ap' + md.render = function() end + md.parse = function(self, section, novld) + if self:cfgvalue(section) ~= md.default then + return self.map:set(section, self.option, value) + end + end + + nwk = s:option(Value, "network") + nwk.default = "lan" + nwk.render = function() end + nwk.parse = function(self, section, novld) + if self:cfgvalue(section) ~= nwk.default then + return self.map:set(section, self.option, value) + end + end + function s.remove(self, section) + m.changed = true + return self.map:del(section) + end +end + +function s:filter(section) + mode = self.map:get(section, "mode") + return mode == "ap" or mode == nil +end + +s.valuefooter = "cbi/full_valuefooter" +s.template_addremove = "cbi/commotion/addAP" --This template controls the addremove form for adding a new access point so that it has better wording. + +name = s:option(Value, "ssid", translate("Name"), translate("The access point name (SSID) is the name that people will look for when connecting to this device.")) +name.default = "CommotionWireless" + +function name:validate(val) + if #val > 31 or #val == 0 then + return nil + elseif val:match("[%$\"%[%]%?%+%/]") then + return nil + elseif val:match("^[%s%!%#]") then + return nil + else + return val + end +end + +local wifi_dev = {} +uci.foreach("wireless", "wifi-device", + function(s) + local name = s[".name"] + local mode = s.hwmode + table.insert(wifi_dev, {name, mode}) + end +) + +--Check for more than one radio, and if not don't offer to change radio's. +if #wifi_dev > 1 then + radios = s:option(ListValue, "device", translate("wifi-device"), translate("Select the wireless network interface to use for your access point.")) + for _,dev in ipairs(wifi_dev) do + local freq = cnw.get_channels(dev[2], true) + radios:value(dev[1], dev[1].." "..freq) + local channels = s:option(ListValue, "channel_"..dev[1], translate("Channel"), translate("The channel of this wireless interface.")) + channels:depends("device", dev[1]) + --adds the values to the list based on frequency + for _,x in pairs((cnw.get_channels(dev[2]))) do + channels:value(x[1], x[2]) + end + channels.default = uci:get("wireless", dev[1], "channel") + function channels.write(self, section, value) + local enable = self.map:set(dev[1], "disabled", "0") -- enable the radio + return self.map:set(dev[1], "channel", value) + end + end +else + + local channels = s:option(ListValue, "channel", translate("Channel"), translate("The channel of your wireless interface.")) + channels.default = uci:get("wireless", wifi_dev[1][1], "channel") + function channels.write(self, section, value) + local enable = self.map:set(wifi_dev[1][1], "disabled", "0") -- enable the radio + self.map:set(section, "device", wifi_dev[1][1]) --set iface to use this device. + return self.map:set(wifi_dev[1][1], "channel", value) + end + for _,x in pairs(cnw.get_channels(wifi_dev[1][2])) do + channels:value(x[1], x[2]) + end +end + +enc = s:option(Flag, "encryption", translate("Require a Password?"), translate("When people connect to this access point, should a password be required?")) +enc.disabled = "none" +enc.enabled = "psk2" +enc.rmempty = false +enc.default = "none" --default must == disabled value for rmempty to work + +enc.write=ccbi.flag_write +--Have enc flag also remove the encryption key when deleted and mark as changed. +function enc.remove(self, section) + value = self.map:get(section, self.option) + if value ~= self.disabled then + local key = self.map:del(section, "key") + local enc = self.map:del(section, self.option) + self.section.changed = true + return key and enc or false + end +end + +--dummy value set to not reveal password +pw1 = s:option(Value, "_pw1", translate("Password"), translate("Enter the password people should use to connect to this access point. Commotion uses WPA2 security for Access Point passwords.")) +pw1.password = true +pw1:depends("encryption", "psk2") +pw1.datatype = "wpakey" + +--password should write to the key, not to the dummy value +function pw1.write(self, section, value) + return self.map:set(section, "key", value) +end + +pw2 = s:option(Value, "_dummy", translate("Confirmation")) +pw2.password = true +pw2:depends("encryption", "psk2") + +--Don't actually write this value, just return success +function pw2.write(self, section, value) + return true +end + +--make sure passwords are equal +function pw1.validate(self, value, section) + local v1 = value + local v2 = pw2:formvalue(section) + --local v2 = http.formvalue(string.gsub(self:cbid(section), "%d$", "2")) + if v1 and v2 and #v1 > 0 and #v2 > 0 then + if v1 == v2 then + if m.message == nil then + m.message = translate("Password successfully changed!") + end + return value + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Given password confirmation did not match, password not changed!")) + return nil + end + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Unknown Error, password not changed!")) + return nil + end +end + +return m diff --git a/luasrc/model/cbi/commotion/client_wp.lua b/luasrc/model/cbi/commotion/client_wp.lua new file mode 100644 index 0000000..a8831e4 --- /dev/null +++ b/luasrc/model/cbi/commotion/client_wp.lua @@ -0,0 +1,142 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local utils = require "luci.util" +local db = require "luci.commotion.debugger" +local uci = require "luci.model.uci".cursor() +local fs = require "nixio.fs" +local ccbi = require "luci.commotion.ccbi" +local lfs = require "luci.fs" + +m = Map("nodogsplash", translate("Welcome Page")) + +--redirect on saved and changed to check changes. +m.on_after_save = ccbi.conf_page + +enable = m:section(TypedSection, "settings", translate("On/Off"), translate("Users can be redirected to a “welcome page” when they first connect to this node.")) +enable.anonymous = true + +toggle = enable:option(Flag, "enable") + +function toggle.write(self, section, fvalue) + value = self.map:get(section, self.option) + if value ~= fvalue then + self.section.changed = true + self.map:set("interfaces", "interface", "br-lan") + return self.map:set(section, self.option, fvalue) + end +end + +function toggle.remove(self, section) + value = self.map:get(section, self.option) + if value ~= self.disabled then + self.section.changed = true + return self.map:del(section, self.option) + end +end + +--[[ifaces = m:section(TypedSection, "interfaces", translate("For which network connection should this welcome page be active?"), translate("Select list of Aps and /or defined networks on this node's interfaces Auto select the first AP interface if configured.")) +ifaces.anonymous = true + +iflist = ifaces:option(ListValue, "interface") +current = uci:get("nodogsplash", "interfaces", "interface") +iflist:value(current) +iflist.default = current +uci.foreach("wireless", "wifi-iface", + function(s) + local name = s[".name"] + if not utils.contains(iflist.vallist, name) then + iflist:value(name) + end + end + ) +]]-- + +stime = m:section(TypedSection, "settings", translate("Time until welcome page is shown again")) +stime.anonymous = true + +tfield = stime:option(Value, "splashtime") +tfield.datatype = "uinteger" +tfield.forcewrite = true + +timeopt = stime:option(ListValue, "splashunit") +timeopt:value("seconds") +timeopt:value("minutes") +timeopt:value("hours") +timeopt:value("days") + +splshtxt = m:section(TypedSection, "_page", translate("Edit Welcome Page Text"), translate("The welcome page can include terms of service, advertisements, or other information. Edit the welcome page text here or upload an HTML file.")) +splshtxt.cfgsections = function() return { "_page" } end +splshtxt.anonymous = true + +edit2 = splshtxt:option(Flag, "edit", translate("Edit Welcome Page Text")) +upload2 = splshtxt:option(Flag, "upload", translate("Upload Welcome Page Text")) + +local splashtextfile = "/usr/lib/lua/luci/view/commotion-splash/splashtext.htm" + +local help_text = translate("You can enter text and HTML that will be displayed on the welcome page.").."

"..translate("These variables can be used to provide custom values from this node on the welcome page :").."
"..translate("$gatewayname: The value of GatewayName as set in the Welcome Page configuration file (/path/nodogsplash.conf).").."
"..translate("$authtarget: The URL of the user's original web request.").."
"..translate("$imagesdir: The directory in on this node where images to be displayed in the splash page must be located.").."
"..translate("The welcome page might include terms of service, advertisements, or other information. Edit the welcome page text here or upload an HTML file.").."
" + +help = splshtxt:option(DummyValue, "_dummy", nil, help_text) +--help.template = "cbi/nullsection" +help:depends("edit", "1") +help:depends("upload", "1") + +t = splshtxt:option(TextValue, "text") +t.rmempty = true +t.rows = 30 +t:depends("edit", "1") + +function t.cfgvalue() + return fs.readfile(splashtextfile) or "" +end + +uploader = splshtxt:option(FileUpload, "_upload") +uploader:depends("upload", "1") + +function m.on_parse(self) + local b_press = luci.http.formvalue("cbid.nodogsplash._page._page") + uploaded = "cbid.nodogsplash._page._upload" + if lfs.isfile("/lib/uci/upload/"..uploaded) then + if fs.move("/lib/uci/upload/"..uploaded, splashtextfile) then + m.proceed = true + m.message = "Success! Your welcome page text has been updated!" + else + m.proceed = true + m.message = "Sorry! There was a problem moving your welcome text to the correct location. You can find it in ".."/lib/uci/upload/"..uploaded.. " and move it to "..splashtextfile + end + elseif luci.http.formvalue(uploaded) ~= nil then + m.proceed = true + m.message = "Sorry! There was a problem updating your welcome page text. Please try again." + end + text = luci.http.formvalue("cbid.nodogsplash._page.text") + if text then + if text ~= "" then + fs.writefile(splashtextfile, text:gsub("\r\n", "\n")) + m.proceed = true + m.message = "Success! Your welcome page text has been updated!" + else + fs.unlink(splashtextfile) + m.proceed = true + m.message ="The default welcome page has been restored." + end + end + return true +end + +return m + + diff --git a/luasrc/model/cbi/commotion/confirm_config.lua b/luasrc/model/cbi/commotion/confirm_config.lua new file mode 100644 index 0000000..fdf39f7 --- /dev/null +++ b/luasrc/model/cbi/commotion/confirm_config.lua @@ -0,0 +1,22 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local cursor = require "luci.model.uci".cursor() +--Main title and system config map for hostname value +local m = Map("system", translate("Basic Configuration"), translate("In this section you'll set the basic required settings for this device, and the basic network settings required to connect this device to a Commotion Mesh network. You will be prompted to save your settings along the way and apply them at the end.")) + +return m diff --git a/luasrc/model/cbi/commotion/meshconfig.lua b/luasrc/model/cbi/commotion/meshconfig.lua deleted file mode 100644 index 7453a06..0000000 --- a/luasrc/model/cbi/commotion/meshconfig.lua +++ /dev/null @@ -1,135 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - -Copyright 2011 Josh King - -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 uci = require "luci.model.uci".cursor() -local sys = require "luci.sys" -local util = require "luci.util" - -m = Map("wireless", translate("Configuration"), translate("This configuration wizard will assist you in setting up your router for a Commotion network.")) - -sctAP = m:section(NamedSection, "quickstartAP", "wifi-iface", translate("Access Point")) -sctAP.optional = true -sctAP:option(Value, "ssid", translate("Name (SSID)"), translate("The public facing name of this interface")) - -sctSecAP = m:section(NamedSection, "quickstartSec", "wifi-iface", translate("Secure Access Point")) -sctSecAP.optional = true -sctSecAP:option(Value, "ssid", translate("Name (SSID)"), translate("The public facing name of this interface")) - -sctMesh = m:section(NamedSection, "quickstartMesh", "wifi-iface", translate("Mesh Backhaul")) -sctMesh.optional = true -sctMesh:option(Value, "ssid", translate("Name (SSID)"), translate("The public facing name of this interface")) -sctMesh:option(Value, "bssid", translate("Device Designation (BSSID)"), translate("The device read name of this interface. (Letters A-F, and numbers 0-9 only)")) - -e = m:section(TypedSection, "wifi-device", translate("Network-wide Settings")) -e.anonymous = true - -local fiveghz = {'11a', '11adt', '11na'} -protocol = uci:get_first("wireless", "wifi-device", "hwmode") - -if util.contains(fiveghz, protocol) then - c = e:option(ListValue, "channel", translate("5GHz Channel"), translate("The 5GHz backhaul channel of the mesh network, if applicable.")) - c:value(36, "Channel 36 (5.180 GHz)") - c:value(40, "Channel 40 (5.200 GHz)") - c:value(44, "Channel 44 (5.220 GHz)") - c:value(48, "Channel 48 (5.240 GHz)") - c:value(149, "Channel 149 (5.745 GHz)") - c:value(153, "Channel 153 (5.765 GHz)") - c:value(157, "Channel 157 (5.785 GHz)") - c:value(161, "Channel 161 (5.805 GHz)") - c:value(165, "Channel 165 (5.825 GHz)") -else - c = e:option(ListValue, "channel", translate("2GHz Channel"), translate("The 2.4GHz backhaul channel of the mesh network, if applicable")) - for i=1, 11 do - c:value(i, "Channel " .. i .. " (" .. tostring(2.407+(i*0.005)) .. " GHz)") - end -end - --- TASK: Add option to manual mesh config to change DNS server -m3 = Map("network") -s_namesrv = m3:section(TypedSection, "_dummy", translate("DNS Servers"), - translate("Override nameservers defined in Commotion profiles")) -s_namesrv.optional = true -s_namesrv.anonymous = true - -local netifs = {} -local placeholder={} -o_dns = s_namesrv:option(Value, "dns", "", - translate("Separate IP addresses with spaces")) -o_dns.rmempty = true - --- Check /etc/config/network for existing overrides -uci:foreach("network","interface", - function(interface) - if interface["proto"] == "commotion" and interface["dns"] then - table.insert(netifs, interface[".name"]) - if #placeholder == 0 then - table.insert(placeholder, interface["dns"]) - elseif #placeholder > 0 and util.contains(placeholder, interface["dns"]) == false then - table.insert(placeholder, interface["dns"]) - end - end - end -) -o_dns.default = table.concat(placeholder, " ") - -function s_namesrv.cfgsections() - return { "_dns" } -end - -function m3.on_before_commit(map) - local datatypes = require "luci.cbi.datatypes" - if o_dns:formvalue("_dns") then - dns = o_dns:formvalue("_dns") - if #dns > 0 then - dns = util.split(dns, " ") - for _, d in ipairs(dns) do - if datatypes.ipaddr(d) == false then - m.message = translate("DNS field must contain valid IP addresses separated by spaces") - m.save = false - m2.save = false - m3.save = false - end - end - else - m.message = translate("Removing DNS overrides") - end - end -end - -function m3.on_commit(map) -if m.save==true and m2.save==true and m3.save==true then - local dns1 = o_dns:formvalue("_dns") - if dns1 ~= nil then - uci:foreach("network","interface", - function(interface) - if interface["proto"] == "commotion" then - uci:set("network", interface[".name"], "dns", dns1) - end - end - ) - else - if interface["dns"] then - uci:delete("network", interface[".name"], "dns") - end - end - uci:commit("network") -end -end - -m2 = Map("commotiond") -node = m2:section(TypedSection, "node", translate("Settings specific to this node")) -node.anonymous = true -node.optional = true -node:option(Value, "dhcp_timeout", translate("DHCP Timeout"), translate("How many seconds to wait on boot for a DHCP lease from the gateway")) - -return m, m3, m2 diff --git a/luasrc/model/cbi/commotion/security_pass.lua b/luasrc/model/cbi/commotion/security_pass.lua new file mode 100644 index 0000000..314d582 --- /dev/null +++ b/luasrc/model/cbi/commotion/security_pass.lua @@ -0,0 +1,228 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- +local db = require "luci.commotion.debugger" +local http = require "luci.http" +local ccbi = require "luci.commotion.ccbi" +local uci = require "luci.model.uci".cursor() + +local m = Map("wireless", translate("Passwords"), translate("Commotion basic security settings places all the passwords and other security features in one place for quick configuration. ")) + +--redirect on saved and changed to check changes. +m.on_after_save = ccbi.conf_page + +--PASSWORDS +local v0 = true -- track password success across maps + +-- CURRENT PASSWORD +-- Allow incorrect root password to prevent settings change +-- Don't prompt for password if none has been set +if luci.sys.user.getpasswd("root") then + s0 = m:section(TypedSection, "_dummy", translate("Current Node Administration Password"), translate("Current node administration password required to make changes on this page")) + s0.addremove = false + s0.anonymous = true + pw0 = s0:option(Value, "_pw0") + pw0.password = true + -- fail by default + v0 = false + function s0.cfgsections() + return { "_pass0" } + end +end + +local interfaces = {} +uci:foreach("wireless", "wifi-iface", + function(s) + local name = s[".name"] + local key = s.key or "NONE" + local mode = s.mode or "NONE" + local enc = s.encryption or "NONE" + table.insert(interfaces, {name=name, mode=mode, key=key, enc=enc}) + end +) + +--iface password creator for all other interfaces +--! @name pw_sec_opt +--! @brief create password options to add to interface passed- +function pw_sec_opt(pw_s, iface) + --section options + pw_s.addremove = false + pw_s.anonymous = true + + --encryption toggle + enc = pw_s:option(Flag, "encryption", translate("Require a Password?"), translate("When people connect to this access point, should a password be required?")) + enc.disabled = "none" + enc.enabled = "psk2" + enc.rmempty = true + enc.default = enc.disabled --default must == disabled value for rmempty to work + + --Make enc flag actually check for section.changed and set that flag for the confirmation page to work + enc.write = ccbi.flag_write + function enc.remove(self, section) + value = self.map:get(section, self.option) + if value ~= self.disabled then + local key = self.map:del(section, "key") + local enc = self.map:del(section, self.option) + self.section.changed = true + return key and enc or false + end + end + + --password options + pw1 = pw_s:option(Value, (iface.name.."_pw1")) + pw1.password = true + pw1:depends("encryption", "psk2") + pw1.datatype = "wpakey" + + --confirmation password + pw2 = pw_s:option(Value, iface.name.."_pw2", nil, translate("Confirm Password")) + pw2.password = true + pw2:depends("encryption", "psk2") + + --password should write to the key, not to the dummy value + function pw1.write(self, section, value) + return self.map:set(section, "key", value) + end + + --Don't actually write this value, just return success + function pw2.write(self, section, value) + return true + end + + --make sure passwords are equal + function pw1.validate(self, value, section) + local v1 = value + local v2 = http.formvalue(string.gsub(self:cbid(section), "%d$", "2")) + if v1 and v2 and #v1 > 0 and #v2 > 0 then + if v1 == v2 then + if m.message == nil then + m.message = translate("Password successfully changed!") + end + return value + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Given password confirmation did not match, password not changed!")) + m.state = -1 + return nil + end + else + m.message = translate("Error, no changes saved. See below.") + self:add_error(section, translate("Unknown Error, password not changed!")) + m.state = -1 + return nil + end + end +end + +--MESH ECRYPTION PASSWORD +--Check for mesh interfaces +mesh_ifaces = {} +for i,iface in ipairs(interfaces) do + if iface.mode == "adhoc" then + table.insert(mesh_ifaces, iface) + end +end + +local pw_text = "To encrypt Commotion mesh network data between devices, each device must share a common mesh encryption password. Enter that shared password here." +if #mesh_ifaces > 1 then + for _,x in pairs(mesh_ifaces) do + local meshPW = m:section(NamedSection, x.name, "wifi-iface", x.name, pw_text) + meshPW = pw_sec_opt(meshPW, x) + end +else + local meshPW = m:section(NamedSection, mesh_ifaces[1].name, "wifi-iface", mesh_ifaces[1].name, pw_text) + meshPW = pw_sec_opt(meshPW, mesh_ifaces[1]) +end + +--ADMIN PASSWORD +admin_pw_text = "This password is used to login to this node." +admin_pw_s = m:section(TypedSection,"_dummy", translate("Administration Password"), translate(admin_pw_text)) +admin_pw_s.addremove = false +admin_pw_s.anonymous = true + +admin_pw1 = admin_pw_s:option(Value, "admin_pw1") +admin_pw1.password = true + +admin_pw2 = admin_pw_s:option(Value, "admin_pw2", translate("Confirmation")) +admin_pw2.password = true + +function admin_pw_s.cfgsections() + return { "_pass" } +end + +--Check for other Interfaces +for i,iface in ipairs(interfaces) do + if iface.mode ~= "adhoc" then + local otherPW = m:section(NamedSection, iface.name, "wifi-iface", iface.name.." Interface", translate("Enter the password people should use to connect to this interface.")) + otherPW = pw_sec_opt(otherPW, iface, iface.name) + end +end + +--!brief This map checks for the admin password field and denies all saving and removes the confirmation page redirect if it is there. +function m.on_parse(self) + local form = http.formvaluetable("cbid.wireless") + local check = nil + local conf_pass = nil + for field,val in pairs(form) do + string.gsub(field, ".-_pw(%d)$", + function(num) + if tonumber(num) == 0 then + conf_pass = val + end + if val then + check = true + end + end) + end + if check ~= nil then + if conf_pass then + v0 = luci.sys.user.checkpasswd("root", conf_pass) + if v0 ~= true then + m.message = translate("Incorrect password. Changes rejected!") + m.save = false + end + else + m.message = translate("Please enter your old password. Changes rejected!") + m.save = false + end + end +end + +--! admin password changes checks +function m.on_save(self) + local v1 = admin_pw1:formvalue("_pass") + local v2 = admin_pw2:formvalue("_pass") + if v0 == true and v1 and v2 and #v1 > 0 and #v2 > 0 then + if v1 == v2 then + if luci.sys.user.setpasswd(luci.dispatcher.context.authuser, v1) == 0 then + m.message = translate("Admin Password successfully changed!") + uci:set("setup_wizard", "passwords", "admin_pass", 'changed') + uci:save("setup_wizard") + else + m.message = translate("Unknown Error, password not changed!") + m.state = -1 + end + else + m.message = translate("Given password confirmation did not match, password not changed!") + m.state = -1 + end + end +end + +return m + + + diff --git a/luasrc/model/cbi/commotion/security_smk.lua b/luasrc/model/cbi/commotion/security_smk.lua new file mode 100644 index 0000000..b91465c --- /dev/null +++ b/luasrc/model/cbi/commotion/security_smk.lua @@ -0,0 +1,212 @@ +--[[ + Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- + +local ccbi = require "luci.commotion.ccbi" +local db = require "luci.commotion.debugger" +local uci = require "luci.model.uci".cursor() +local cnet = require "luci.commotion.network" +local sys = require "luci.sys" +local fs = require "luci.fs" +local util = require "luci.util" + +m = Map("olsrd", translate("Shared Mesh Keychain"), translate("To ensure that only authorized devices can route traffic on your Commotion mesh network, one Shared Mesh Keychain file can be generated and shared by all devices.")) + +--redirect on saved and changed to check changes. +m.on_after_save = ccbi.conf_page + +s = m:section(TypedSection, "LoadPlugin", translate("Use a Shared Mesh Keychain to sign mesh routes. Yes/No"), translate("Add or remove a Shared Mesh Keychain on this device. You can also upload or generate a new Shared Mesh Keychain once you add a Shared Mesh Keychain to this device.")) +s.addremove = true +s.anonymous = true + +function s.filter(self, section) + return self.map:get(section, "library") == "olsrd_mdp.so.0.1" +end + +function s.remove(self, section) + unset_commotion() + return self.map:del(section) +end + +function s.parse(self, novld) + local changes = uci:changes("olsrd") + if changes and changes.olsrd then + db.log("changes found:") + self.changed = true + else + db.log("changes NOT found") + end + TypedSection.parse(self, novld) +end + +lib = s:option(Value, "library") +lib.default = "olsrd_mdp.so.0.1" +lib.render = function() end +function lib:parse(section) + db.log("lib parse") + local cvalue = self:cfgvalue(section) + db.log(cvalue) + if not cvalue then + self:write(section, self.default) + end +end + +function lib.write(self, section, value) + set_commotion() + return self.map:set(section, self.option, value) +end + + +sp = s:option(Value, "servalpath") +sp.default = "/etc/commotion/keys.d/mdp/serval.keyring" +sp.render = function() end +function sp:parse(section) + db.log("sp parse") + local cvalue = self:cfgvalue(section) + db.log(cvalue) + if not cvalue then + self:write(section, self.default) + end +end +function sp.write(self, section, value) + set_commotion() + m.changed = true + return self.map:set(section, self.option, value) +end + +function set_commotion() + db.log("set") + uci.foreach("wireless", "wifi-iface", + function(s) + local sid = get_sid() + local name = s[".name"] + if s.mode == 'adhoc' then + if uci:get("network", s.network, "proto") == "commotion" then + local profile = uci:get("network", s.network, "profile") + cnet.commotion_set(profile, {mdp_keyring="/etc/commotion/keys.d/mdp/", mdp_sid=sid, serval='true'}) + cnet.commotion_set(profile) + end + end + end + ) +end + +function unset_commotion() + db.log("unset") + uci.foreach("wireless", "wifi-iface", + function(s) + local sid = get_sid() + local name = s[".name"] + if s.mode == 'adhoc' then + if uci:get("network", s.network, "proto") == "commotion" then + local profile = uci:get("network", s.network, "profile") + cnet.commotion_set(profile, {serval='false'}) + end + end + end + ) +end + +function get_sid(path) + db.log("get sid") + if path == nil then + path = "/etc/commotion/keys.d/mdp/" + end + if not fs.isfile("/etc/commotion/keys.d/mdp/serval.keyring") then + sys.exec("SERVALINSTANCE_PATH=/etc/commotion/keys.d/mdp/ serval-client keyring create") + sys.exec("SERVALINSTANCE_PATH=/etc/commotion/keys.d/mdp/ serval-client keyring add") + end + local sid = sys.exec("SERVALINSTANCE_PATH="..path.." serval-client keyring list") + local key = string.match(sid, "^(%w*):%w*:?") + if key == nil or string.len(key) ~= 64 then + m.message = translate("The file supplied is not a proper keyring, or is password protected. Please upload another key.") + m.state = -1 + return false + else + return key + end +end + +sid = s:option(Value, "sid") +sid.default = get_sid() +function sid.write(self, section, value) + db.log("sid write") + local value = get_sid() + m.changed = true + return self.map:set(section, self.option, value) +end + +sid.render = function() end +function sid:parse(section) + db.log("sid parse") + local cvalue = self:cfgvalue(section) + if not cvalue then + self:write(section, self.default) + end +end + +uploader = s:option(FileUpload, "_upload", translate("Upload Shared Mesh Keychain File"), translate("If a Shared Mesh Keychain file was provided to you by a network administrator or another community member, select and upload it here to join this device to an existing mesh network.")) +uploader.anonymous = true + +function uploader.write(self, section, value) + db.log("uploader write") + local nfs = require "nixio.fs" + sys.exec("mv /lib/uci/upload/cbid.olsrd.*._upload /lib/uci/upload/serval.keyring") + if get_sid("/lib/uci/upload/") ~= false then + local mv = nfs.move("/lib/uci/upload/serval.keyring", "/etc/commotion/keys.d/mdp/serval.keyring") + self.map:set(section, "sid", get_sid()) + set_commotion() + m.changed = true + else + return false + end +end + +dwnld = s:option(Button, "_dummy", translate("Download Shared Mesh Keychain"), translate("Download a copy of this device's existing Shared Mesh Keychain. Use this feature to make a backup of this file, or to share it with people putting up new devices on your Commotion mesh network.")) +dwnld.anonymous = true + +function dwnld.write(self, section, value) + local ltn12 = require "luci.ltn12" + keyring = uci:get("serval", "settings", "olsrd_mdp_keyring") + local f = io.open(keyring.."/serval.keyring") + if not f then + self:add_error(section, translate("No Current Serval Key To Download.")) + return nil + end + luci.http.prepare_content("application/force-download") + luci.http.header("Content-Disposition", "attachment; filename=serval.keyring") + luci.ltn12.pump.all(luci.ltn12.source.file(f), luci.http.write) + io.close(f) + return true +end + +new = s:option(Button, "_dummy2", translate("Create a new Shared Mesh Keychain file"), translate("Click on the button below to create a new Shared Mesh Keychain file. This will DELETE the existing Shared Mesh Keychain on this device. Use this option if you are creating a brand new Commotion mesh network, or if you are changing the Shared Mesh Keyhchain on an existing network. In either case, create a backup of the existing Shared Mesh Keychain first.")) +new.anonymous = true + +function new.write(self, section, value) + m.changed = true + return self.map:set(section, "sid", get_sid()) +end + +function new.validate(self, section, value) + db.log("new") + m.save = true + sys.exec("rm /etc/commotion/keys.d/mdp/serval.keyring") + set_commotion() + return true +end + +return m diff --git a/luasrc/model/cbi/commotion/setup_wizard.lua b/luasrc/model/cbi/commotion/setup_wizard.lua new file mode 100644 index 0000000..277eee7 --- /dev/null +++ b/luasrc/model/cbi/commotion/setup_wizard.lua @@ -0,0 +1,68 @@ +--[[ +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +]]-- +local db = require "luci.commotion.debugger" +local cursor = require "luci.model.uci".cursor() + +local d = Delegator() +d.allow_finish = true +d.allow_back = true +d.allow_cancel = true +d.allow_reset = true +d.template = "cbi/commotion/delegator" + +function d.on_cancel() + return true +end + +d:add("Node Settings", "commotion/basic_ns") +d:add("Mesh Network", "commotion/basic_mn") +d:add("Wireless Network", "commotion/basic_wn") +d:add("Configuration Complete", "commotion/basic_done") +d:add("Additional Network Interfaces", "commotion/basic_ani") + + +function d.parse(self, ...) + local form = luci.http.formvalue() + local page = form.sw_page + if page ~= nil then + d.current = page + if page == '' then + d.current = nil + s.active = self.chain[1] + end + end + return Delegator.parse(self, ...) +end + + +function d.get_next(self, state) + local form = luci.http.formvalue() + local page = form.sw_page + db.log("is there a page") + db.log(page) + if page then + for k, v in ipairs(self.chain) do + if v == state then + return self.chain[k] + end + end + else + return Delegator.get_next(self, state) + end +end + +return d diff --git a/luasrc/view/cbi/commotion/addAP.htm b/luasrc/view/cbi/commotion/addAP.htm new file mode 100644 index 0000000..cae42a8 --- /dev/null +++ b/luasrc/view/cbi/commotion/addAP.htm @@ -0,0 +1,15 @@ +<%:Access Point%> +
<%:Click here to create a new access point. Once created you will be able to come back to this page to configure it.%>
+
+ <% if self.anonymous then -%> + + <%- else -%> + <% if self.invalid_cts then -%>
<% end %> + + + + <% if self.invalid_cts then -%> +
<%:Invalid%>
+ <%- end %> + <%- end %> +
diff --git a/luasrc/view/cbi/commotion/addMesh.htm b/luasrc/view/cbi/commotion/addMesh.htm new file mode 100644 index 0000000..4d48754 --- /dev/null +++ b/luasrc/view/cbi/commotion/addMesh.htm @@ -0,0 +1,17 @@ +<%:Mesh Network%> +
<%:Click here to add a new mesh network interface. Once created you will be able to come back to this page to configure it%>
+
+ <%if self.map.changed ~= true then %> + <% if self.anonymous then -%> + + <%- else -%> + <% if self.invalid_cts then -%>
<% end %> + + + + <% if self.invalid_cts then -%> +
<%:Invalid%>
+ <%- end %> + <%- end %> + <%- end %> +
diff --git a/luasrc/view/cbi/commotion/delegator.htm b/luasrc/view/cbi/commotion/delegator.htm new file mode 100644 index 0000000..cc341c5 --- /dev/null +++ b/luasrc/view/cbi/commotion/delegator.htm @@ -0,0 +1,53 @@ +<%+header%> +
+ + + + +
+
+
+ <% for i, x in ipairs(self.chain) do + local title = translate(x) + -%> + <%if x == self.current then%> +
+ <%else%> +
+ <%end%> +

<%=title%> +

+
+ <% end %> +
+
+
+ <%- self.active:render() %> +
+ +<% for _, x in ipairs(self.chain) do %> + +<% end %> +<% if not self.disallow_pageactions then %> +<% if self.allow_back and self:get_prev(self.current) then %> + +<% end %> +<% if self.allow_reset then %> + +<% end %> +<% if self.current == "Configuration Complete" then %> + +<% end %> +<% if self.allow_finish and not self:get_next(self.current) then %> + +<% elseif self:get_next(self.current) then %> + +<% end %> +<% end %> + +
diff --git a/luasrc/view/commotion/about.htm b/luasrc/view/commotion/about.htm new file mode 100644 index 0000000..289a22f --- /dev/null +++ b/luasrc/view/commotion/about.htm @@ -0,0 +1,19 @@ +<%+header%> + +
+

<%:About Commotion%>

+

<%:Commotion is an open-source communication tool that uses mobile phones, computers, and other wireless devices to create decentralized mesh networks.%>

+ +

<%:Wireless mesh networks allow devices to connect directly to each other without going through a centralized point.%>

+ +

<%:Commotion is intended to be a:%>

+ +
    +
  • <%:simple, easy-to-use communications tool that anyone can set up and use without technical expertise%>
  • +
  • <%:platform for building community wireless networks and hosting local applications%>
  • +
  • <%:tool for creating infrastructure that is resilient against surveillance and disruption%>
  • +
  • <%:flexible, open-source software platform that programmers around the globe can continually adapt and build upon%>
  • +
+
+ +<%+footer%> diff --git a/luasrc/view/commotion/apply.htm b/luasrc/view/commotion/apply.htm new file mode 100644 index 0000000..77fa0f0 --- /dev/null +++ b/luasrc/view/commotion/apply.htm @@ -0,0 +1,49 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +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: apply.htm 9014 2012-08-14 13:08:18Z jow $ + +-%> +<%SW = require "luci.commotion.setup_wizard"%> +<%+header%> +<%if SW.status() then + redirect = luci.dispatcher.build_url("commotion", "advanced") + else + redirect = luci.dispatcher.build_url("admin","commotion") + end%> + +

<%:Applying Changes%>

+ +<% if changes then %> + <%+commotion/apply_xhr%> + <%+admin_uci/changelog%> + + <%- cbi_apply_xhr('uci-apply', configs, redirect) -%> + +

<%:The following changes have been committed%>:

+ <%- uci_changelog(changes) -%> +<% else %> +

<%:There are no pending changes to apply!%>

+<% end %> +<%sw_stat = SW.status()%> +<%if sw_stat then%> +
+<%end%> +
+
+ +
+
+<%if sw_stat then%> +
+<%end%> + +<%+footer%> diff --git a/luasrc/view/commotion/apply_xhr.htm b/luasrc/view/commotion/apply_xhr.htm new file mode 100644 index 0000000..5d79ad1 --- /dev/null +++ b/luasrc/view/commotion/apply_xhr.htm @@ -0,0 +1,44 @@ +<% export("cbi_apply_xhr", function(id, configs, redirect) -%> +
+ <%:Applying changes%> + + + <%:Loading%> + <%:Applying changes... +Please be patient, this may take a few moments. Once the settings are complete you will be returned to the Administrative login.%> +
+<%- end) %> diff --git a/luasrc/view/commotion/confirm.htm b/luasrc/view/commotion/confirm.htm new file mode 100644 index 0000000..1c42244 --- /dev/null +++ b/luasrc/view/commotion/confirm.htm @@ -0,0 +1,82 @@ +<%# +Modifications of... + +Copyright 2013 - Seamus Tuohy + +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 + +-%> +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +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: changes.htm 9014 2012-08-14 13:08:18Z jow $ + +-%> + +<%SW = require "luci.commotion.setup_wizard" + disp = require "luci.dispatcher" + util = require "luci.util"%> + +<%+header%> + +

<%:Confirm Configuration%>

+ +

Review the settings you have chosen below. To finish the Setup Wizard click “Apply” at the bottom of this page. The device will restart when you click apply. You will lose access while it restarts.

+ +<% if changes then %> + <%+admin_uci/changelog%> + <%- uci_changelog(changes) -%> +<% else %> +

<%:There are no pending changes!%>

+<% end %> +<%sw_stat = SW.status()%> +<%if sw_stat then%> +
+<%end%> +
+ <%if sw_stat then%> +
+ +
+ <%else%> + <% local r = luci.http.formvalue("redir"); if r and #r > 0 then %> +
+ +
+ <% end + end%> + <%if sw_stat then%> +
+ <%else%> + + <%end%> + " /> + +
+ <%if sw_stat then%> +
+ " /> + + <%else%> + + " /> + + <%end%> +
+
+<%if sw_stat then%> +
+<%end%> +<%+footer%> diff --git a/luasrc/view/commotion/conn_clients.htm b/luasrc/view/commotion/conn_clients.htm new file mode 100644 index 0000000..97063b5 --- /dev/null +++ b/luasrc/view/commotion/conn_clients.htm @@ -0,0 +1,114 @@ +<%# +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +%> + + +<%- +local util = require "luci.util" +local db = require "luci.commotion.debugger" +--Rip open assets table to get its sweet innards (need this because of the open ended include) +for name,obj in pairs(assets) do + _G[name] = obj +end + +colors = {default="", grn="#00cc00", yellow="#ffcb05", orange="#ff6600", red="#bb3333" } + +headers = {{title="IP-Address", var="ip"}, + {title="Time Connected", var="duration"}, + {title="Bandwidth Used", var="bnd_wdth"}, + {title="Average Speed", var="avg_spd"}, + {title="Currently Active", var="curr_conn"}} +if #clients > 0 then + for _,iface in ipairs(ifaces) do + for i in util.execi("iw dev "..iface.." station dump") do + string.gsub(i, "^Station%s(.-)%s%(", + function(mac) + for client,dossier in ipairs(clients) do + local c_idx = util.contains(dossier, mac) + if c_idx then + clients[client].curr_conn = "Yes" + end + end + end) + end + end +end + +function create_table_header(values) + for _,x in ipairs(values) do + row_title = translate(x.title) + -%><%=row_title%><%- + if x.help then + -%> + + help + + <%- + else + -%><%- + end + end +end + +function create_table(values, data, colors) + i = 1 + for _,curr in ipairs(data) do + i = ((i % 2) + 1) + -%><%- + for _,val in pairs(values) do + if curr[val.var] then + result = curr[val.var] + else + result = "N/A" + end + if curr.color then + color = curr.color(colors) --The color var should point to an anonymous function to run over the color table which returns a color from that table (or anywhere else really). + else + color=colors.default + end + -%><%=result%><%- + end + end +end + + +if #clients > 0 then +--Actually create the table below here. +%> +

<%:Connected Clients!%>

+

A client is a device that has connected to this node. Information about clients is displayed below.

+ <% if warning then %> +

<%:You are not using a welcome page (splash page/captive portal.) Without a splash page we can only show a clients ip-address and the status of their connection.%>

+ <%end%> + + + + <%create_table_header(headers)%> + + + + + <%create_table(headers, clients, colors)%> + +
+<%else%> +

<%:Connected Clients!%>

+

<%:No connected clients were found at this time. If a Welcome Page has been enabled, clients must click through it before being displayed here.%>

+<%end%> diff --git a/luasrc/view/commotion/debug.htm b/luasrc/view/commotion/debug.htm new file mode 100644 index 0000000..fb6a7f1 --- /dev/null +++ b/luasrc/view/commotion/debug.htm @@ -0,0 +1,54 @@ + +<%- + local debugurl = luci.dispatcher.build_url('admin', 'status', 'debug', 'submit') +-%> + +
+ +

<%:Commotion Bug Info%>

+ +
+

<%:Name%>:
+

+

<%:Contact Info%>:
+

+ +

<%:What were you doing on the device when the error occurred?%>
+

+ +

<%:Please describe the behavior you expected%>:
+ +

+ +

<%:Please describe the behavior you experienced that you believe is wrong%>:
+ +

+ +

<%:Debugging Info%>:

+ <%:Mesh Network Information%>
+ <%:Router Status%>
+ <%:Router Traffic Routing Rules%>
+ <%:All Info%>
+ +
+

+
+ diff --git a/luasrc/view/commotion/error_olsr.htm b/luasrc/view/commotion/error_olsr.htm new file mode 100644 index 0000000..07df09c --- /dev/null +++ b/luasrc/view/commotion/error_olsr.htm @@ -0,0 +1,17 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +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: error_olsr.htm 5448 2009-10-31 15:54:11Z jow $ + +-%> +

<%:OLSR Daemon%>

+

<%:Unable to connect to the OLSR daemon!%>

+

<%:Make sure that OLSRd is running, the "txtinfo" plugin is loaded, configured on port 2006 and accepts connections from "127.0.0.1".%>

diff --git a/luasrc/view/commotion/help.htm b/luasrc/view/commotion/help.htm new file mode 100644 index 0000000..e405ae2 --- /dev/null +++ b/luasrc/view/commotion/help.htm @@ -0,0 +1,27 @@ +<%+header%> + +
+

<%:Help with Commotion%>

+

<%:Commotion is an evolving project that includes many parts. The documentation on our website has been created by the Commotion community to aid users and developers. It is often updated and we encourage you to get involved by contributing documentation. Listed below are links to the most relevant documentation to get started:%> +

+

+

<%:You can also connect with the Commotion community by subscribing to one or more of our listservs:%> +

    +
  • commotion-announce: <%:only official announcements with minimal discussion.%>
  • +
  • commotion-discuss: <%:for those who want to use Commotion without getting too technical.%>
  • +
  • commotion-dev: <%:for those who want to discuss improvements the Commotion software.%>
  • +
+

+

<%:You can also join us using your chat program on IRC in the #commotion channel on the irc.freenode.net server.%>

+ +

<%:Learn more about using IRC%>.

+ +
+ +<%+footer%> diff --git a/luasrc/view/commotion/license.htm b/luasrc/view/commotion/license.htm new file mode 100644 index 0000000..8ed2afd --- /dev/null +++ b/luasrc/view/commotion/license.htm @@ -0,0 +1,16 @@ +<%+header%> + +
+

<%:Commotion License%>

+ +

<%:This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.%>

+ +

<%:This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.%>

+ +

<%:You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.%>

+ +

<%:The OpenWrt distribution upon which Commotion is built bundles a lot of third party applications and modules which are available under various other Open Source licenses or Public Domain. The sources for those packages can be found on the OpenWrt mirrot at: http://downloads.openwrt.org/sources/ . Please refer to these source packages to find out which license applies to them.%>

+ +
+ +<%+footer%> diff --git a/luasrc/view/commotion/meshprofile.htm b/luasrc/view/commotion/meshprofile.htm index 33112cb..ecaa12f 100644 --- a/luasrc/view/commotion/meshprofile.htm +++ b/luasrc/view/commotion/meshprofile.htm @@ -1,89 +1,33 @@ - <%+header%> -<%- -uri = REQUEST_URI -uri_prefix, num_post = string.gsub(uri, "meshprofile_.*", "meshprofile") --%> - -

<%:Node Profiles%>

- -

<%:If your node includes configuration profiles for multiple Commotion -networks, this form allows you to set your defaults.%> <%:NOTE: Channel settings must be manually changed.%> -<%:We recommend that you reboot your node after changing these settings.%>

- -<%-if ERR then-%> -
<%=ERR%>
-<%end%> - -<%-if isDuplicate == true then-%> -
-

Overwrite existing profile?

- -<%:Yes, please overwrite the existing profile%>
-<%:No, I will rename the file and upload it again%>
- -
-<%end%> - -
-

<%:Select an interface%>

-
    -
- - -

<%:Select a profile to apply%>

- - - -
+

<%:Welcome to Commotion%>

-

-

<%:Download a Profile%>

-
- - - -
+

<%:This router has not yet been set up. Use the Setup Wizard for step-by-step configuration, upload a configuration file if you have one from a previously configured router, or choose Advanced for manual GUI or CLI configuration.%>

-

<%:Upload a Profile%>

-
-
-
- -
- - -
- <%:Upload a config file from your own computer.%> -
-
-
-
- -
- +
+ +
<%+footer%> diff --git a/luasrc/view/commotion/nearby_md.htm b/luasrc/view/commotion/nearby_md.htm new file mode 100644 index 0000000..5c5c5c7 --- /dev/null +++ b/luasrc/view/commotion/nearby_md.htm @@ -0,0 +1,152 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich +Copyright 2011 Manuel Munz + +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 + +-%> + +<%- + --Rip open assets table to get its sweet innards + for name,obj in pairs(assets) do + _G[name] = obj + end +-%> + +<% +local olsrtools = require "luci.tools.olsr" +local i = 1 + +if luci.http.formvalue("status") == "1" then + local rv = {} + for k, link in ipairs(links) do + link.Cost = tonumber(link.Cost) or 0 + local color = olsrtools.etx_color(link.Cost) + defaultgw_color = "" + if link.defaultgw == 1 then + defaultgw_color = "#ffff99" + end + + rv[#rv+1] = { + rip = link["Remote IP"], + hn = link["Hostname"], + lip = link["Local IP"], + dev = link["Local Device"], + lq = link.LQ, + nlq = link.NLQ, + cost = string.format("%.3f", link.Cost), + color = color, + dfgcolor = defaultgw_color + } + end + luci.http.prepare_content("application/json") + luci.http.write_json(rv) + return +end +%> + + + + + +

<%:OLSR Links%>

+ +
+ <%:Overview of currently established OLSR connections (device-to-device)%> + + + + + + + + + + + <% local i = 1 + for k, link in ipairs(links) do + link.Cost = tonumber(link.Cost) or 0 + color = olsrtools.etx_color(link.Cost) + + defaultgw_color = "" + if link.defaultgw == 1 then + defaultgw_color = "#ffff99" + end + %> + + + + + + + <% + i = ((i % 2) + 1) + end %> + +
<%:Hostname%><%:IP Address%><%:ETX Value%> + + help + +
<% if link["Hostname"] then %><%=link["Hostname"]%><% else %>?<% end %><%=link["Remote IP"]%><%=string.format("%.3f", link.Cost)%>
+
+ +

<%:Legend%>:

+
    +
  • ETX: <%:Expected retransmission count - the expected number of retransmission attempts that are required for a packet to travel to and from a destination.%>
  • +
  • <%:Green%>:<%:Very good (ETX < 2)%>
  • +
  • <%:Yellow%>:<%:Good (2 < ETX < 4)%>
  • +
  • <%:Orange%>:<%:Still usable (4 < ETX < 10)%>
  • +
  • <%:Red%>:<%:Bad (ETX > 10)%>
  • +
+
diff --git a/luasrc/view/commotion/revert.htm b/luasrc/view/commotion/revert.htm new file mode 100644 index 0000000..838e022 --- /dev/null +++ b/luasrc/view/commotion/revert.htm @@ -0,0 +1,51 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +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: revert.htm 9014 2012-08-14 13:08:18Z jow $ + +-%> + +<%SW = require "luci.commotion.setup_wizard"%> +<%sw_stat = SW.status()%> + +<%+header%> + +

<%:Revert%>

+ +<% if changes then %> + <%+cbi/apply_xhr%> + <%+admin_uci/changelog%> +

<%:Reverting changes... +Please be patient, this may take a few moments. Once the settings are reset click "Start Over" to return to the beginning of the Setup Wizard.%>:

+ <%- uci_changelog(changes) -%> +<% else %> +

<%:There are no pending changes to revert!%>

+<% end %> +<%if sw_stat then%> +
+<%end%> +
+ <%if sw_stat then%> +
+
+ +
+
+ <%else%> +
+ +
+ <%end%> +
+<%if sw_stat then%> +
+<%end%> +<%+footer%> diff --git a/luasrc/view/commotion/status.htm b/luasrc/view/commotion/status.htm new file mode 100644 index 0000000..1660cb3 --- /dev/null +++ b/luasrc/view/commotion/status.htm @@ -0,0 +1,55 @@ +<%#- +--! @param gateway_provided String "Yes" if node is providing gateway to (mesh/ap?) "No" if not providing a gateway. +-%> +<% + sys = require "luci.sys" + --Sets tabs to select current page + tabs = {nd = "nearby_devices", + mv = "mesh_visualizer", + cc = "connected_clients", + dr = "debug_report"} + for i,x in pairs(tabs) do + if x == active_tab then + tabs[i] = "cbi-tab active" + else + tabs[i] = "cbi-tab-disabled" + end + end + %> + +<%+header%> + +
+
+ <%:Providing Gateway%> + <%=gateway_provided%> +


+ <%- for _,iface in pairs(ifaces) do -%> +
+ <%=iface.name%> + (<%=iface.status%>) (<%=iface.sec%>) (<%=iface.conn%> <%:Client Connections%>) +
+ <%end%> +
+
+
+ +
+<%include(page, {assets=assets})%> + +<%+footer%> diff --git a/luasrc/view/commotion/viz.htm b/luasrc/view/commotion/viz.htm new file mode 100644 index 0000000..0641ba4 --- /dev/null +++ b/luasrc/view/commotion/viz.htm @@ -0,0 +1,7 @@ + + + diff --git a/luasrc/view/commotion/welcome.htm b/luasrc/view/commotion/welcome.htm new file mode 100644 index 0000000..de144ee --- /dev/null +++ b/luasrc/view/commotion/welcome.htm @@ -0,0 +1,36 @@ +<%# +Copyright (C) 2013 Seamus Tuohy + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +along with this program. If not, see . +%> +<%+header%> + +<%- + local disp = require "luci.dispatcher" + local http = require "luci.http" + setup = disp.build_url("commotion", "setup_wizard", "start") + advanced = disp.build_url("commotion", "advanced") +-%> +
+

<%:Welcome to Commotion%>

+ +

<%:This router has not yet been set up. Use the Setup Wizard for step-by-step configuration, upload a configuration file if you have one from a previously configured router, or choose Advanced for manual GUI or CLI configuration.%>

+ + +<%+footer%>