Merge pull request #4471 from Ansuel/ddns-refresh
authorFlorian Eckert <fe@dev.tdt.de>
Fri, 2 Oct 2020 13:54:25 +0000 (15:54 +0200)
committerGitHub <noreply@github.com>
Fri, 2 Oct 2020 13:54:25 +0000 (15:54 +0200)
luci-app-ddns: rework with new ddns changes

applications/luci-app-ddns/htdocs/luci-static/resources/view/ddns/overview.js
applications/luci-app-ddns/root/usr/libexec/rpcd/luci.ddns
applications/luci-app-ddns/root/usr/share/rpcd/acl.d/luci-app-ddns.json

index 46f33d0923c0d5ee63f979746f74d97bcabd42ee..28c96da3810db9df341f5304a44bd166f4d5022f 100644 (file)
@@ -1,4 +1,5 @@
 'use strict';
+'require ui';
 'require view';
 'require dom';
 'require poll';
@@ -8,43 +9,40 @@
 'require form';
 'require tools.widgets as widgets';
 
-var callGetLogServices, callInitAction, callDDnsGetStatus;
-
-var NextUpdateStrings = {};
-
-NextUpdateStrings = {
-       'Verify' : _("Verify"),
-       'Run once' : _("Run once"),
-       'Disabled' : _("Disabled"),
-       'Stopped' : _("Stopped")
-}
+return view.extend({
 
-var time_res = {};
-time_res['seconds'] = 1;
-time_res['minutes'] = 60;
-time_res['hours'] = 3600;
+       NextUpdateStrings : {
+               'Verify' : _("Verify"),
+               'Run once' : _("Run once"),
+               'Disabled' : _("Disabled"),
+               'Stopped' : _("Stopped")
+       },
 
-callGetLogServices = rpc.declare({
-       object: 'luci.ddns',
-       method: 'get_services_log',
-       params: [ 'service_name' ],
-       expect: {  },
-});
+       time_res : {
+               seconds : 1,
+               minutes : 60,
+               hours : 3600,
+       },
 
-callInitAction = rpc.declare({
-       object: 'luci',
-       method: 'setInitAction',
-       params: [ 'name', 'action' ],
-       expect: { result: false }
-});
+       callGetLogServices: rpc.declare({
+               object: 'luci.ddns',
+               method: 'get_services_log',
+               params: [ 'service_name' ],
+               expect: {  },
+       }),
 
-callDDnsGetStatus = rpc.declare({
-       object: 'luci.ddns',
-       method: 'get_ddns_state',
-       expect: {  }
-});
+       callInitAction: rpc.declare({
+               object: 'luci',
+               method: 'setInitAction',
+               params: [ 'name', 'action' ],
+               expect: { result: false }
+       }),
 
-return view.extend({
+       callDDnsGetStatus: rpc.declare({
+               object: 'luci.ddns',
+               method: 'get_ddns_state',
+               expect: {  }
+       }),
 
        callDDnsGetEnv: rpc.declare({
                object: 'luci.ddns',
@@ -58,14 +56,137 @@ return view.extend({
                expect: {  }
        }),
 
+       services: {},
+
+       /*
+        * Services list is gen by 3 different source:
+        * 1. /usr/share/ddns/default contains the service installed by opkg
+        * 2. /usr/share/ddns/custom contains any service installed by the
+        *    user or the ddns script (for example when service are
+        *    downloaded)
+        * 3. /usr/share/ddns/list contains all the service that can be
+        *    downloaded by using the ddns script ('service on demand' feature)
+        * 
+        * (Special services that requires a dedicated package ARE NOT
+        * supported by the 'service on demand' feature)
+        */
+       callGenServiceList: function(m, ev) {
+               return Promise.all([
+                       L.resolveDefault(fs.list('/usr/share/ddns/default'), []),
+                       L.resolveDefault(fs.list('/usr/share/ddns/custom'), []),
+                       L.resolveDefault(fs.read('/usr/share/ddns/list'), null)
+               ]).then(L.bind(function (data) {
+                       var default_service = data[0],
+                               custom_service = data[1],
+                               list_service = data[2] && data[2].split("\n") || [],
+                               _this = this;
+
+                       this.services = {};
+
+                       default_service.forEach(function (service) {
+                               _this.services[service.name.replace('.json','')] = true
+                       });
+
+                       custom_service.forEach(function (service) {
+                               _this.services[service.name.replace('.json','')] = true
+                       });
+
+                       list_service.forEach(function (service) {
+                               if (!_this.services[service])
+                                       _this.services[service] = false;
+                       });
+               }, this))
+       },
+
+       /* 
+       * Check if the service is supported.
+       * If the script doesn't find any json assume a 'service on demand' install.
+       * If a json is found check if the ip type is supported.
+       * Invalidate the service_name if is not supported.
+       */
+       handleCheckService : function(s, service_name, ipv6, ev, section_id) {
+
+               var value = service_name.formvalue(section_id);
+               s.service_supported = null;
+               service_name.triggerValidation(section_id);
+
+               return this.handleGetServiceData(value)
+                       .then(L.bind(function (service_data) {
+                               if (value != '-' && service_data) {
+                                       service_data = JSON.parse(service_data);
+                                       if (ipv6.formvalue(section_id) == "1" && !service_data.ipv6) {
+                                               s.service_supported = false;
+                                               return;
+                                       }
+                               }
+                               s.service_supported = true;
+                       }, service_name))
+                       .then(L.bind(service_name.triggerValidation, service_name, section_id))
+       },
+
+       handleGetServiceData: function(service) {
+               return Promise.all([
+                       L.resolveDefault(fs.read('/usr/share/ddns/custom/'+service+'.json'), null),
+                       L.resolveDefault(fs.read('/usr/share/ddns/default/'+service+'.json'), null)
+               ]).then(function(data) {
+                       return data[0] || data[1] || null;
+               })
+       },
+
+       handleInstallService: function(m, service_name, section_id, section, _this, ev) {
+               var service = service_name.formvalue(section_id)
+               return fs.exec('/usr/bin/ddns', ['service', 'install', service])
+                       .then(L.bind(_this.callGenServiceList, _this))
+                       .then(L.bind(m.render, m))
+                       .then(L.bind(this.renderMoreOptionsModal, this, section))
+                       .catch(function(e) { ui.addNotification(null, E('p', e.message)) });
+       },
+
+       handleRefreshServicesList: function(m, ev) {
+               return fs.exec('/usr/bin/ddns', ['service', 'update'])
+                       .then(L.bind(this.load, this))
+                       .then(L.bind(this.render, this))
+                       .catch(function(e) { ui.addNotification(null, E('p', e.message)) });
+       },
+
+       handleReloadDDnsRule: function(m, section_id, ev) {
+               return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
+                                                       [ '-S', section_id, '--', 'start' ])
+                       .then(L.bind(m.load, m))
+                       .then(L.bind(m.render, m))
+                       .catch(function(e) { ui.addNotification(null, E('p', e.message)) });
+       },
+
+       HandleStopDDnsRule: function(m, section_id, ev) {
+               return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
+                                                       [ '-S', section_id, '--', 'start' ])
+                       .then(L.bind(m.render, m))
+                       .catch(function(e) { ui.addNotification(null, E('p', e.message)) });
+       },
+
+       handleToggleDDns: function(m, ev) {
+               return this.callInitAction('ddns', 'enabled')
+                       .then(L.bind(function (action) { return this.callInitAction('ddns', action ? 'disable' : 'enable')}, this))
+                       .then(L.bind(function (action) { return this.callInitAction('ddns', action ? 'stop' : 'start')}, this))
+                       .then(L.bind(m.render, m))
+                       .catch(function(e) { ui.addNotification(null, E('p', e.message)) });
+       },
+
+       handleRestartDDns: function(m, ev) {
+               return this.callInitAction('ddns', 'restart')
+                       .then(L.bind(m.render, m));
+       },
+
        poll_status: function(map, data) {
                var status = data[1] || [], service = data[0] || [], rows = map.querySelectorAll('.cbi-section-table-row[data-sid]'),
                        section_id, cfg_detail_ip, cfg_update, cfg_status, host, ip, last_update,
                        next_update, service_status, reload, cfg_enabled, stop,
                        ddns_enabled = map.querySelector('[data-name="_enabled"]').querySelector('.cbi-value-field'),
-                       ddns_toggle = map.querySelector('[data-name="_toggle"]').querySelector('button');
+                       ddns_toggle = map.querySelector('[data-name="_toggle"]').querySelector('button'),
+                       services_list = map.querySelector('[data-name="_services_list"]').querySelector('.cbi-value-field');
 
                ddns_toggle.innerHTML = status['_enabled'] ? _('Stop DDNS') : _('Start DDNS')
+               services_list.innerHTML = status['_services_list'];
 
                dom.content(ddns_enabled, function() {
                        return E([], [
@@ -101,7 +222,7 @@ return view.extend({
                                if (service[section_id].last_update)
                                        last_update = service[section_id].last_update;
                                if (service[section_id].next_update)
-                                       next_update = NextUpdateStrings[service[section_id].next_update] || service[section_id].next_update;
+                                       next_update = this.NextUpdateStrings[service[section_id].next_update] || service[section_id].next_update;
                                if (service[section_id].pid)
                                        service_status = '<b>' + _('Running') + '</b> : ' + service[section_id].pid;
                        }
@@ -117,10 +238,9 @@ return view.extend({
        load: function() {
                return Promise.all([
                        this.callDDnsGetServicesStatus(),
-                       callDDnsGetStatus(),
+                       this.callDDnsGetStatus(),
                        this.callDDnsGetEnv(),
-                       fs.lines('/etc/ddns/services'),
-                       fs.lines('/etc/ddns/services_ipv6'),
+                       this.callGenServiceList(),
                        uci.load('ddns')
                ]);
        },
@@ -131,20 +251,7 @@ return view.extend({
                var env = data[2] || [];
                var logdir = uci.get('ddns', 'global', 'ddns_logdir') || "/var/log/ddns";
 
-               var services4 = [];
-               var services6 = [];
-
-               data[3].forEach(function(item) {
-                       if (!item.startsWith("#")) {
-                               services4.push(item.split('\t')[0].slice(1,-1));
-                       }
-               });
-
-               data[4].forEach(function(item) {
-                       if (!item.startsWith("#")) {
-                               services6.push(item.split('\t')[0].slice(1,-1));
-                       }
-               });
+               var _this = this;
 
                var m, s, o;
 
@@ -170,33 +277,29 @@ return view.extend({
                        return res ? _('DDNS Autostart enabled') : _('DDNS Autostart disabled')
                };
 
-               o = s.taboption('info', form.DummyValue, '_toggle', '&#160;');
-               o.cfgvalue = function() {
-                       var action = status['_enabled'] ? 'stop' : 'start';
-                       return E([], [
-                               E('button', {
-                               'class': 'cbi-button cbi-button-apply',
-                               'click': L.ui.createHandlerFn(this, function() {
-                                       return callDDnsGetStatus().then(L.bind(function(data) {
-                                               return callInitAction('ddns', action == 'stop' ? 'disable' : 'enable').then(function() {
-                                                       return callInitAction('ddns', action);
-                                               });
-                                       }, this)).then(L.bind(m.render, m));
-                               })
-                       }, _(action.toUpperCase() + ' DDns'))]);
-               };
+               o = s.taboption('info', form.Button, '_toggle');
+               o.title      = '&#160;';
+               o.inputtitle = _((status['_enabled'] ? 'stop' : 'start').toUpperCase() + ' DDns');
+               o.inputstyle = 'apply';
+               o.onclick = L.bind(this.handleToggleDDns, this, m);
 
-               o = s.taboption('info', form.DummyValue, '_restart', '&#160;');
+               o = s.taboption('info', form.Button, '_restart');
+               o.title      = '&#160;';
+               o.inputtitle = _('Restart DDns');
+               o.inputstyle = 'apply';
+               o.onclick = L.bind(this.handleRestartDDns, this, m);
+
+               o = s.taboption('info', form.DummyValue, '_services_list', _('Services list last update'));
                o.cfgvalue = function() {
-                       return E([], [
-                               E('button', {
-                               'class': 'cbi-button cbi-button-apply',
-                               'click': L.ui.createHandlerFn(this, function() {
-                                       return callInitAction('ddns', 'restart').then(L.bind(m.render, m));
-                               })
-                       }, _('Restart DDns'))]);
+                       return status[this.option];
                };
 
+               o = s.taboption('info', form.Button, '_refresh_services');
+               o.title      = '&#160;';
+               o.inputtitle = _('Update DDns Services List');
+               o.inputstyle = 'apply';
+               o.onclick = L.bind(this.handleRefreshServicesList, this, m);
+
                // DDns hints
 
                if (!env['has_ipv6']) {
@@ -344,6 +447,14 @@ return view.extend({
 
                }
 
+               o = s.taboption('global', form.Value, 'cacert', _('Ca Certs path'));
+               o.description = _('Ca Certs path that will be used to download services data. Set IGNORE to skip certificate validation.');
+               o.placeholder = 'IGNORE';
+
+               o = s.taboption('global', form.Value, 'services_url', _('Services URL Download'));
+               o.description = _('Url used to download services file. By default is the master openwrt ddns package repo.');
+               o.placeholder = 'https://raw.githubusercontent.com/openwrt/packages/master/net/ddns-scripts/files';
+
                // DDns services
                s = m.section(form.GridSection, 'service', _('Services'));
                s.anonymous = true;
@@ -354,9 +465,25 @@ return view.extend({
                s.addremove = true;
                s.sortable  = true;
 
+               s.handleCreateDDnsRule = function(m, name, service_name, ipv6, ev) {
+                       var section_id = name.isValid('_new_') ? name.formvalue('_new_') : null,
+                               service_value = service_name.isValid('_new_') ? service_name.formvalue('_new_') : null,
+                               ipv6_value = ipv6.isValid('_new_') ? ipv6.formvalue('_new_') : null;
+       
+                       if (section_id == null || section_id == '' || service_value == null || section_id == '' || ipv6_value == null || ipv6_value == '')
+                               return;
+       
+                       return m.save(function() {
+                               uci.add('ddns', 'service', section_id);
+                               uci.set('ddns', section_id, 'service_name', service_value);
+                               uci.set('ddns', section_id, 'use_ipv6', ipv6_value);
+                       }).then(L.bind(m.children[1].renderMoreOptionsModal, m.children[1], section_id));
+               };
+
                s.handleAdd = function(ev) {
                        var m2 = new form.Map('ddns'),
-                               s2 = m2.section(form.NamedSection, '_new_');
+                               s2 = m2.section(form.NamedSection, '_new_'),
+                               name, ipv6, service_name;
 
                        s2.render = function() {
                                return Promise.all([
@@ -376,26 +503,41 @@ return view.extend({
                                return true;
                        };
 
+                       ipv6 = s2.option( form.ListValue, 'use_ipv6',
+                               _("IP address version"),
+                               _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
+                       ipv6.default = '0';
+                       ipv6.value("0", _("IPv4-Address"))
+                       if (env["has_ipv6"]) {
+                               ipv6.value("1", _("IPv6-Address"))
+                       }
+
+                       service_name = s2.option(form.ListValue, 'service_name',
+                                       String.format('%s', _("DDNS Service provider")));
+                       service_name.value('-',"-- " + _("custom") + " --");
+                       for (var elem in _this.services)
+                               service_name.value(elem);
+                       service_name.validate = function(section_id, value) {
+                               if (value == '') return _("Select a service");
+                               if (s2.service_supported == null) return _("Checking the service support...");
+                               if (!s2.service_supported) return _("Service doesn't support this ip type");
+                               return true;
+                       };
+
+                       ipv6.onchange = L.bind(_this.handleCheckService, _this, s2, service_name, ipv6);
+                       service_name.onchange = L.bind(_this.handleCheckService, _this, s2, service_name, ipv6);
+
                        m2.render().then(L.bind(function(nodes) {
-                               L.ui.showModal(_('Add new services...'), [
+                               ui.showModal(_('Add new services...'), [
                                        nodes,
                                        E('div', { 'class': 'right' }, [
                                                E('button', {
                                                        'class': 'btn',
-                                                       'click': L.ui.hideModal
+                                                       'click': ui.hideModal
                                                }, _('Cancel')), ' ',
                                                E('button', {
                                                        'class': 'cbi-button cbi-button-positive important',
-                                                       'click': L.ui.createHandlerFn(this, function(ev) {
-                                                               var nameval = name.isValid('_new_') ? name.formvalue('_new_') : null;
-
-                                                               if (nameval == null || nameval == '')
-                                                                       return;
-
-                                                               return m.save(function() {
-                                                                       uci.add('ddns', 'service', nameval);
-                                                               }).then(L.bind(m.children[1].renderMoreOptionsModal, m.children[1], nameval));
-                                                       })
+                                                       'click': ui.createHandlerFn(this, 'handleCreateDDnsRule', m, name, service_name, ipv6)
                                                }, _('Create service'))
                                        ])
                                ], 'cbi-modal');
@@ -409,18 +551,12 @@ return view.extend({
                                cfg_enabled = uci.get('ddns', section_id, 'enabled'),
                                reload_opt = {
                                        'class': 'cbi-button cbi-button-neutral reload',
-                                       'click': L.ui.createHandlerFn(this, function() {
-                                               return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
-                                                       [ '-S', section_id, '--', 'start' ]).then(L.bind(m.render, m));
-                                       }),
+                                       'click': ui.createHandlerFn(_this, 'handleReloadDDnsRule', m, section_id),
                                        'title': _('Reload this service'),
                                },
                                stop_opt = {
                                        'class': 'cbi-button cbi-button-neutral stop',
-                                       'click': L.ui.createHandlerFn(this, function() {
-                                               return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
-                                                       [ '-S', section_id, '--', 'start' ]).then(L.bind(m.render, m));
-                                       }),
+                                       'click': ui.createHandlerFn(_this, 'HandleStopDDnsRule', m, section_id),
                                        'title': _('Stop this service'),
                                };
 
@@ -442,11 +578,544 @@ return view.extend({
                        return tdEl;
                };
 
+               s.modaltitle = function(section_id) {
+                       return _('DDns Service') + ' Â» ' + section_id;
+               };
+
+               s.addModalOptions = function(s, section_id) {
+
+                       var service = uci.get('ddns', section_id, 'service_name') || '-',
+                               ipv6 = uci.get('ddns', section_id, 'use_ipv6'), service_name, use_ipv6;
+
+                       return _this.handleGetServiceData(service).then(L.bind(function (service_data) {
+                               s.service_available = true;
+                               s.service_supported = true;
+
+                               if (service != '-') {
+                                       if (!service_data)
+                                               s.service_available = false;
+                                       else {
+                                               service_data = JSON.parse(service_data);
+                                               if (ipv6 == "1" && !service_data.ipv6)
+                                                       s.service_supported = false;
+                                       }
+                               }
+
+                               s.tab('basic', _('Basic Settings'));
+                               s.tab('advanced', _('Advanced Settings'));
+                               s.tab('timer', _('Timer Settings'));
+                               s.tab('logview', _('Log File Viewer'));
+
+                               o = s.taboption('basic', form.Flag, 'enabled',
+                                       _('Enabled'),
+                                       _("If this service section is disabled it could not be started.")
+                                       + "<br />" +
+                                       _("Neither from LuCI interface nor from console."));
+                               o.modalonly = true;
+                               o.rmempty  = false;
+                               o.default = '1';
+
+                               o = s.taboption('basic', form.Value, 'lookup_host',
+                                       _("Lookup Hostname"),
+                                       _("Hostname/FQDN to validate, if IP update happen or necessary"));
+                               o.rmempty = false;
+                               o.placeholder = "myhost.example.com";
+                               o.datatype = 'and(minlength(3),hostname("strict"))';
+                               o.modalonly = true;
+
+                               use_ipv6 = s.taboption('basic', form.ListValue, 'use_ipv6',
+                                       _("IP address version"),
+                                       _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
+                               use_ipv6.default = '0';
+                               use_ipv6.modalonly = true;
+                               use_ipv6.rmempty  = false;
+                               use_ipv6.value("0", _("IPv4-Address"))
+                               if (env["has_ipv6"]) {
+                                       use_ipv6.value("1", _("IPv6-Address"))
+                               }
+
+                               service_name = s.taboption('basic', form.ListValue, 'service_name',
+                                       String.format('%s', _("DDNS Service provider")));
+                               service_name.modalonly = true;
+                               service_name.value('-',"-- " + _("custom") + " --");
+                               for (var elem in _this.services)
+                                       service_name.value(elem);
+                               service_name.cfgvalue = function(section_id) {
+                                       return uci.get('ddns', section_id, 'service_name') || '-';
+                               };
+                               service_name.write = function(section_id, service) {
+                                       if (service != '-') {
+                                               uci.set('ddns', section_id, 'update_url', null);
+                                               uci.set('ddns', section_id, 'update_script', null);
+                                               return uci.set('ddns', section_id, 'service_name', service);
+                                       }
+                                       return uci.set('ddns', section_id, 'service_name', null);
+                               };
+                               service_name.validate = function(section_id, value) {
+                                       if (value == '') return _("Select a service");
+                                       if (s.service_available == null) return _("Checking the service support...");
+                                       if (!s.service_available) return _('Service not installed');
+                                       if (!s.service_supported) return _("Service doesn't support this ip type");
+                                       return true;
+                               };
+
+                               service_name.onchange = L.bind(_this.handleCheckService, _this, s, service_name, use_ipv6);
+                               use_ipv6.onchange = L.bind(_this.handleCheckService, _this, s, service_name, use_ipv6);
+
+                               if (!s.service_available) {
+                                       o = s.taboption('basic', form.Button, '_download_service');
+                                       o.modalonly  = true;
+                                       o.title      = _('Service not installed');
+                                       o.inputtitle = _('Install Service');
+                                       o.inputstyle = 'apply';
+                                       o.onclick = L.bind(_this.handleInstallService,
+                                               this, m, service_name, section_id, s.section, _this)
+                               }
+
+                               if (!s.service_supported) {
+                                       o = s.taboption('basic', form.DummyValue, '_not_supported', '&nbsp');
+                                       o.cfgvalue = function () {
+                                               return _("Service doesn't support this ip type")
+                                       };
+                               }
+
+                               var service_switch = s.taboption('basic', form.Button, '_switch_proto');
+                               service_switch.modalonly  = true;
+                               service_switch.title      = _('Really switch service?');
+                               service_switch.inputtitle = _('Switch service');
+                               service_switch.inputstyle = 'apply';
+                               service_switch.onclick = L.bind(function(ev) {
+                                       if (!s.service_supported) return;
+
+                                       return s.map.save()
+                                               .then(L.bind(m.load, m))
+                                               .then(L.bind(m.render, m))
+                                               .then(L.bind(this.renderMoreOptionsModal, this, s.section));
+                               }, this);
+
+                               if (s.service_available && s.service_supported) {
+
+                                       o = s.taboption('basic', form.Value, 'update_url',
+                                               _("Custom update-URL"),
+                                               _("Update URL to be used for updating your DDNS Provider.")
+                                               + "<br />" +
+                                               _("Follow instructions you will find on their WEB page."));
+                                       o.modalonly = true;
+                                       o.rmempty = true;
+                                       o.optional = true;
+                                       o.depends("service_name","-");
+                                       o.validate = function(section_id, value) {
+                                               var other = this.section.children.filter(function(o) { return o.option == 'update_script' })[0].formvalue(section_id);
+
+                                               if ((value == "" && other == "") || (value != "" && other != "")) {
+                                                       return _("Insert a Update Script OR a Update URL");
+                                               }
+
+                                               return true;
+                                       };
+
+                                       o = s.taboption('basic', form.Value, 'update_script',
+                                               _("Custom update-script"),
+                                               _("Custom update script to be used for updating your DDNS Provider."));
+                                       o.modalonly = true;
+                                       o.rmempty = true;
+                                       o.optional = true;
+                                       o.depends("service_name","-");
+                                       o.validate = function(section_id, value) {
+                                               var other = this.section.children.filter(function(o) { return o.option == 'update_url' })[0].formvalue(section_id);
+
+                                               if ((value == "" && other == "") || (value != "" && other != "")) {
+                                                       return _("Insert a Update Script OR a Update URL");
+                                               }
+
+                                               return true;
+                                       };
+
+                                       o = s.taboption('basic', form.Value, 'domain',
+                                               _("Domain"),
+                                               _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
+                                       o.modalonly = true;
+                                       o.rmempty = false;
+
+                                       o = s.taboption('basic', form.Value, 'username',
+                                               _("Username"),
+                                               _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
+                                       o.modalonly = true;
+                                       o.rmempty = false;
+
+                                       o = s.taboption('basic', form.Value, 'password',
+                                               _("Password"),
+                                               _("Replaces [PASSWORD] in Update-URL (URL-encoded)"));
+                                       o.password = true;
+                                       o.modalonly = true;
+                                       o.rmempty = false;
+
+                                       o = s.taboption('basic', form.Value, 'param_enc',
+                                               _("Optional Encoded Parameter"),
+                                               _("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)"));
+                                       o.optional = true;
+                                       o.modalonly = true;
+
+                                       o = s.taboption('basic', form.Value, 'param_opt',
+                                               _("Optional Parameter"),
+                                               _("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)"));
+                                       o.optional = true;
+                                       o.modalonly = true;
+
+                                       if (env['has_ssl']) {
+                                               o = s.taboption('basic', form.Flag, 'use_https',
+                                                       _("Use HTTP Secure"),
+                                                       _("Enable secure communication with DDNS provider"));
+                                               o.optional = true;
+                                               o.modalonly = true;
+
+                                               o = s.taboption('basic', form.Value, 'cacert',
+                                                       _("Path to CA-Certificate"),
+                                                       _("directory or path/file")
+                                                       + "<br />" +
+                                                       _("or")
+                                                       + '<b>' + " IGNORE " + '</b>' +
+                                                       _("to run HTTPS without verification of server certificates (insecure)"));
+                                               o.modalonly = true;
+                                               o.depends("use_https", "1");
+                                               o.placeholder = "/etc/ssl/certs";
+                                               o.rmempty = false;
+                                       };
+
+
+                                       o = s.taboption('advanced', form.ListValue, 'ip_source',
+                                               _("IP address source"),
+                                               _("Defines the source to read systems IP-Address from, that will be send to the DDNS provider"));
+                                       o.modalonly = true;
+                                       o.default = "network";
+                                       o.value("network", _("Network"));
+                                       o.value("web", _("URL"));
+                                       o.value("interface", _("Interface"));
+                                       o.value("script", _("Script"));
+                                       o.write = function(section_id, formvalue) {
+                                               switch(formvalue) {
+                                                       case 'network':
+                                                               uci.set('ddns', section_id, "ip_url",null);
+                                                               uci.set('ddns', section_id, "ip_interface",null);
+                                                               uci.set('ddns', section_id, "ip_script",null);
+                                                               break;
+                                                       case 'web':
+                                                               uci.set('ddns', section_id, "ip_network",null);
+                                                               uci.set('ddns', section_id, "ip_interface",null);
+                                                               uci.set('ddns', section_id, "ip_script",null);
+                                                               break;
+                                                       case 'interface':
+                                                               uci.set('ddns', section_id, "ip_network",null);
+                                                               uci.set('ddns', section_id, "ip_url",null);
+                                                               uci.set('ddns', section_id, "ip_script",null);
+                                                               break;
+                                                       case 'script':
+                                                               uci.set('ddns', section_id, "ip_network",null);
+                                                               uci.set('ddns', section_id, "ip_url",null);
+                                                               uci.set('ddns', section_id, "ip_interface",null);
+                                                               break;
+                                                       default:
+                                                               break;
+                                               };
+
+                                               return uci.set('ddns', section_id, 'ip_source', formvalue )
+                                       };
+
+                                       o = s.taboption('advanced', widgets.NetworkSelect, 'ip_network',
+                                               _("Network"),
+                                               _("Defines the network to read systems IP-Address from"));
+                                       o.depends('ip_source','network');
+                                       o.modalonly = true;
+                                       o.default = 'wan';
+                                       o.multiple = false;
+
+                                       o = s.taboption('advanced', form.Value, 'ip_url',
+                                               _("URL to detect"),
+                                               _("Defines the Web page to read systems IP-Address from.")
+                                               + '<br />' +
+                                               String.format('%s %s', _('Example for IPv4'), ': http://checkip.dyndns.com')
+                                               + '<br />' +
+                                               String.format('%s %s', _('Example for IPv6'), ': http://checkipv6.dyndns.com'));
+                                       o.depends("ip_source", "web")
+                                       o.modalonly = true;
+
+                                       o = s.taboption('advanced', widgets.DeviceSelect, 'ip_interface',
+                                               _("Interface"),
+                                               _("Defines the interface to read systems IP-Address from"));
+                                       o.modalonly = true;
+                                       o.depends("ip_source", "interface")
+                                       o.multiple = false;
+                                       o.default = 'wan';
+
+                                       o = s.taboption('advanced', form.Value, 'ip_script',
+                                               _("Script"),
+                                               _("User defined script to read systems IP-Address"));
+                                       o.modalonly = true;
+                                       o.depends("ip_source", "script")
+                                       o.placeholder = "/path/to/script.sh"
+
+                                       o = s.taboption('advanced', widgets.DeviceSelect, 'interface',
+                                               _("Event Network"),
+                                               _("Network on which the ddns-updater scripts will be started"));
+                                       o.modalonly = true;
+                                       o.multiple = false;
+                                       o.default = 'wan';
+                                       o.depends("ip_source", "web");
+                                       o.depends("ip_source", "script");
+
+                                       o = s.taboption('advanced', form.DummyValue, '_interface',
+                                               _("Event Network"),
+                                               _("Network on which the ddns-updater scripts will be started"));
+                                       o.depends("ip_source", "interface");
+                                       o.depends("ip_source", "network");
+                                       o.forcewrite = true;
+                                       o.modalonly = true;
+                                       o.cfgvalue = function(section_id) {
+                                               return uci.get('ddns', section_id, 'interface') || _('This will be autoset to the selected interface');
+                                       };
+                                       o.write = function(section_id) {
+                                               var opt = this.section.children.filter(function(o) { return o.option == 'ip_source' })[0].formvalue(section_id);
+                                               var val = this.section.children.filter(function(o) { return o.option == 'ip_'+opt })[0].formvalue(section_id);
+                                               return uci.set('ddns', section_id, 'interface', val);
+                                       };
+
+                                       if (env['has_bindnet']) {
+                                               o = s.taboption('advanced', widgets.ZoneSelect, 'bind_network',
+                                                       _("Bind Network"),
+                                                       _('OPTIONAL: Network to use for communication')
+                                                       + '<br />' +
+                                                       _("Network on which the ddns-updater scripts will be started"));
+                                               o.depends("ip_source", "web");
+                                               o.optional = true;
+                                               o.rmempty = true;
+                                               o.modalonly = true;
+                                       }
+
+                                       if (env['has_forceip']) {
+                                               o = s.taboption('advanced', form.Flag, 'force_ipversion',
+                                                       _("Force IP Version"),
+                                                       _('OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.'));
+                                               o.optional = true;
+                                               o.rmempty = true;
+                                               o.modalonly = true;
+                                       }
+
+                                       if (env['has_dnsserver']) {
+                                               o = s.taboption("advanced", form.Value, "dns_server",
+                                                       _("DNS-Server"),
+                                                       _("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.")
+                                                       + "<br />" +
+                                                       _("Format: IP or FQDN"));
+                                               o.placeholder = "mydns.lan"
+                                               o.optional = true;
+                                               o.rmempty = true;
+                                               o.modalonly = true;
+                                       }
+
+                                       if (env['has_bindhost']) {
+                                               o = s.taboption("advanced", form.Flag, "force_dnstcp",
+                                                       _("Force TCP on DNS"),
+                                                       _("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests."));
+                                               o.optional = true;
+                                               o.rmempty = true;
+                                               o.modalonly = true;
+                                       }
+
+                                       if (env['has_proxy']) {
+                                               o = s.taboption("advanced", form.Value, "proxy",
+                                                       _("PROXY-Server"),
+                                                       _("OPTIONAL: Proxy-Server for detection and updates.")
+                                                       + "<br />" +
+                                                       String.format('%s: <b>%s</b>', _("Format"), "[user:password@]proxyhost:port")
+                                                       + "<br />" +
+                                                       String.format('%s: <b>%s</b>', _("IPv6 address must be given in square brackets"), "[2001:db8::1]:8080"));
+                                               o.optional = true;
+                                               o.rmempty = true;
+                                               o.modalonly = true;
+                                       }
+
+                                       o = s.taboption("advanced", form.ListValue, "use_syslog",
+                                               _("Log to syslog"),
+                                               _("Writes log messages to syslog. Critical Errors will always be written to syslog."));
+                                       o.modalonly = true;
+                                       o.default = "2"
+                                       o.optional = true;
+                                       o.value("0", _("No logging"))
+                                       o.value("1", _("Info"))
+                                       o.value("2", _("Notice"))
+                                       o.value("3", _("Warning"))
+                                       o.value("4", _("Error"))
+
+                                       o = s.taboption("advanced", form.Flag, "use_logfile",
+                                               _("Log to file"));
+                                       o.default = '1';
+                                       o.optional = true;
+                                       o.modalonly = true;
+                                       o.cfgvalue = function(section_id) {
+                                               this.description = _("Writes detailed messages to log file. File will be truncated automatically.") + "<br />" +
+                                               _("File") + ': "' + logdir + '/' + section_id + '.log"';
+                                               return uci.get('ddns', section_id, 'use_logfile');
+                                       };
+
+
+                                       o = s.taboption("timer", form.Value, "check_interval",
+                                               _("Check Interval"));
+                                       o.placeholder = "30";
+                                       o.modalonly = true;
+                                       o.datatype = 'uinteger';
+                                       o.validate = function(section_id, formvalue) {
+                                               var unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id),
+                                                       time_to_sec = _this.time_res[unit || 'minutes'] * formvalue;
+
+                                               if (formvalue && time_to_sec < 300)
+                                                       return _('Values below 5 minutes == 300 seconds are not supported');
+
+                                               return true;
+                                       };
+
+                                       o = s.taboption("timer", form.ListValue, "check_unit",
+                                               _('Check Unit'),
+                                               _("Interval unit to check for changed IP"));
+                                       o.modalonly = true;
+                                       o.default  = "minutes"
+                                       o.value("seconds", _("seconds"));
+                                       o.value("minutes", _("minutes"));
+                                       o.value("hours", _("hours"));
+
+                                       o = s.taboption("timer", form.Value, "force_interval",
+                                               _("Force Interval"),
+                                               _("Interval to force updates send to DDNS Provider")
+                                               + "<br />" +
+                                               _("Setting this parameter to 0 will force the script to only run once"));
+                                       o.placeholder = "72";
+                                       o.optional = true;
+                                       o.modalonly = true;
+                                       o.datatype = 'uinteger';
+                                       o.validate = function(section_id, formvalue) {
+
+                                               if (!formvalue)
+                                                       return true;
+
+                                               var check_unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id),
+                                                       check_val = this.section.children.filter(function(o) { return o.option == 'check_interval' })[0].formvalue(section_id),
+                                                       force_unit = this.section.children.filter(function(o) { return o.option == 'force_unit' })[0].formvalue(section_id),
+                                                       check_to_sec = _this.time_res[check_unit || 'minutes'] * ( check_val || '30'),
+                                                       force_to_sec = _this.time_res[force_unit || 'minutes'] * formvalue;
+
+                                               if (force_to_sec != 0 && force_to_sec < check_to_sec)
+                                                       return _("Values lower 'Check Interval' except '0' are not supported");
+
+                                               return true;
+                                       };
+
+                                       o = s.taboption("timer", form.ListValue, "force_unit", 
+                                               _('Force Unit'),
+                                               _("Interval unit to force updates send to DDNS Provider"));
+                                       o.modalonly = true;
+                                       o.optional = true;
+                                       o.default  = "minutes"
+                                       o.value("minutes", _("minutes"));
+                                       o.value("hours", _("hours"));
+                                       o.value("days", _("days"));
+
+                                       o = s.taboption("timer", form.Value, "retry_count",
+                                               _("Error Retry Counter"),
+                                               _("On Error the script will stop execution after given number of retrys")
+                                               + "<br />" +
+                                               _("The default setting of '0' will retry infinite."));
+                                       o.placeholder = "0";
+                                       o.optional = true;
+                                       o.modalonly = true;
+                                       o.datatype = 'uinteger';
+
+                                       o = s.taboption("timer", form.Value, "retry_interval",
+                                               _("Error Retry Interval"),
+                                               _("On Error the script will stop execution after given number of retrys")
+                                               + "<br />" +
+                                               _("The default setting of '0' will retry infinite."));
+                                       o.placeholder = "60";
+                                       o.optional = true;
+                                       o.modalonly = true;
+                                       o.datatype = 'uinteger';
+
+                                       o = s.taboption("timer", form.ListValue, "retry_unit",
+                                               _('Retry Unit'),
+                                               _("On Error the script will retry the failed action after given time"));
+                                       o.modalonly = true;
+                                       o.optional = true;
+                                       o.default  = "seconds"
+                                       o.value("seconds", _("seconds"));
+                                       o.value("minutes", _("minutes"));
+
+                                       o = s.taboption('logview', form.Button, '_read_log');
+                                       o.title      = '';
+                                       o.depends('use_logfile','1');
+                                       o.modalonly = true;
+                                       o.inputtitle = _('Read / Reread log file');
+                                       o.inputstyle = 'apply';
+                                       o.onclick = L.bind(function(ev, section_id) {
+                                               return _this.callGetLogServices(section_id).then(L.bind(log_box.update_log, log_box));
+                                       }, this);
+
+                                       var log_box = s.taboption("logview", form.DummyValue, "_logview");
+                                       log_box.depends('use_logfile','1');
+                                       log_box.modalonly = true;
+
+                                       log_box.update_log = L.bind(function(view, log_data) {
+                                               return document.getElementById('log_area').textContent = log_data.result;
+                                       }, o, this);
+
+                                       log_box.render = L.bind(function() {
+                                               return E([
+                                                       E('p', {}, _('This is the current content of the log file in ') + logdir + ' for this service.'),
+                                                       E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20, 'readonly' : 'readonly', 'id' : 'log_area' }, _('Please press [Read] button') ))
+                                               ]);
+                                       }, o, this);
+                               }
+
+                               for (var i = 0; i < s.children.length; i++) {
+                                       o = s.children[i];
+                                       switch (o.option) {
+                                       case '_switch_proto':
+                                               o.depends({ service_name : service, use_ipv6: ipv6, "!reverse": true })
+                                               continue;
+                                       case 'enabled':
+                                       case 'service_name':
+                                       case 'use_ipv6':
+                                       case 'update_script':
+                                       case 'update_url':
+                                       case 'lookup_host':
+                                               continue;
+
+                                       default:
+                                               if (o.deps.length)
+                                                       for (var j = 0; j < o.deps.length; j++) {
+                                                               o.deps[j].service_name = service;
+                                                               o.deps[j].use_ipv6 = ipv6;
+                                                       }
+                                               else
+                                                       o.depends({service_name: service, use_ipv6: ipv6 });
+                                       }
+                               }
+                       }, this)
+               )};
+
+               o = s.option(form.DummyValue, '_cfg_status', _('Status'));
+               o.modalonly = false;
+               o.textvalue = function(section_id) {
+                       var text = '<b>' + _('Not Running') + '</b>';
+
+                       if (resolved[section_id] && resolved[section_id].pid)
+                               text = '<b>' + _('Running') + '</b> : ' + resolved[section_id].pid;
+
+                       return text;
+               };
+
                o = s.option(form.DummyValue, '_cfg_name', _('Name'));
                o.modalonly = false;
                o.textvalue = function(section_id) {
                        return '<b>' + section_id + '</b>';
-               }
+               };
 
                o = s.option(form.DummyValue, '_cfg_detail_ip', _('Lookup Hostname') + "<br />" + _('Registered IP'));
                o.rawhtml   = true;
@@ -474,496 +1143,17 @@ return view.extend({
                                if (resolved[section_id].last_update)
                                        last_update = resolved[section_id].last_update;
                                if (resolved[section_id].next_update)
-                                       next_update =  NextUpdateStrings[resolved[section_id].next_update] || resolved[section_id].next_update;
+                                       next_update = _this.NextUpdateStrings[resolved[section_id].next_update] || resolved[section_id].next_update;
                        }
 
                        return  last_update + '<br />' + next_update;
                };
 
-               s.modaltitle = function(section_id) {
-                       return _('DDns Service') + ' Â» ' + section_id;
-               };
-
-               o = s.option(form.DummyValue, '_cfg_status', _('Status'));
-               o.modalonly = false;
-               o.textvalue = function(section_id) {
-                       var text = '<b>' + _('Not Running') + '</b>';
-
-                       if (resolved[section_id] && resolved[section_id].pid)
-                               text = '<b>' + _('Running') + '</b> : ' + resolved[section_id].pid;
-
-                       return text;
-               }
-
-
-               s.tab('basic', _('Basic Settings'));
-               s.tab('advanced', _('Advanced Settings'));
-               s.tab('timer', _('Timer Settings'));
-               s.tab('logview', _('Log File Viewer'));
-
-               o = s.taboption('basic', form.Flag, 'enabled',
-                       _('Enabled'),
-                       _("If this service section is disabled it could not be started.")
-                       + "<br />" +
-                       _("Neither from LuCI interface nor from console."));
-               o.modalonly = true;
-               o.rmempty  = false;
-               o.default = '1';
-
-               o = s.taboption('basic', form.Value, 'lookup_host',
-                       _("Lookup Hostname"),
-                       _("Hostname/FQDN to validate, if IP update happen or necessary"));
-               o.rmempty = false;
-               o.placeholder = "myhost.example.com";
-               o.datatype = 'and(minlength(3),hostname("strict"))';
-               o.modalonly = true;
-
-               o = s.taboption('basic', form.ListValue, 'use_ipv6',
-                       _("IP address version"),
-                       _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
-               o.default = '0';
-               o.modalonly = true;
-               o.rmempty  = false;
-               o.value("0", _("IPv4-Address"))
-               if (env["has_ipv6"]) {
-                       o.value("1", _("IPv6-Address"))
-               }
-
-               o = s.taboption('basic', form.ListValue, 'ipv4_service_name',
-                       String.format('%s %s', _("DDNS Service provider"), "[IPv4]"));
-               o.depends("use_ipv6", "0")
-               o.modalonly = true;
-               o.value('-',"-- " + _("custom") + " --");
-               for (var i = 0; i < services4.length; i++)
-                       o.value(services4[i]);
-               o.cfgvalue = function(section_id) {
-                       return uci.get('ddns', section_id, 'service_name') || '-';
-               };
-               o.write = function(section_id, formvalue) {
-                       if (formvalue != '-') {
-                               uci.set('ddns', section_id, 'update_url', null);
-                               uci.set('ddns', section_id, 'update_script', null);
-                               return uci.set('ddns', section_id, 'service_name', formvalue);
-                       }
-                       return uci.set('ddns', section_id, 'service_name', null);
-               };
-
-               o = s.taboption('basic', form.ListValue, 'ipv6_service_name',
-                       String.format('%s %s', _("DDNS Service provider"), "[IPv6]"));
-               o.depends("use_ipv6", "1")
-               o.modalonly = true;
-               o.value('-',"-- " + _("custom") + " --");
-               for (var i = 0; i < services6.length; i++) {
-                       o.value(services6[i]);
-               }
-               o.cfgvalue = function(section_id) {
-                       var service = uci.get('ddns', section_id, 'service_name'),
-                               update_script = uci.get('ddns', section_id, 'update_script'),
-                               update_url = uci.get('ddns', section_id, 'update_url');
-
-                       if (!service && (update_script || update_url))
-                               return "-";
-
-                       return service;
-               };
-               o.write = function(section_id, formvalue) {
-                       if (formvalue != '-') {
-                               uci.set('ddns', section_id, 'update_url', null);
-                               uci.set('ddns', section_id, 'update_script', null);
-                               return uci.set('ddns', section_id, 'service_name', formvalue);
-                       }
-                       return uci.set('ddns', section_id, 'service_name', null);
-               };
-
-               o = s.taboption('basic', form.Value, 'update_url',
-                       _("Custom update-URL"),
-                       _("Update URL to be used for updating your DDNS Provider.")
-                       + "<br />" +
-                       _("Follow instructions you will find on their WEB page."));
-               o.modalonly = true;
-               o.rmempty = true;
-               o.optional = true;
-               o.depends("ipv6_service_name","-");
-               o.depends("ipv4_service_name","-");
-               o.validate = function(section_id, value) {
-                       var other = this.section.children.filter(function(o) { return o.option == 'update_script' })[0].formvalue(section_id);
-
-                       if ((value == "" && other == "") || (value != "" && other != "")) {
-                               return _("Insert a Update Script OR a Update URL");
-                       }
-
-                       return true;
-               };
-
-               o = s.taboption('basic', form.Value, 'update_script',
-                       _("Custom update-script"),
-                       _("Custom update script to be used for updating your DDNS Provider."));
-               o.modalonly = true;
-               o.rmempty = true;
-               o.optional = true;
-               o.depends("ipv6_service_name","-");
-               o.depends("ipv4_service_name","-");
-               o.validate = function(section_id, value) {
-                       var other = this.section.children.filter(function(o) { return o.option == 'update_url' })[0].formvalue(section_id);
-
-                       if ((value == "" && other == "") || (value != "" && other != "")) {
-                               return _("Insert a Update Script OR a Update URL");
-                       }
-
-                       return true;
-               };
-
-               o = s.taboption('basic', form.Value, 'domain',
-                       _("Domain"),
-                       _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
-               o.modalonly = true;
-               o.rmempty = false;
-
-               o = s.taboption('basic', form.Value, 'username',
-                       _("Username"),
-                       _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
-               o.modalonly = true;
-               o.rmempty = false;
-
-               o = s.taboption('basic', form.Value, 'password',
-                       _("Password"),
-                       _("Replaces [PASSWORD] in Update-URL (URL-encoded)"));
-               o.password = true;
-               o.modalonly = true;
-               o.rmempty = false;
-
-               o = s.taboption('basic', form.Value, 'param_enc',
-                       _("Optional Encoded Parameter"),
-                       _("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)"));
-               o.optional = true;
-               o.modalonly = true;
-
-               o = s.taboption('basic', form.Value, 'param_opt',
-                       _("Optional Parameter"),
-                       _("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)"));
-               o.optional = true;
-               o.modalonly = true;
-
-               if (env['has_ssl']) {
-                       o = s.taboption('basic', form.Flag, 'use_https',
-                               _("Use HTTP Secure"),
-                               _("Enable secure communication with DDNS provider"));
-                       o.optional = true;
-                       o.modalonly = true;
-
-                       o = s.taboption('basic', form.Value, 'cacert',
-                               _("Path to CA-Certificate"),
-                               _("directory or path/file")
-                               + "<br />" +
-                               _("or")
-                               + '<b>' + " IGNORE " + '</b>' +
-                               _("to run HTTPS without verification of server certificates (insecure)"));
-                       o.modalonly = true;
-                       o.depends("use_https", "1");
-                       o.placeholder = "/etc/ssl/certs";
-                       o.rmempty = false;
-               };
-
-
-               o = s.taboption('advanced', form.ListValue, 'ip_source',
-                       _("IP address source"),
-                       _("Defines the source to read systems IP-Address from, that will be send to the DDNS provider"));
-               o.modalonly = true;
-               o.default = "network";
-               o.value("network", _("Network"));
-               o.value("web", _("URL"));
-               o.value("interface", _("Interface"));
-               o.value("script", _("Script"));
-               o.write = function(section_id, formvalue) {
-                       switch(formvalue) {
-                               case 'network':
-                                       uci.set('ddns', section_id, "ip_url",null);
-                                       uci.set('ddns', section_id, "ip_interface",null);
-                                       uci.set('ddns', section_id, "ip_script",null);
-                                       break;
-                               case 'web':
-                                       uci.set('ddns', section_id, "ip_network",null);
-                                       uci.set('ddns', section_id, "ip_interface",null);
-                                       uci.set('ddns', section_id, "ip_script",null);
-                                       break;
-                               case 'interface':
-                                       uci.set('ddns', section_id, "ip_network",null);
-                                       uci.set('ddns', section_id, "ip_url",null);
-                                       uci.set('ddns', section_id, "ip_script",null);
-                                       break;
-                               case 'script':
-                                       uci.set('ddns', section_id, "ip_network",null);
-                                       uci.set('ddns', section_id, "ip_url",null);
-                                       uci.set('ddns', section_id, "ip_interface",null);
-                                       break;
-                               default:
-                                       break;
-                       };
-
-                       return uci.set('ddns', section_id, 'ip_source', formvalue )
-               };
-
-               o = s.taboption('advanced', widgets.NetworkSelect, 'ip_network',
-                       _("Network"),
-                       _("Defines the network to read systems IP-Address from"));
-               o.depends('ip_source','network');
-               o.modalonly = true;
-               o.default = 'wan';
-               o.multiple = false;
-
-               o = s.taboption('advanced', form.Value, 'ip_url',
-                       _("URL to detect"),
-                       _("Defines the Web page to read systems IP-Address from.")
-                       + '<br />' +
-                       String.format('%s %s', _('Example for IPv4'), ': http://checkip.dyndns.com')
-                       + '<br />' +
-                       String.format('%s %s', _('Example for IPv6'), ': http://checkipv6.dyndns.com'));
-               o.depends("ip_source", "web")
-               o.modalonly = true;
-
-               o = s.taboption('advanced', widgets.DeviceSelect, 'ip_interface',
-                       _("Interface"),
-                       _("Defines the interface to read systems IP-Address from"));
-               o.modalonly = true;
-               o.depends("ip_source", "interface")
-               o.multiple = false;
-               o.default = 'wan';
-
-               o = s.taboption('advanced', form.Value, 'ip_script',
-                       _("Script"),
-                       _("User defined script to read systems IP-Address"));
-               o.modalonly = true;
-               o.depends("ip_source", "script")
-               o.placeholder = "/path/to/script.sh"
-
-               o = s.taboption('advanced', widgets.DeviceSelect, 'interface',
-                       _("Event Network"),
-                       _("Network on which the ddns-updater scripts will be started"));
-               o.modalonly = true;
-               o.multiple = false;
-               o.default = 'wan';
-               o.depends("ip_source", "web");
-               o.depends("ip_source", "script");
-
-               o = s.taboption('advanced', form.DummyValue, '_interface',
-                       _("Event Network"),
-                       _("Network on which the ddns-updater scripts will be started"));
-               o.depends("ip_source", "interface");
-               o.depends("ip_source", "network");
-               o.forcewrite = true;
-               o.modalonly = true;
-               o.cfgvalue = function(section_id) {
-                       return uci.get('ddns', section_id, 'interface') || _('This will be autoset to the selected interface');
-               };
-               o.write = function(section_id) {
-                       var opt = this.section.children.filter(function(o) { return o.option == 'ip_source' })[0].formvalue(section_id);
-                       var val = this.section.children.filter(function(o) { return o.option == 'ip_'+opt })[0].formvalue(section_id);
-                       return uci.set('ddns', section_id, 'interface', val);
-               };
-
-               if (env['has_bindnet']) {
-                       o = s.taboption('advanced', widgets.ZoneSelect, 'bind_network',
-                               _("Bind Network"),
-                               _('OPTIONAL: Network to use for communication')
-                               + '<br />' +
-                               _("Network on which the ddns-updater scripts will be started"));
-                       o.depends("ip_source", "web");
-                       o.optional = true;
-                       o.rmempty = true;
-                       o.modalonly = true;
-               }
-
-               if (env['has_forceip']) {
-                       o = s.taboption('advanced', form.Flag, 'force_ipversion',
-                               _("Force IP Version"),
-                               _('OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.'));
-                       o.optional = true;
-                       o.rmempty = true;
-                       o.modalonly = true;
-               }
-
-               if (env['has_dnsserver']) {
-                       o = s.taboption("advanced", form.Value, "dns_server",
-                               _("DNS-Server"),
-                               _("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.")
-                               + "<br />" +
-                               _("Format: IP or FQDN"));
-                       o.placeholder = "mydns.lan"
-                       o.optional = true;
-                       o.rmempty = true;
-                       o.modalonly = true;
-               }
-
-               if (env['has_bindhost']) {
-                       o = s.taboption("advanced", form.Flag, "force_dnstcp",
-                               _("Force TCP on DNS"),
-                               _("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests."));
-                       o.optional = true;
-                       o.rmempty = true;
-                       o.modalonly = true;
-               }
-
-               if (env['has_proxy']) {
-                       o = s.taboption("advanced", form.Value, "proxy",
-                               _("PROXY-Server"),
-                               _("OPTIONAL: Proxy-Server for detection and updates.")
-                               + "<br />" +
-                               String.format('%s: <b>%s</b>', _("Format"), "[user:password@]proxyhost:port")
-                               + "<br />" +
-                               String.format('%s: <b>%s</b>', _("IPv6 address must be given in square brackets"), "[2001:db8::1]:8080"));
-                       o.optional = true;
-                       o.rmempty = true;
-                       o.modalonly = true;
-               }
-
-               o = s.taboption("advanced", form.ListValue, "use_syslog",
-                       _("Log to syslog"),
-                       _("Writes log messages to syslog. Critical Errors will always be written to syslog."));
-               o.modalonly = true;
-               o.default = "2"
-               o.optional = true;
-               o.value("0", _("No logging"))
-               o.value("1", _("Info"))
-               o.value("2", _("Notice"))
-               o.value("3", _("Warning"))
-               o.value("4", _("Error"))
-
-               o = s.taboption("advanced", form.Flag, "use_logfile",
-                       _("Log to file"));
-               o.default = '1';
-               o.optional = true;
-               o.modalonly = true;
-               o.cfgvalue = function(section_id) {
-                       this.description = _("Writes detailed messages to log file. File will be truncated automatically.") + "<br />" +
-                       _("File") + ': "' + logdir + '/' + section_id + '.log"';
-                       return uci.get('ddns', section_id, 'use_logfile');
-               };
-
-
-               o = s.taboption("timer", form.Value, "check_interval",
-                       _("Check Interval"));
-               o.placeholder = "30";
-               o.modalonly = true;
-               o.datatype = 'uinteger';
-               o.validate = function(section_id, formvalue) {
-                       var unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id),
-                               time_to_sec = time_res[unit || 'minutes'] * formvalue;
-
-                       if (formvalue && time_to_sec < 300)
-                               return _('Values below 5 minutes == 300 seconds are not supported');
-
-                       return true;
-               };
-
-               o = s.taboption("timer", form.ListValue, "check_unit",
-                       _('Check Unit'),
-                       _("Interval unit to check for changed IP"));
-               o.modalonly = true;
-               o.default  = "minutes"
-               o.value("seconds", _("seconds"));
-               o.value("minutes", _("minutes"));
-               o.value("hours", _("hours"));
-
-               o = s.taboption("timer", form.Value, "force_interval",
-                       _("Force Interval"),
-                       _("Interval to force updates send to DDNS Provider")
-                       + "<br />" +
-                       _("Setting this parameter to 0 will force the script to only run once"));
-               o.placeholder = "72";
-               o.optional = true;
-               o.modalonly = true;
-               o.datatype = 'uinteger';
-               o.validate = function(section_id, formvalue) {
-
-                       if (!formvalue)
-                               return true;
-
-                       var check_unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id),
-                               check_val = this.section.children.filter(function(o) { return o.option == 'check_interval' })[0].formvalue(section_id),
-                               force_unit = this.section.children.filter(function(o) { return o.option == 'force_unit' })[0].formvalue(section_id),
-                               check_to_sec = time_res[check_unit || 'minutes'] * ( check_val || '30'),
-                               force_to_sec = time_res[force_unit || 'minutes'] * formvalue;
-
-                       if (force_to_sec != 0 && force_to_sec < check_to_sec)
-                               return _("Values lower 'Check Interval' except '0' are not supported");
-
-                       return true;
-               };
-
-               o = s.taboption("timer", form.ListValue, "force_unit", 
-                       _('Force Unit'),
-                       _("Interval unit to force updates send to DDNS Provider"));
-               o.modalonly = true;
-               o.optional = true;
-               o.default  = "minutes"
-               o.value("minutes", _("minutes"));
-               o.value("hours", _("hours"));
-               o.value("days", _("days"));
-
-               o = s.taboption("timer", form.Value, "retry_count",
-                       _("Error Retry Counter"),
-                       _("On Error the script will stop execution after given number of retrys")
-                       + "<br />" +
-                       _("The default setting of '0' will retry infinite."));
-               o.placeholder = "0";
-               o.optional = true;
-               o.modalonly = true;
-               o.datatype = 'uinteger';
-
-               o = s.taboption("timer", form.Value, "retry_interval",
-                       _("Error Retry Interval"),
-                       _("On Error the script will stop execution after given number of retrys")
-                       + "<br />" +
-                       _("The default setting of '0' will retry infinite."));
-               o.placeholder = "60";
-               o.optional = true;
-               o.modalonly = true;
-               o.datatype = 'uinteger';
-
-               o = s.taboption("timer", form.ListValue, "retry_unit",
-                       _('Retry Unit'),
-                       _("On Error the script will retry the failed action after given time"));
-               o.modalonly = true;
-               o.optional = true;
-               o.default  = "seconds"
-               o.value("seconds", _("seconds"));
-               o.value("minutes", _("minutes"));
-
-
-               o = s.taboption("logview", form.DummyValue, '_read_log', '');
-               o.depends('use_logfile','1');
-               o.modalonly = true;
-               o.cfgvalue = function(section_id) {
-                       return E([], [
-                               E('button', {
-                               'class': 'cbi-button cbi-button-apply',
-                               'click': L.ui.createHandlerFn(this, function() {
-                                       var o = this.section.children.filter(function(o) { return o.option == '_logview' })[0];
-                                       return callGetLogServices(section_id).then(L.bind(o.update_log, o));
-                               })
-                       }, _('Read / Reread log file'))]);
-               };
-
-               o = s.taboption("logview", form.DummyValue, "_logview");
-               o.depends('use_logfile','1');
-               o.modalonly = true;
-
-               o.update_log = L.bind(function(view, log_data) {
-                       return document.getElementById('log_area').textContent = log_data.result;
-               }, o, this)
-
-               o.render = L.bind(function() {
-                       return E([
-                               E('p', {}, _('This is the current content of the log file in ') + logdir + ' for this service.'),
-                               E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20, 'readonly' : 'readonly', 'id' : 'log_area' }, _('Please press [Read] button') ))
-                       ]);
-               }, o, this)
-
                return m.render().then(L.bind(function(m, nodes) {
                        poll.add(L.bind(function() {
                                return Promise.all([
                                        this.callDDnsGetServicesStatus(),
-                                       callDDnsGetStatus()
+                                       this.callDDnsGetStatus()
                                ]).then(L.bind(this.poll_status, this, nodes));
                        }, this), 5);
                        return nodes;
index 7710ee66f47e0b8901e5d1254f60824f2dc954a0..0a60142e6f713febe53e10d44a6e365f3cdf4b6c 100755 (executable)
@@ -7,6 +7,7 @@ local UCI = require "luci.model.uci"
 local sys  = require "luci.sys"
 local util = require "luci.util"
 
+local ddns_package_path = "/usr/share/ddns"
 local luci_helper = "/usr/lib/ddns/dynamic_dns_lucihelper.sh"
 local srv_name    = "ddns-scripts"
 
@@ -155,6 +156,7 @@ local methods = {
                        local ipkg = require "luci.model.ipkg"
                        local uci = UCI.cursor()
                        local dateformat = uci:get("ddns", "global", "ddns_dateformat") or "%F %R"
+                       local services_mtime = fs.stat(ddns_package_path .. "/list", 'mtime')
                        uci:unload("ddns")
                        local ver, srv_ver_cmd
                        local res = {}
@@ -169,6 +171,7 @@ local methods = {
                        res['_version'] = ver and #ver > 0 and ver or nil
                        res['_enabled'] = sys.init.enabled("ddns")
                        res['_curr_dateformat'] = os.date(dateformat)
+                       res['_services_list'] = services_mtime and os.date(dateformat, services_mtime) or 'NO_LIST'
 
                        return res
                end
index 94952792f03dc682c24c0888803031a4092909c9..298378452d28f3b4a67f2ee01602aed5f4aa1168 100644 (file)
@@ -7,8 +7,12 @@
                                "luci": [ "setInitAction" ]
                        },
                        "file": {
-                               "/etc/ddns/services": [ "read" ],
-                               "/etc/ddns/services_ipv6": [ "read" ],
+                               "/usr/share/ddns/default": [ "list" ],
+                               "/usr/share/ddns/default/*": [ "read" ],
+                               "/usr/share/ddns/custom": [ "list" ],
+                               "/usr/share/ddns/custom/*": [ "read" ],
+                               "/usr/share/ddns/list": [ "read" ],
+                               "/usr/bin/ddns": [ "exec" ],
                                "/usr/lib/ddns/dynamic_dns_lucihelper.sh": [ "exec" ]
                        },
                        "uci": [ "ddns" ]