luci-app-ddns: removing unneeded assert
[project/luci.git] / applications / luci-app-ddns / luasrc / tools / ddns.lua
1 -- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
2 -- Licensed to the public under the Apache License 2.0.
3
4 module("luci.tools.ddns", package.seeall)
5
6 local NX   = require "nixio"
7 local NXFS = require "nixio.fs"
8 local OPKG = require "luci.model.ipkg"
9 local UCI  = require "luci.model.uci"
10 local SYS  = require "luci.sys"
11 local UTIL = require "luci.util"
12
13 -- function to calculate seconds from given interval and unit
14 function calc_seconds(interval, unit)
15         if not tonumber(interval) then
16                 return nil
17         elseif unit == "days" then
18                 return (tonumber(interval) * 86400)     -- 60 sec * 60 min * 24 h
19         elseif unit == "hours" then
20                 return (tonumber(interval) * 3600)      -- 60 sec * 60 min
21         elseif unit == "minutes" then
22                 return (tonumber(interval) * 60)        -- 60 sec
23         elseif unit == "seconds" then
24                 return tonumber(interval)
25         else
26                 return nil
27         end
28 end
29
30 -- check if IPv6 supported by OpenWrt
31 function check_ipv6()
32         return NXFS.access("/proc/net/ipv6_route")
33            and NXFS.access("/usr/sbin/ip6tables")
34 end
35
36 -- check if Wget with SSL support or cURL installed
37 function check_ssl()
38         if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) then
39                 return true
40         else
41                 return NXFS.access("/usr/bin/curl")
42         end
43 end
44
45 -- check if Wget with SSL or cURL with proxy support installed
46 function check_proxy()
47         -- we prefere GNU Wget for communication
48         if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) then
49                 return true
50
51         -- if not installed cURL must support proxy
52         elseif NXFS.access("/usr/bin/curl") then
53                 return (SYS.call([[ grep -i all_proxy /usr/lib/libcurl.so* >/dev/null 2>&1 ]]) == 0)
54
55         -- only BusyBox Wget is installed
56         else
57                 return NXFS.access("/usr/bin/wget")
58         end
59 end
60
61 -- check if BIND host installed
62 function check_bind_host()
63         return NXFS.access("/usr/bin/host")
64 end
65
66 -- convert epoch date to given format
67 function epoch2date(epoch, format)
68         if not format or #format < 2 then
69                 local uci = UCI.cursor()
70                 format    = uci:get("ddns", "global", "date_format") or "%F %R"
71                 uci:unload("ddns")
72         end
73         format = format:gsub("%%n", "<br />")   -- replace newline
74         format = format:gsub("%%t", "    ")     -- replace tab
75         return os.date(format, epoch)
76 end
77
78 -- read lastupdate from [section].update file
79 function get_lastupd(section)
80         local uci     = UCI.cursor()
81         local run_dir = uci:get("ddns", "global", "run_dir") or "/var/run/ddns"
82         local etime   = tonumber(NXFS.readfile("%s/%s.update" % { run_dir, section } ) or 0 )
83         uci:unload("ddns")
84         return etime
85 end
86
87 -- read PID from run file and verify if still running
88 function get_pid(section)
89         local uci     = UCI.cursor()
90         local run_dir = uci:get("ddns", "global", "run_dir") or "/var/run/ddns"
91         local pid     = tonumber(NXFS.readfile("%s/%s.pid" % { run_dir, section } ) or 0 )
92         if pid > 0 and not NX.kill(pid, 0) then
93                 pid = 0
94         end
95         uci:unload("ddns")
96         return pid
97 end
98
99 -- replacement of build-in read of UCI option
100 -- modified AbstractValue.cfgvalue(self, section) from cbi.lua
101 -- needed to read from other option then current value definition
102 function read_value(self, section, option)
103         local value
104         if self.tag_error[section] then
105                 value = self:formvalue(section)
106         else
107                 value = self.map:get(section, option)
108         end
109
110         if not value then
111                 return nil
112         elseif not self.cast or self.cast == type(value) then
113                 return value
114         elseif self.cast == "string" then
115                 if type(value) == "table" then
116                         return value[1]
117                 end
118         elseif self.cast == "table" then
119                 return { value }
120         end
121 end
122
123 -- replacement of build-in parse of "Value"
124 -- modified AbstractValue.parse(self, section, novld) from cbi.lua
125 -- validate is called if rmempty/optional true or false
126 -- before write check if forcewrite, value eq default, and more
127 function value_parse(self, section, novld)
128         local fvalue = self:formvalue(section)
129         local fexist = ( fvalue and (#fvalue > 0) )     -- not "nil" and "not empty"
130         local cvalue = self:cfgvalue(section)
131         local rm_opt = ( self.rmempty or self.optional )
132         local eq_cfg                                    -- flag: equal cfgvalue
133
134         -- If favlue and cvalue are both tables and have the same content
135         -- make them identical
136         if type(fvalue) == "table" and type(cvalue) == "table" then
137                 eq_cfg = (#fvalue == #cvalue)
138                 if eq_cfg then
139                         for i=1, #fvalue do
140                                 if cvalue[i] ~= fvalue[i] then
141                                         eq_cfg = false
142                                 end
143                         end
144                 end
145                 if eq_cfg then
146                         fvalue = cvalue
147                 end
148         end
149
150         -- removed parameter "section" from function call because used/accepted nowhere
151         -- also removed call to function "transfer"
152         local vvalue, errtxt = self:validate(fvalue)
153
154         -- error handling; validate return "nil"
155         if not vvalue then
156                 if novld then           -- and "novld" set
157                         return          -- then exit without raising an error
158                 end
159
160                 if fexist then          -- and there is a formvalue
161                         self:add_error(section, "invalid", errtxt)
162                         return          -- so data are invalid
163
164                 elseif not rm_opt then  -- and empty formvalue but NOT (rmempty or optional) set
165                         self:add_error(section, "missing", errtxt)
166                         return          -- so data is missing
167                 end
168         end
169
170         -- lets continue with value returned from validate
171         eq_cfg  = ( vvalue == cvalue )                  -- update equal_config flag
172         local vexist = ( vvalue and (#vvalue > 0) )     -- not "nil" and "not empty"
173         local eq_def = ( vvalue == self.default )       -- equal_default flag
174
175         -- not forcewrite and (rmempty or optional)
176         -- and (no data or equal_default)
177         if not self.forcewrite and rm_opt
178           and (not vexist or eq_def) then
179                 if self:remove(section) then            -- remove data from UCI
180                         self.section.changed = true     -- and push events
181                 end
182                 return
183         end
184
185         -- not forcewrite and no changes, so nothing to write
186         if not self.forcewrite and eq_cfg then
187                 return
188         end
189
190         -- write data to UCI; raise event only on changes
191         if self:write(section, vvalue) and not eq_cfg then
192                 self.section.changed = true
193         end
194 end
195
196 -----------------------------------------------------------------------------
197 -- copied from https://svn.nmap.org/nmap/nselib/url.lua
198 -- @author Diego Nehab
199 -- @author Eddie Bell <ejlbell@gmail.com>
200 --[[
201     URI parsing, composition and relative URL resolution
202     LuaSocket toolkit.
203     Author: Diego Nehab
204     RCS ID: $Id: url.lua,v 1.37 2005/11/22 08:33:29 diego Exp $
205     parse_query and build_query added For nmap (Eddie Bell <ejlbell@gmail.com>)
206 ]]--
207 ---
208 -- Parses a URL and returns a table with all its parts according to RFC 2396.
209 --
210 -- The following grammar describes the names given to the URL parts.
211 -- <code>
212 -- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
213 -- <authority> ::= <userinfo>@<host>:<port>
214 -- <userinfo> ::= <user>[:<password>]
215 -- <path> :: = {<segment>/}<segment>
216 -- </code>
217 --
218 -- The leading <code>/</code> in <code>/<path></code> is considered part of
219 -- <code><path></code>.
220 -- @param url URL of request.
221 -- @param default Table with default values for each field.
222 -- @return A table with the following fields, where RFC naming conventions have
223 --   been preserved:
224 --     <code>scheme</code>, <code>authority</code>, <code>userinfo</code>,
225 --     <code>user</code>, <code>password</code>, <code>host</code>,
226 --     <code>port</code>, <code>path</code>, <code>params</code>,
227 --     <code>query</code>, and <code>fragment</code>.
228 -----------------------------------------------------------------------------
229 function parse_url(url) --, default)
230         -- initialize default parameters
231         local parsed = {}
232 --      for i,v in base.pairs(default or parsed) do
233 --              parsed[i] = v
234 --      end
235
236         -- remove whitespace
237 --      url = string.gsub(url, "%s", "")
238         -- get fragment
239         url = string.gsub(url, "#(.*)$",
240                 function(f)
241                         parsed.fragment = f
242                         return ""
243                 end)
244         -- get scheme. Lower-case according to RFC 3986 section 3.1.
245         url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
246                 function(s)
247                         parsed.scheme = string.lower(s);
248                         return ""
249                 end)
250         -- get authority
251         url = string.gsub(url, "^//([^/]*)",
252                 function(n)
253                         parsed.authority = n
254                         return ""
255                 end)
256         -- get query stringing
257         url = string.gsub(url, "%?(.*)",
258                 function(q)
259                         parsed.query = q
260                         return ""
261                 end)
262         -- get params
263         url = string.gsub(url, "%;(.*)",
264                 function(p)
265                         parsed.params = p
266                         return ""
267                 end)
268         -- path is whatever was left
269         parsed.path = url
270
271         local authority = parsed.authority
272         if not authority then
273                 return parsed
274         end
275         authority = string.gsub(authority,"^([^@]*)@",
276                 function(u)
277                         parsed.userinfo = u;
278                         return ""
279                 end)
280         authority = string.gsub(authority, ":([0-9]*)$",
281                 function(p)
282                         if p ~= "" then
283                                 parsed.port = p
284                         end;
285                         return ""
286                 end)
287         if authority ~= "" then
288                 parsed.host = authority
289         end
290
291         local userinfo = parsed.userinfo
292         if not userinfo then
293                 return parsed
294         end
295         userinfo = string.gsub(userinfo, ":([^:]*)$",
296                 function(p)
297                         parsed.password = p;
298                         return ""
299                 end)
300         parsed.user = userinfo
301         return parsed
302 end