From 0b04912f8d495ea836d565f3536b59d030225cfa Mon Sep 17 00:00:00 2001 From: Dirk Brenken Date: Sat, 20 Oct 2018 21:22:49 +0200 Subject: [PATCH] luci-app-openvpn: add ovpn upload support & more * add the ability to upload ovpn files directly, incl. appropriate uci entry in openvpn config * add the ability to edit ovpn files directly ('file' mode), beside the 'basic' and 'advanced' modes for normal setups * client side checks to validate instance name & template selection, incl. online error reporting * automatically remove non-ascii characters & windows line endings from transfered ovpn file * change from after_commit to after_apply hook * remove misleading default values for Port & Protocol in Overview Signed-off-by: Dirk Brenken --- .../luasrc/controller/openvpn.lua | 43 +++++++ .../luasrc/model/cbi/openvpn-basic.lua | 1 - .../luasrc/model/cbi/openvpn-file.lua | 61 +++++++++ .../luasrc/model/cbi/openvpn.lua | 35 ++--- .../view/openvpn/cbi-select-input-add.htm | 120 ++++++++++++++++-- .../luasrc/view/openvpn/ovpn_css.htm | 44 +++++++ .../luasrc/view/openvpn/pageswitch.htm | 26 ++-- 7 files changed, 294 insertions(+), 36 deletions(-) create mode 100644 applications/luci-app-openvpn/luasrc/model/cbi/openvpn-file.lua create mode 100644 applications/luci-app-openvpn/luasrc/view/openvpn/ovpn_css.htm diff --git a/applications/luci-app-openvpn/luasrc/controller/openvpn.lua b/applications/luci-app-openvpn/luasrc/controller/openvpn.lua index 2e48a469a..2c153c4cd 100644 --- a/applications/luci-app-openvpn/luasrc/controller/openvpn.lua +++ b/applications/luci-app-openvpn/luasrc/controller/openvpn.lua @@ -8,4 +8,47 @@ function index() entry( {"admin", "services", "openvpn"}, cbi("openvpn"), _("OpenVPN") ) entry( {"admin", "services", "openvpn", "basic"}, cbi("openvpn-basic"), nil ).leaf = true entry( {"admin", "services", "openvpn", "advanced"}, cbi("openvpn-advanced"), nil ).leaf = true + entry( {"admin", "services", "openvpn", "file"}, form("openvpn-file"), nil ).leaf = true + entry( {"admin", "services", "openvpn", "upload"}, call("ovpn_upload")) +end + +function ovpn_upload() + local fs = require("nixio.fs") + local http = require("luci.http") + local util = require("luci.util") + local uci = require("luci.model.uci").cursor() + local upload = http.formvalue("ovpn_file") + local name = util.shellquote(http.formvalue("instance_name2")) + local file = "/etc/openvpn/" ..name.. ".ovpn" + + if name and upload then + local fp + + http.setfilehandler( + function(meta, chunk, eof) + local data = util.trim(chunk:gsub("\r\n", "\n")) .. "\n" + data = util.trim(data:gsub("[\128-\255]", "")) + + if not fp and meta and meta.name == "ovpn_file" then + fp = io.open(file, "w") + end + if fp and data then + fp:write(data) + end + if fp and eof then + fp:close() + end + end + ) + + if fs.access(file) then + if not uci:get_first("openvpn", name) then + uci:set("openvpn", name, "openvpn") + uci:set("openvpn", name, "config", file) + uci:save("openvpn") + uci:commit("openvpn") + end + end + end + http.redirect(luci.dispatcher.build_url('admin/services/openvpn')) end diff --git a/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-basic.lua b/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-basic.lua index 483860c8e..6b6323e07 100644 --- a/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-basic.lua +++ b/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-basic.lua @@ -85,4 +85,3 @@ for _, option in ipairs(basicParams) do end return m - diff --git a/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-file.lua b/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-file.lua new file mode 100644 index 000000000..6878275d7 --- /dev/null +++ b/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-file.lua @@ -0,0 +1,61 @@ +-- Licensed to the public under the Apache License 2.0. + +local ip = require("luci.ip") +local fs = require("nixio.fs") +local util = require("luci.util") +local uci = require("luci.model.uci").cursor() +local cfg_file = uci:get("openvpn", arg[1], "config") + +local m = Map("openvpn") + +local p = m:section( SimpleSection ) +p.template = "openvpn/pageswitch" +p.mode = "file" +p.instance = arg[1] + +if not cfg_file or not fs.access(cfg_file) then + local f = SimpleForm("error", nil, translatef("The OVPN config file (%s) could not be found, please check your configuration.", cfg_file or "n/a")) + f:append(Template("openvpn/ovpn_css")) + f.reset = false + f.submit = false + return m, f +end + +if fs.stat(cfg_file).size >= 102400 then + f = SimpleForm("error", nil, + translatef("The size of the OVPN config file (%s) is too large for online editing in LuCI (≥ 100 KB). ", cfg_file) + .. translate("Please edit this file directly in a terminal session.")) + f:append(Template("openvpn/ovpn_css")) + f.reset = false + f.submit = false + return m, f +end + +f = SimpleForm("cfg", nil) +f:append(Template("openvpn/ovpn_css")) +f.submit = translate("Save") +f.reset = false + +s = f:section(SimpleSection, nil, translatef("This form allows you to modify the content of the OVPN config file (%s). ", cfg_file)) +file = s:option(TextValue, "data") +file.datatype = "string" +file.rows = 20 +file.rmempty = true + +function file.cfgvalue() + return fs.readfile(cfg_file) or "" +end + +function file.write(self, section, data) + return fs.writefile(cfg_file, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n") +end + +function file.remove(self, section, value) + return fs.writefile(cfg_file, "") +end + +function s.handle(self, state, data) + return true +end + +return m, f diff --git a/applications/luci-app-openvpn/luasrc/model/cbi/openvpn.lua b/applications/luci-app-openvpn/luasrc/model/cbi/openvpn.lua index e17aa4085..8f4859c0e 100644 --- a/applications/luci-app-openvpn/luasrc/model/cbi/openvpn.lua +++ b/applications/luci-app-openvpn/luasrc/model/cbi/openvpn.lua @@ -4,7 +4,7 @@ local fs = require "nixio.fs" local sys = require "luci.sys" local uci = require "luci.model.uci".cursor() -local testfullps = luci.sys.exec("ps --help 2>&1 | grep BusyBox") --check which ps do we have +local testfullps = sys.exec("ps --help 2>&1 | grep BusyBox") --check which ps do we have local psstring = (string.len(testfullps)>0) and "ps w" or "ps axfw" --set command we use to get pid local m = Map("openvpn", translate("OpenVPN")) @@ -13,9 +13,16 @@ s.template = "cbi/tblsection" s.template_addremove = "openvpn/cbi-select-input-add" s.addremove = true s.add_select_options = { } -s.extedit = luci.dispatcher.build_url( - "admin", "services", "openvpn", "basic", "%s" -) + +file_cfg = s:option(DummyValue, "config") +function file_cfg.cfgvalue(self, section) + local file_cfg = self.map:get(section, "config") + if file_cfg then + s.extedit = luci.dispatcher.build_url("admin", "services", "openvpn", "file", "%s") + else + s.extedit = luci.dispatcher.build_url("admin", "services", "openvpn", "basic", "%s") + end +end uci:load("openvpn_recipes") uci:foreach( "openvpn_recipes", "openvpn_recipe", @@ -61,10 +68,10 @@ function s.create(self, name) if s then local options = uci:get_all("openvpn_recipes", recipe) for k, v in pairs(options) do - uci:set("openvpn", name, k, v) + if k ~= "_role" and k ~= "_description" then + uci:set("openvpn", name, k, v) + end end - uci:delete("openvpn", name, "_role") - uci:delete("openvpn", name, "_description") uci:save("openvpn") luci.http.redirect( self.extedit:format(name) ) end @@ -75,7 +82,6 @@ function s.create(self, name) return 0 end - s:option( Flag, "enabled", translate("Enabled") ) local active = s:option( DummyValue, "_active", translate("Started") ) @@ -106,28 +112,27 @@ function updown.cfgvalue(self, section) end function updown.write(self, section, value) if self.option == "stop" then - luci.sys.call("/etc/init.d/openvpn stop %s" % section) + sys.call("/etc/init.d/openvpn stop %s" % section) else - luci.sys.call("/etc/init.d/openvpn start %s" % section) + sys.call("/etc/init.d/openvpn start %s" % section) end luci.http.redirect( self.redirect ) end - local port = s:option( DummyValue, "port", translate("Port") ) function port.cfgvalue(self, section) local val = AbstractValue.cfgvalue(self, section) - return val or "1194" + return val or "-" end local proto = s:option( DummyValue, "proto", translate("Protocol") ) function proto.cfgvalue(self, section) local val = AbstractValue.cfgvalue(self, section) - return val or "udp" + return val or "-" end -function m.on_after_commit(self,map) - require("luci.sys").call('/etc/init.d/openvpn reload') +function m.on_after_apply(self,map) + sys.call('/etc/init.d/openvpn reload') end return m diff --git a/applications/luci-app-openvpn/luasrc/view/openvpn/cbi-select-input-add.htm b/applications/luci-app-openvpn/luasrc/view/openvpn/cbi-select-input-add.htm index 0166de778..09da2eb22 100644 --- a/applications/luci-app-openvpn/luasrc/view/openvpn/cbi-select-input-add.htm +++ b/applications/luci-app-openvpn/luasrc/view/openvpn/cbi-select-input-add.htm @@ -1,11 +1,111 @@ -
- <% if self.invalid_cts then -%>
<% end %> - - - - <% if self.invalid_cts then %>
<%:Invalid%>
<% end %> + + + +<%+openvpn/ovpn_css%> + +
+
+

<%:Template based configuration%>

+
+
+ +
+
+ +
+
+
+
+
+

<%:OVPN configuration file upload%>

+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
diff --git a/applications/luci-app-openvpn/luasrc/view/openvpn/ovpn_css.htm b/applications/luci-app-openvpn/luasrc/view/openvpn/ovpn_css.htm new file mode 100644 index 000000000..c7062b8d7 --- /dev/null +++ b/applications/luci-app-openvpn/luasrc/view/openvpn/ovpn_css.htm @@ -0,0 +1,44 @@ + diff --git a/applications/luci-app-openvpn/luasrc/view/openvpn/pageswitch.htm b/applications/luci-app-openvpn/luasrc/view/openvpn/pageswitch.htm index 8cb019b46..17beef0d3 100644 --- a/applications/luci-app-openvpn/luasrc/view/openvpn/pageswitch.htm +++ b/applications/luci-app-openvpn/luasrc/view/openvpn/pageswitch.htm @@ -4,25 +4,31 @@ Licensed to the public under the Apache License 2.0. -%> +<%+openvpn/ovpn_css%> +

- <%:Overview%> » + <%:Overview%> » <%=luci.i18n.translatef("Instance \"%s\"", self.instance)%>

- - <% if self.mode == "basic" then %> - "><%:Switch to advanced configuration »%> - <% else %> - <%:« Switch to basic configuration%> -
+ <% if self.mode == "file" then %> + <%:Switch to basic configuration%> »

+ "><%:Switch to advanced configuration%> » +


+ <% elseif self.mode == "basic" then %> + "><%:Switch to advanced configuration%> »

+ <%:Switch to file based configuration%> » +


+ <% elseif self.mode == "advanced" then %> + <%:Switch to basic configuration%> »

+ <%:Switch to file based configuration%> » +


<%:Configuration category%>: <% for i, c in ipairs(self.categories) do %> <% if c == self.category then %> <%=translate(c)%> <% else %> - "><%=translate(c)%> + "><%=translate(c)%> <% end %> <% if next(self.categories, i) then %>|<% end %> <% end %> -- 2.25.1