Merge pull request #2235 from dibdot/ovpn-upload
luci-app-openvpn: add ovpn upload support & more
This commit is contained in:
commit
1a5b613b87
7 changed files with 294 additions and 36 deletions
|
@ -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
|
||||
|
|
|
@ -85,4 +85,3 @@ for _, option in ipairs(basicParams) do
|
|||
end
|
||||
|
||||
return m
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1,11 +1,111 @@
|
|||
<div class="cbi-section-create">
|
||||
<% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
|
||||
<input type="text" class="cbi-section-create-name" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.text" />
|
||||
<select class="cbi-section-create-name" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.select">
|
||||
<%- for k, v in luci.util.kspairs(self.add_select_options) do %>
|
||||
<option value="<%=k%>"><%=luci.util.pcdata(v)%></option>
|
||||
<% end -%>
|
||||
</select>
|
||||
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>" title="<%:Add%>" />
|
||||
<% if self.invalid_cts then %><br /><%:Invalid%></div><% end %>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function vpn_add()
|
||||
{
|
||||
var vpn_name = div_add.querySelector("#instance_name1").value.replace(/[^\x00-\x7F]|[\s!@#$%^&*()+=\[\]{};':"\\|,<>\/?]/g,'');
|
||||
var vpn_template = div_add.querySelector("#instance_template").value;
|
||||
var form = document.getElementsByName('cbi')[0];
|
||||
|
||||
if (!vpn_name || !vpn_name.length)
|
||||
{
|
||||
return info_message(vpn_output, "<%=pcdata(translate("The 'Name' field must not be empty!"))%>", 2000);
|
||||
}
|
||||
|
||||
document.getElementById("instance_name1").value = vpn_name;
|
||||
if (document.getElementById("cbi-openvpn-" + vpn_name) != null)
|
||||
{
|
||||
return info_message(vpn_output, "<%=pcdata(translate("Instance with that name already exists!"))%>", 2000);
|
||||
}
|
||||
|
||||
if (!vpn_template || !vpn_template.length)
|
||||
{
|
||||
return info_message(vpn_output, "<%=pcdata(translate("Please select a valid VPN template!"))%>", 2000);
|
||||
}
|
||||
|
||||
if (form)
|
||||
{
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function vpn_upload()
|
||||
{
|
||||
var vpn_name = div_upload.querySelector("#instance_name2").value.replace(/[^\x00-\x7F]|[\s!@#$%^&*()+=\[\]{};':"\\|,<>\/?]/g,'');
|
||||
var vpn_file = document.getElementById("ovpn_file").value;
|
||||
var form = document.getElementsByName('cbi')[0];
|
||||
|
||||
if (!vpn_name || !vpn_name.length)
|
||||
{
|
||||
return info_message(vpn_output, "<%=pcdata(translate("The 'Name' field must not be empty!"))%>", 2000);
|
||||
}
|
||||
|
||||
document.getElementById("instance_name2").value = vpn_name;
|
||||
if (document.getElementById("cbi-openvpn-" + vpn_name) != null)
|
||||
{
|
||||
return info_message(vpn_output, "<%=pcdata(translate("Instance with that name already exists!"))%>", 2000);
|
||||
}
|
||||
|
||||
if (!vpn_file || !vpn_file.length)
|
||||
{
|
||||
return info_message(vpn_output, "<%=pcdata(translate("Please select a valid OVPN config file to upload!"))%>", 2000);
|
||||
}
|
||||
|
||||
if (form)
|
||||
{
|
||||
form.enctype = 'multipart/form-data';
|
||||
form.action = '<%=url('admin/services/openvpn/upload')%>';
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function info_message(output, msg, timeout)
|
||||
{
|
||||
timeout = timeout || 0;
|
||||
output.innerHTML = '<em>' + msg + '</em>';
|
||||
if (timeout > 0)
|
||||
{
|
||||
setTimeout(function(){ output.innerHTML=""}, timeout);
|
||||
}
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<%+openvpn/ovpn_css%>
|
||||
|
||||
<div class="cbi-section-node">
|
||||
<div class="table cbi-section-table">
|
||||
<h4><%:Template based configuration%></h4>
|
||||
<div class="tr cbi-section-table-row" id="div_add">
|
||||
<div class="td">
|
||||
<input type="text" maxlength="20" placeholder="Instance name" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.text" id="instance_name1" />
|
||||
</div>
|
||||
<div class="td">
|
||||
<select id="instance_template" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.select">
|
||||
<option value="" selected="selected" disabled="disabled"><%:Select template ...%></option>
|
||||
<%- for k, v in luci.util.kspairs(self.add_select_options) do %>
|
||||
<option value="<%=k%>"><%=luci.util.pcdata(v)%></option>
|
||||
<% end -%>
|
||||
</select>
|
||||
</div>
|
||||
<div class="td">
|
||||
<input class="cbi-button cbi-button-add" type="submit" onclick="vpn_add(); return false;" value="<%:Add%>" title="<%:Add template based configuration%>" /><br />
|
||||
</div>
|
||||
</div>
|
||||
<h4><%:OVPN configuration file upload%></h4>
|
||||
<div class="tr cbi-section-table-row" id="div_upload">
|
||||
<div class="td">
|
||||
<input type="text" maxlength="20" placeholder="Instance name" name="instance_name2" id="instance_name2" />
|
||||
</div>
|
||||
<div class="td">
|
||||
<input type="file" name="ovpn_file" id="ovpn_file" accept="application/x-openvpn-profile,.ovpn" />
|
||||
</div>
|
||||
<div class="td">
|
||||
<input class="cbi-button cbi-button-add" type="submit" onclick="vpn_upload(); return false;" value="<%:Upload%>" title="<%:Upload ovpn file%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vpn-output">
|
||||
<span id="vpn_output"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<style type="text/css">
|
||||
h4
|
||||
{
|
||||
white-space: nowrap;
|
||||
border-bottom: 0px;
|
||||
margin: 10px 5px 5px 5px;
|
||||
}
|
||||
.tr
|
||||
{
|
||||
border: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
.td
|
||||
{
|
||||
text-align: left;
|
||||
border-top: 0px;
|
||||
margin: 5px;
|
||||
}
|
||||
.vpn-output
|
||||
{
|
||||
box-shadow: none;
|
||||
margin: 10px 5px 5px 5px;
|
||||
color: #a22;
|
||||
}
|
||||
textarea
|
||||
{
|
||||
border: 1px solid #cccccc;
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
resize: none;
|
||||
white-space: pre;
|
||||
overflow-wrap: normal;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
a
|
||||
{
|
||||
line-height: 1.5;
|
||||
}
|
||||
hr
|
||||
{
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
</style>
|
|
@ -4,25 +4,31 @@
|
|||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+openvpn/ovpn_css%>
|
||||
|
||||
<div class="cbi-section">
|
||||
<h3>
|
||||
<a href="<%=url('admin/services/openvpn')%>"><%:Overview%></a> »
|
||||
<a href="<%=url('admin/services/openvpn')%>"><%:Overview%></a> »
|
||||
<%=luci.i18n.translatef("Instance \"%s\"", self.instance)%>
|
||||
</h3>
|
||||
|
||||
<% if self.mode == "basic" then %>
|
||||
<a href="<%=url('admin/services/openvpn/advanced', self.instance, "Service")%>"><%:Switch to advanced configuration »%></a>
|
||||
<% else %>
|
||||
<a href="<%=url('admin/services/openvpn/basic', self.instance)%>"><%:« Switch to basic configuration%></a>
|
||||
<hr style="margin:0.5em 0" />
|
||||
<% if self.mode == "file" then %>
|
||||
<a href="<%=url('admin/services/openvpn/basic', self.instance)%>"><%:Switch to basic configuration%> »</a><p/>
|
||||
<a href="<%=url('admin/services/openvpn/advanced', self.instance, "Service")%>"><%:Switch to advanced configuration%> »</a>
|
||||
<hr />
|
||||
<% elseif self.mode == "basic" then %>
|
||||
<a href="<%=url('admin/services/openvpn/advanced', self.instance, "Service")%>"><%:Switch to advanced configuration%> »</a><p/>
|
||||
<a href="<%=url('admin/services/openvpn/file', self.instance)%>"><%:Switch to file based configuration%> »</a>
|
||||
<hr />
|
||||
<% elseif self.mode == "advanced" then %>
|
||||
<a href="<%=url('admin/services/openvpn/basic', self.instance)%>"><%:Switch to basic configuration%> »</a><p/>
|
||||
<a href="<%=url('admin/services/openvpn/file', self.instance)%>"><%:Switch to file based configuration%> »</a>
|
||||
<hr />
|
||||
<%:Configuration category%>:
|
||||
<% for i, c in ipairs(self.categories) do %>
|
||||
<% if c == self.category then %>
|
||||
<strong><%=translate(c)%></strong>
|
||||
<% else %>
|
||||
<a href="<%=luci.dispatcher.build_url(
|
||||
"admin", "services", "openvpn", "advanced", self.instance, c
|
||||
)%>"><%=translate(c)%></a>
|
||||
<a href="<%=luci.dispatcher.build_url("admin", "services", "openvpn", "advanced", self.instance, c)%>"><%=translate(c)%></a>
|
||||
<% end %>
|
||||
<% if next(self.categories, i) then %>|<% end %>
|
||||
<% end %>
|
||||
|
|
Loading…
Reference in a new issue