From 6509e007e29f438d74b01eb6e23a09f02775dd6c Mon Sep 17 00:00:00 2001 From: Daniel Dickinson Date: Tue, 1 Dec 2015 23:02:38 -0500 Subject: luci-base: Make default for FileUpload 'safe' Some files and pointers to files are not safe to remove without a replacement file and config pointing to the file. For instance for uhttpd application in the works, removing the certificate or key config or files without having the replacements in places renders the WeUI inaccessible. The only other place where FileUpload is currently used is for wifi certificates for which the 'safe' handling is also preferred. Therefore make the default for the FileUpload widget the safe handling and add a property self.unsafeupload that allows for the old unsafe handling should it prove useful in some case. Also allow to specify a file already on router instead of uploading a file. Signed-off By: Daniel Dickinson --- modules/luci-base/luasrc/cbi.lua | 58 +++++++++++++++++++++------- modules/luci-base/luasrc/view/cbi/upload.htm | 16 ++++++-- 2 files changed, 57 insertions(+), 17 deletions(-) (limited to 'modules/luci-base/luasrc') diff --git a/modules/luci-base/luasrc/cbi.lua b/modules/luci-base/luasrc/cbi.lua index b5b2c8d8f..9b88e9fd9 100644 --- a/modules/luci-base/luasrc/cbi.lua +++ b/modules/luci-base/luasrc/cbi.lua @@ -1811,6 +1811,7 @@ function Button.__init__(self, ...) self.template = "cbi/button" self.inputstyle = nil self.rmempty = true + self.unsafeupload = false end @@ -1827,9 +1828,15 @@ function FileUpload.__init__(self, ...) end function FileUpload.formcreated(self, section) - return AbstractValue.formcreated(self, section) or - self.map:formvalue("cbi.rlf."..section.."."..self.option) or - self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") + if self.unsafeupload then + return AbstractValue.formcreated(self, section) or + self.map:formvalue("cbi.rlf."..section.."."..self.option) or + self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") or + self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox") + else + return AbstractValue.formcreated(self, section) or + self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox") + end end function FileUpload.cfgvalue(self, section) @@ -1840,27 +1847,50 @@ function FileUpload.cfgvalue(self, section) return nil end +-- If we have a new value, use it +-- otherwise use old value +-- deletion should be managed by a separate button object +-- unless self.unsafeupload is set in which case if the user +-- choose to remove the old file we do so. +-- Also, allow to specify (via textbox) a file already on router function FileUpload.formvalue(self, section) local val = AbstractValue.formvalue(self, section) if val then - if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and - not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") - then + if self.unsafeupload then + if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and + not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") + then + return val + end + fs.unlink(val) + self.value = nil + return nil + elseif val ~= "" then return val - end - fs.unlink(val) - self.value = nil + end end - return nil + val = luci.http.formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox") + if val == "" then + val = nil + end + if not self.unsafeupload then + if not val then + val = self.map:formvalue("cbi.rlf."..section.."."..self.option) + end + end + return val end function FileUpload.remove(self, section) - local val = AbstractValue.formvalue(self, section) - if val and fs.access(val) then fs.unlink(val) end - return AbstractValue.remove(self, section) + if self.unsafeupload then + local val = AbstractValue.formvalue(self, section) + if val and fs.access(val) then fs.unlink(val) end + return AbstractValue.remove(self, section) + else + return nil + end end - FileBrowser = class(AbstractValue) function FileBrowser.__init__(self, ...) diff --git a/modules/luci-base/luasrc/view/cbi/upload.htm b/modules/luci-base/luasrc/view/cbi/upload.htm index 777093411..157f3b36f 100644 --- a/modules/luci-base/luasrc/view/cbi/upload.htm +++ b/modules/luci-base/luasrc/view/cbi/upload.htm @@ -6,9 +6,19 @@ <%+cbi/valueheader%> <% if s then %> <%:Uploaded File%> (<%=t.byte_format(s.size)%>) - /> - " alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" /> - <% else %> + <% if self.unsafeupload then %> + /> + " alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" /> + <% end %> + <% end %> + + <% if not self.unsafeupload then %> + /> + <% end %> + + <% if (not s) or (s and not self.unsafeupload) then %> /> <% end %> + /> <%+cbi/valuefooter%> -- cgit v1.2.3 From 38880407aa6c0246bc9af52073a79ff4a2096a6e Mon Sep 17 00:00:00 2001 From: Daniel Dickinson Date: Mon, 14 Dec 2015 07:38:31 -0500 Subject: modules/luci-base: Fix ipaddrport validator to support ipv6 The previous versiono of ipaddrport validator only worked for ipv4 due to disallowing colons (:) in ip address which obvious fails for ipv6. We now instead allow either ipv4 address or an ipv6 address of the form []:port --- .../luci-base/htdocs/luci-static/resources/cbi.js | 25 ++++++++++++++++++++-- modules/luci-base/luasrc/cbi/datatypes.lua | 18 ++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) (limited to 'modules/luci-base/luasrc') diff --git a/modules/luci-base/htdocs/luci-static/resources/cbi.js b/modules/luci-base/htdocs/luci-static/resources/cbi.js index 8a3cb6fca..6ce947def 100644 --- a/modules/luci-base/htdocs/luci-static/resources/cbi.js +++ b/modules/luci-base/htdocs/luci-static/resources/cbi.js @@ -172,17 +172,38 @@ var cbi_validators = { return false; }, - 'ipaddrport': function() + 'ip4addrport': function() { var hp = this.split(/:/); if (hp.length == 2) return (cbi_validators.ipaddr.apply(hp[0]) && cbi_validators.port.apply(hp[1])); - return false; }, + 'ipaddrport': function(bracket) + { + if (this.match(/^([^\[\]:]+):([^:]+)$/)) { + var addr = RegExp.$1 + var port = RegExp.$2 + return (cbi_validators.ip4addr.apply(addr) && + cbi_validators.port.apply(port)); + } else if ((bracket == 1) && (this.match(/^\[(.+)\]:([^:]+)$/))) { + var addr = RegExp.$1 + var port = RegExp.$2 + return (cbi_validators.ip6addr.apply(addr) && + cbi_validators.port.apply(port)); + } else if ((bracket != 1) && (this.match(/^([^\[\]]+):([^:]+)$/))) { + var addr = RegExp.$1 + var port = RegExp.$2 + return (cbi_validators.ip6addr.apply(addr) && + cbi_validators.port.apply(port)); + } else { + return false; + } + }, + 'wpakey': function() { var v = this; diff --git a/modules/luci-base/luasrc/cbi/datatypes.lua b/modules/luci-base/luasrc/cbi/datatypes.lua index 52f90afee..aafdc5c3c 100644 --- a/modules/luci-base/luasrc/cbi/datatypes.lua +++ b/modules/luci-base/luasrc/cbi/datatypes.lua @@ -189,9 +189,23 @@ function hostport(val) return not not (h and p and host(h) and port(p)) end -function ipaddrport(val) +function ip4addrport(val) local h, p = val:match("^([^:]+):([^:]+)$") - return not not (h and p and ipaddr(h) and port(p)) + return (h and p and ip4addr(h) and port(p)) +end + +function ipaddrport(val, bracket) + local h, p = val:match("^([^%[%]:]+):([^:]+)$") + if (h and p and ip4addr(h) and port(p)) then + return true + elseif (bracket == 1) then + h, p = val:match("^(%[.+%]):([^:]+)$") + if (h and p and ip6addr(h) and port(p)) then + return true + end + end + h, p = val:match("^([^%[%]]+):([^:]+)$") + return (h and p and ip6addr(h) and port(p)) end function wpakey(val) -- cgit v1.2.3 From 0ec3f91a0c7a8c71980f5e70f3322397e496a8d6 Mon Sep 17 00:00:00 2001 From: Daniel Dickinson Date: Mon, 14 Dec 2015 22:51:29 -0500 Subject: validation: Add option ipv4only option to host and hostport datatypes Some applications only support ipv4 so add ipv4only option to host and hostport datatypes so that for thos applications that when an IP address is specified only and ipv4 ip address gets accepted. --- .../luasrc/model/cbi/pbx-voicemail.lua | 2 +- .../luci-app-pbx/luasrc/model/cbi/pbx-advanced.lua | 2 +- applications/luci-app-pbx/luasrc/model/cbi/pbx-voip.lua | 6 +++--- .../luasrc/model/cbi/luci_statistics/olsrd.lua | 2 +- .../luci-app-tinyproxy/luasrc/model/cbi/tinyproxy.lua | 3 ++- .../luasrc/model/cbi/watchcat/watchcat.lua | 2 +- modules/luci-base/htdocs/luci-static/resources/cbi.js | 9 +++++---- modules/luci-base/luasrc/cbi/datatypes.lua | 15 ++++++++++----- .../luasrc/model/cbi/admin_network/dhcp.lua | 2 +- .../luasrc/model/cbi/admin_network/wifi.lua | 4 ++-- .../luasrc/model/cbi/admin_system/system.lua | 2 +- .../luasrc/model/cbi/admin_network/proto_aiccu.lua | 2 +- .../luasrc/model/cbi/admin_network/proto_openconnect.lua | 2 +- .../luasrc/model/cbi/admin_network/proto_l2tp.lua | 2 +- .../luasrc/model/cbi/admin_network/proto_pppossh.lua | 2 +- .../luasrc/model/cbi/admin_network/proto_pptp.lua | 2 +- 16 files changed, 33 insertions(+), 26 deletions(-) (limited to 'modules/luci-base/luasrc') diff --git a/applications/luci-app-pbx-voicemail/luasrc/model/cbi/pbx-voicemail.lua b/applications/luci-app-pbx-voicemail/luasrc/model/cbi/pbx-voicemail.lua index 9ff2ed97f..a6087e9ae 100644 --- a/applications/luci-app-pbx-voicemail/luasrc/model/cbi/pbx-voicemail.lua +++ b/applications/luci-app-pbx-voicemail/luasrc/model/cbi/pbx-voicemail.lua @@ -92,7 +92,7 @@ s = m:section(NamedSection, "voicemail_smtp", "voicemail", translate("Outgoing m s.anonymous = true serv = s:option(Value, "smtp_server", translate("SMTP Server Hostname or IP Address")) -serv.datatype = "host" +serv.datatype = "host(0)" port = s:option(Value, "smtp_port", translate("SMTP Port Number")) port.datatype = "port" diff --git a/applications/luci-app-pbx/luasrc/model/cbi/pbx-advanced.lua b/applications/luci-app-pbx/luasrc/model/cbi/pbx-advanced.lua index 5d4f135c5..34288c663 100644 --- a/applications/luci-app-pbx/luasrc/model/cbi/pbx-advanced.lua +++ b/applications/luci-app-pbx/luasrc/model/cbi/pbx-advanced.lua @@ -264,7 +264,7 @@ h = s:taboption("remote_usage", Value, "externhost", translate("Domain/IP Addres The best thing to input is a static IP address. If your IP address is dynamic and it changes, \ your configuration will become invalid. Hence, it's recommended to set up Dynamic DNS in this case. \ and enter your Dynamic DNS hostname here. You can configure Dynamic DNS with the luci-app-ddns package.")) -h.datatype = "host" +h.datatype = "host(0)" p = s:taboption("remote_usage", Value, "bindport", translate("External SIP Port"), translate("Pick a random port number between 6500 and 9500 for the service to listen on. \ diff --git a/applications/luci-app-pbx/luasrc/model/cbi/pbx-voip.lua b/applications/luci-app-pbx/luasrc/model/cbi/pbx-voip.lua index ed1ed1edb..9b4620285 100644 --- a/applications/luci-app-pbx/luasrc/model/cbi/pbx-voip.lua +++ b/applications/luci-app-pbx/luasrc/model/cbi/pbx-voip.lua @@ -84,7 +84,7 @@ function pwd.write(self, section, value) end h = s:option(Value, "host", translate("SIP Server/Registrar")) -h.datatype = "host" +h.datatype = "host(0)" p = s:option(ListValue, "register", translate("Enable Incoming Calls (Register via SIP)"), translate("This option should be set to \"Yes\" if you have a DID \(real telephone number\) \ @@ -103,7 +103,7 @@ p.default = "yes" from = s:option(Value, "fromdomain", translate("SIP Realm (needed by some providers)")) from.optional = true -from.datatype = "host" +from.datatype = "host(0)" port = s:option(Value, "port", translate("SIP Server/Registrar Port")) port.optional = true @@ -111,6 +111,6 @@ port.datatype = "port" op = s:option(Value, "outboundproxy", translate("Outbound Proxy")) op.optional = true -op.datatype = "host" +op.datatype = "host(0)" return m diff --git a/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/olsrd.lua b/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/olsrd.lua index 59266e553..950d7a797 100644 --- a/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/olsrd.lua +++ b/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/olsrd.lua @@ -12,7 +12,7 @@ enable.default = 0 host = s:option(Value, "Host", translate("Host"), translate("IP or hostname where to get the txtinfo output from")) host.placeholder = "127.0.0.1" -host.datatype = "host" +host.datatype = "host(1)" host.rmempty = true port = s:option(Value, "Port", translate("Port")) diff --git a/applications/luci-app-tinyproxy/luasrc/model/cbi/tinyproxy.lua b/applications/luci-app-tinyproxy/luasrc/model/cbi/tinyproxy.lua index 19bdd9afc..11f34fbad 100644 --- a/applications/luci-app-tinyproxy/luasrc/model/cbi/tinyproxy.lua +++ b/applications/luci-app-tinyproxy/luasrc/model/cbi/tinyproxy.lua @@ -227,7 +227,7 @@ ta = s:option(Value, "target", translate("Target host"), ta.rmempty = true ta.placeholder = "0.0.0.0/0" -ta.datatype = "host" +ta.datatype = "host(1)" v = s:option(Value, "via", translate("Via proxy"), @@ -235,5 +235,6 @@ v = s:option(Value, "via", translate("Via proxy"), v:depends({type="proxy"}) v.placeholder = "10.0.0.1:8080" +v.datatype = "ip4addrport" return m diff --git a/applications/luci-app-watchcat/luasrc/model/cbi/watchcat/watchcat.lua b/applications/luci-app-watchcat/luasrc/model/cbi/watchcat/watchcat.lua index 883416b21..f64370bfe 100644 --- a/applications/luci-app-watchcat/luasrc/model/cbi/watchcat/watchcat.lua +++ b/applications/luci-app-watchcat/luasrc/model/cbi/watchcat/watchcat.lua @@ -38,7 +38,7 @@ period = s:option(Value, "period", pinghost = s:option(Value, "pinghosts", translate("Ping host"), translate("Host address to ping")) -pinghost.datatype = "host" +pinghost.datatype = "host(1)" pinghost.default = "8.8.8.8" pinghost:depends({mode="ping"}) diff --git a/modules/luci-base/htdocs/luci-static/resources/cbi.js b/modules/luci-base/htdocs/luci-static/resources/cbi.js index 6ce947def..8159a378f 100644 --- a/modules/luci-base/htdocs/luci-static/resources/cbi.js +++ b/modules/luci-base/htdocs/luci-static/resources/cbi.js @@ -139,10 +139,11 @@ var cbi_validators = { return (this.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null); }, - 'host': function() + 'host': function(ipv4only) { return cbi_validators.hostname.apply(this) || - cbi_validators.ipaddr.apply(this); + ((ipv4only != 1) && cbi_validators.ipaddr.apply(this)) || + ((ipv4only == 1) && cb_validators.ip4addr.apply(this)); }, 'hostname': function() @@ -161,12 +162,12 @@ var cbi_validators = { cbi_validators.host.apply(this); }, - 'hostport': function() + 'hostport': function(ipv4only) { var hp = this.split(/:/); if (hp.length == 2) - return (cbi_validators.host.apply(hp[0]) && + return (cbi_validators.host.apply(hp[0], ipv4only) && cbi_validators.port.apply(hp[1])); return false; diff --git a/modules/luci-base/luasrc/cbi/datatypes.lua b/modules/luci-base/luasrc/cbi/datatypes.lua index aafdc5c3c..d415c6f58 100644 --- a/modules/luci-base/luasrc/cbi/datatypes.lua +++ b/modules/luci-base/luasrc/cbi/datatypes.lua @@ -176,17 +176,22 @@ function hostname(val) return false end -function host(val) - return hostname(val) or ipaddr(val) +function host(val, ipv4only) + return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val)) end function network(val) return uciname(val) or host(val) end -function hostport(val) +function hostport(val, ipv4only) local h, p = val:match("^([^:]+):([^:]+)$") - return not not (h and p and host(h) and port(p)) + return not not (h and p and host(h, ipv4only) and port(p)) +end + +function ip4addrport(val, bracket) + local h, p = val:match("^([^:]+):([^:]+)$") + return (h and p and ip4addr(h) and port(p)) end function ip4addrport(val) @@ -199,7 +204,7 @@ function ipaddrport(val, bracket) if (h and p and ip4addr(h) and port(p)) then return true elseif (bracket == 1) then - h, p = val:match("^(%[.+%]):([^:]+)$") + h, p = val:match("^%[(.+)%]:([^:]+)$") if (h and p and ip6addr(h) and port(p)) then return true end diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua index 4dc52ada0..572446fef 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua @@ -135,7 +135,7 @@ rd = s:taboption("general", DynamicList, "rebind_domain", translate("List of domains to allow RFC1918 responses for")) rd:depends("rebind_protection", "1") -rd.datatype = "host" +rd.datatype = "host(1)" rd.placeholder = "ihost.netflix.com" diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua index b91c29b08..44528927e 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua @@ -749,7 +749,7 @@ auth_server:depends({mode="ap", encryption="wpa2"}) auth_server:depends({mode="ap-wds", encryption="wpa"}) auth_server:depends({mode="ap-wds", encryption="wpa2"}) auth_server.rmempty = true -auth_server.datatype = "host" +auth_server.datatype = "host(0)" auth_port = s:taboption("encryption", Value, "auth_port", translate("Radius-Authentication-Port"), translatef("Default %d", 1812)) auth_port:depends({mode="ap", encryption="wpa"}) @@ -773,7 +773,7 @@ acct_server:depends({mode="ap", encryption="wpa2"}) acct_server:depends({mode="ap-wds", encryption="wpa"}) acct_server:depends({mode="ap-wds", encryption="wpa2"}) acct_server.rmempty = true -acct_server.datatype = "host" +acct_server.datatype = "host(0)" acct_port = s:taboption("encryption", Value, "acct_port", translate("Radius-Accounting-Port"), translatef("Default %d", 1813)) acct_port:depends({mode="ap", encryption="wpa"}) diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/system.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/system.lua index 94ba8053a..2874b5607 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/system.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/system.lua @@ -204,7 +204,7 @@ if has_ntpd then o = s:option(DynamicList, "server", translate("NTP server candidates")) - o.datatype = "host" + o.datatype = "host(0)" o:depends("enable", "1") -- retain server list even if disabled diff --git a/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua index 9315dc25b..7b5e6043c 100644 --- a/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua +++ b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua @@ -39,7 +39,7 @@ protocol.optional = true server = section:taboption("general", Value, "server", translate("Tunnel setup server"), translate("Optional, specify to override default server (tic.sixxs.net)")) -server.datatype = "host" +server.datatype = "host(0)" server.optional = true diff --git a/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua index 4ed34ae3b..ad3f2edf0 100644 --- a/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua +++ b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua @@ -13,7 +13,7 @@ oc_key_file = "/etc/openconnect/user-key-" .. ifc .. ".pem" oc_ca_file = "/etc/openconnect/ca-" .. ifc .. ".pem" server = section:taboption("general", Value, "server", translate("VPN Server")) -server.datatype = "host" +server.datatype = "host(0)" port = section:taboption("general", Value, "port", translate("VPN Server port")) port.placeholder = "443" diff --git a/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_l2tp.lua b/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_l2tp.lua index 5e8b3fcaa..506170c2d 100644 --- a/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_l2tp.lua +++ b/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_l2tp.lua @@ -8,7 +8,7 @@ local ipv6, defaultroute, metric, peerdns, dns, mtu server = section:taboption("general", Value, "server", translate("L2TP Server")) -server.datatype = "or(host, hostport)" +server.datatype = "or(host(1), hostport(1))" username = section:taboption("general", Value, "username", translate("PAP/CHAP username")) diff --git a/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pppossh.lua b/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pppossh.lua index c93353af9..eca7bdd4d 100644 --- a/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pppossh.lua +++ b/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pppossh.lua @@ -8,7 +8,7 @@ local sshuser, server, port, ssh_options, identity, ipaddr, peeraddr sshuser = section:taboption("general", Value, "sshuser", translate("SSH username")) server = section:taboption("general", Value, "server", translate("SSH server address")) -server.datatype = "host" +server.datatype = "host(0)" port = section:taboption("general", Value, "port", translate("SSH server port")) port.datatype = "port" diff --git a/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pptp.lua b/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pptp.lua index d81db1fae..462d7019f 100644 --- a/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pptp.lua +++ b/protocols/luci-proto-ppp/luasrc/model/cbi/admin_network/proto_pptp.lua @@ -9,7 +9,7 @@ local defaultroute, metric, peerdns, dns, server = section:taboption("general", Value, "server", translate("VPN Server")) -server.datatype = "host" +server.datatype = "host(0)" username = section:taboption("general", Value, "username", translate("PAP/CHAP username")) -- cgit v1.2.3 From b130ca554f13e17c787a1c6fd09e67dd7727a4d3 Mon Sep 17 00:00:00 2001 From: Daniel Dickinson Date: Tue, 15 Dec 2015 11:37:33 -0500 Subject: lib-nixio / luci-base: Fix for reading csrf token prevents file upload The call to http.formvalue in order to read the csrf token causes _parse_input to be triggered *before* controllers and cbi maps have been built. This results in the failure of file uploads because the file handler is not yet in place when _parse_input gets called, and it is in _parse_input that POST data is parsed (including files). To fix this we add the ability to write file fields to temporary files (using mkstemp and unlink in nixio.file) and use this to store file data until the filehandler is registered, with a fallback to reading the file data into memory. Once the filehandler callback gets registered we iterate though all previously parsed (saved) files and copy the data to the file handler, and then close the temporary file (which finally removes because we unlinked after creating the file, but didn't close the file so unlink was deferred). Signed-off-by: Daniel Dickinson --- libs/luci-lib-nixio/src/file.c | 33 ++++++++++++++++++++++++++++++ modules/luci-base/luasrc/http.lua | 31 ++++++++++++++++++++++++++++ modules/luci-base/luasrc/http/protocol.lua | 26 +++++++++++++++++++++++ 3 files changed, 90 insertions(+) (limited to 'modules/luci-base/luasrc') diff --git a/libs/luci-lib-nixio/src/file.c b/libs/luci-lib-nixio/src/file.c index b86e040e1..cfa35dfd1 100644 --- a/libs/luci-lib-nixio/src/file.c +++ b/libs/luci-lib-nixio/src/file.c @@ -79,6 +79,38 @@ static int nixio_open(lua_State *L) { return 1; } +static int nixio_mkstemp(lua_State *L) { + const char *intemplate = luaL_checklstring(L, 1, NULL); + size_t len = lua_strlen(L, 1); + char *template = (char *)lua_newuserdata(L, 13 + len); + if (!template) { + return luaL_error(L, "out of memory"); + } + snprintf(template, 13 + len, "/tmp/%s.XXXXXX", intemplate); + + int fd; + + do { + fd = mkstemp(template); + } while (fd == -1 && errno == EINTR); + if (fd == -1) { + return nixio__perror(L); + } + unlink(template); + + int *udata = lua_newuserdata(L, sizeof(int)); + if (!udata) { + return luaL_error(L, "out of memory"); + } + + *udata = fd; + + luaL_getmetatable(L, NIXIO_FILE_META); + lua_setmetatable(L, -2); + + return 1; +} + static int nixio_open_flags(lua_State *L) { int mode = 0; const int j = lua_gettop(L); @@ -366,6 +398,7 @@ static const luaL_reg R[] = { {"dup", nixio_dup}, {"open", nixio_open}, {"open_flags", nixio_open_flags}, + {"mkstemp", nixio_mkstemp}, {"pipe", nixio_pipe}, {NULL, NULL} }; diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua index 4b3573172..8795dfc4b 100644 --- a/modules/luci-base/luasrc/http.lua +++ b/modules/luci-base/luasrc/http.lua @@ -89,6 +89,37 @@ end function Request.setfilehandler(self, callback) self.filehandler = callback + + -- If input has already been parsed then any files are either in temporary files + -- or are in self.message.params[key] + if self.parsed_input then + for param, value in pairs(self.message.params) do + repeat + -- We're only interested in files + if (not value["file"]) then break end + -- If we were able to write to temporary file + if (value["fd"]) then + fd = value["fd"] + local eof = false + repeat + filedata = fd:read(1024) + if (filedata:len() < 1024) then + eof = true + end + callback({ name=value["name"], file=value["file"] }, filedata, eof) + until (eof) + fd:close() + value["fd"] = nil + -- We had to read into memory + else + -- There should only be one numbered value in table - the data + for k, v in ipairs(value) do + callback({ name=value["name"], file=value["file"] }, v, true) + end + end + until true + end + end end function Request._parse_input(self) diff --git a/modules/luci-base/luasrc/http/protocol.lua b/modules/luci-base/luasrc/http/protocol.lua index 0cb62aeec..061c6ad54 100644 --- a/modules/luci-base/luasrc/http/protocol.lua +++ b/modules/luci-base/luasrc/http/protocol.lua @@ -113,6 +113,16 @@ local function __initval( tbl, key ) end end +-- (Internal function) +-- Initialize given file parameter. +local function __initfileval( tbl, key, filename, fd ) + if tbl[key] == nil then + tbl[key] = { file=filename, fd=fd, name=key, "" } + else + table.insert( tbl[key], "" ) + end +end + -- (Internal function) -- Append given data to given parameter, either by extending the string value -- or by appending it to the last string in the parameter's value table. @@ -313,6 +323,22 @@ function mimedecode_message_body( src, msg, filecb ) __appendval( msg.params, field.name, field.file ) store = filecb + elseif field.name and field.file then + local nxf = require "nixio" + local fd = nxf.mkstemp(field.name) + __initfileval ( msg.params, field.name, field.file, fd ) + if fd then + store = function(hdr, buf, eof) + fd:write(buf) + if (eof) then + fd:seek(0, "set") + end + end + else + store = function( hdr, buf, eof ) + __appendval( msg.params, field.name, buf ) + end + end elseif field.name then __initval( msg.params, field.name ) -- cgit v1.2.3