10 'require tools.widgets as widgets';
15 'Verify' : _("Verify"),
16 'Run once' : _("Run once"),
17 'Disabled' : _("Disabled"),
18 'Stopped' : _("Stopped")
27 callGetLogServices
: rpc
.declare({
29 method
: 'get_services_log',
30 params
: [ 'service_name' ],
34 callInitAction
: rpc
.declare({
36 method
: 'setInitAction',
37 params
: [ 'name', 'action' ],
38 expect
: { result
: false }
41 callDDnsGetStatus
: rpc
.declare({
43 method
: 'get_ddns_state',
47 callDDnsGetEnv
: rpc
.declare({
53 callDDnsGetServicesStatus
: rpc
.declare({
55 method
: 'get_services_status',
62 * Services list is gen by 3 different source:
63 * 1. /usr/share/ddns/default contains the service installed by opkg
64 * 2. /usr/share/ddns/custom contains any service installed by the
65 * user or the ddns script (for example when service are
67 * 3. /usr/share/ddns/list contains all the service that can be
68 * downloaded by using the ddns script ('service on demand' feature)
70 * (Special services that requires a dedicated package ARE NOT
71 * supported by the 'service on demand' feature)
73 callGenServiceList: function(m
, ev
) {
75 L
.resolveDefault(fs
.list('/usr/share/ddns/default'), []),
76 L
.resolveDefault(fs
.list('/usr/share/ddns/custom'), []),
77 L
.resolveDefault(fs
.read('/usr/share/ddns/list'), null)
78 ]).then(L
.bind(function (data
) {
79 var default_service
= data
[0],
80 custom_service
= data
[1],
81 list_service
= data
[2] && data
[2].split("\n") || [],
86 default_service
.forEach(function (service
) {
87 _this
.services
[service
.name
.replace('.json','')] = true
90 custom_service
.forEach(function (service
) {
91 _this
.services
[service
.name
.replace('.json','')] = true
94 this.services
= Object
.fromEntries(Object
.entries(this.services
).sort());
96 list_service
.forEach(function (service
) {
97 if (!_this
.services
[service
])
98 _this
.services
[service
] = false;
104 * Check if the service is supported.
105 * If the script doesn't find any json assume a 'service on demand' install.
106 * If a json is found check if the ip type is supported.
107 * Invalidate the service_name if is not supported.
109 handleCheckService : function(s
, service_name
, ipv6
, ev
, section_id
) {
111 var value
= service_name
.formvalue(section_id
);
112 s
.service_supported
= null;
113 service_name
.triggerValidation(section_id
);
115 return this.handleGetServiceData(value
)
116 .then(L
.bind(function (service_data
) {
117 if (value
!= '-' && service_data
) {
118 service_data
= JSON
.parse(service_data
);
119 if (ipv6
.formvalue(section_id
) == "1" && !service_data
.ipv6
) {
120 s
.service_supported
= false;
124 s
.service_supported
= true;
126 .then(L
.bind(service_name
.triggerValidation
, service_name
, section_id
))
129 handleGetServiceData: function(service
) {
131 L
.resolveDefault(fs
.read('/usr/share/ddns/custom/'+service
+'.json'), null),
132 L
.resolveDefault(fs
.read('/usr/share/ddns/default/'+service
+'.json'), null)
133 ]).then(function(data
) {
134 return data
[0] || data
[1] || null;
138 handleInstallService: function(m
, service_name
, section_id
, section
, _this
, ev
) {
139 var service
= service_name
.formvalue(section_id
)
140 return fs
.exec('/usr/bin/ddns', ['service', 'install', service
])
141 .then(L
.bind(_this
.callGenServiceList
, _this
))
142 .then(L
.bind(m
.render
, m
))
143 .then(L
.bind(this.renderMoreOptionsModal
, this, section
))
144 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
147 handleRefreshServicesList: function(m
, ev
) {
148 return fs
.exec('/usr/bin/ddns', ['service', 'update'])
149 .then(L
.bind(this.load
, this))
150 .then(L
.bind(this.render
, this))
151 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
154 handleReloadDDnsRule: function(m
, section_id
, ev
) {
155 return fs
.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
156 [ '-S', section_id
, '--', 'start' ])
157 .then(L
.bind(m
.load
, m
))
158 .then(L
.bind(m
.render
, m
))
159 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
162 HandleStopDDnsRule: function(m
, section_id
, ev
) {
163 return fs
.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
164 [ '-S', section_id
, '--', 'start' ])
165 .then(L
.bind(m
.render
, m
))
166 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
169 handleToggleDDns: function(m
, ev
) {
170 return this.callInitAction('ddns', 'enabled')
171 .then(L
.bind(function (action
) { return this.callInitAction('ddns', action
? 'disable' : 'enable')}, this))
172 .then(L
.bind(function (action
) { return this.callInitAction('ddns', action
? 'stop' : 'start')}, this))
173 .then(L
.bind(m
.render
, m
))
174 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
177 handleRestartDDns: function(m
, ev
) {
178 return this.callInitAction('ddns', 'restart')
179 .then(L
.bind(m
.render
, m
));
182 poll_status: function(map
, data
) {
183 var status
= data
[1] || [], service
= data
[0] || [], rows
= map
.querySelectorAll('.cbi-section-table-row[data-sid]'),
184 section_id
, cfg_detail_ip
, cfg_update
, cfg_status
, host
, ip
, last_update
,
185 next_update
, service_status
, reload
, cfg_enabled
, stop
,
186 ddns_enabled
= map
.querySelector('[data-name="_enabled"]').querySelector('.cbi-value-field'),
187 ddns_toggle
= map
.querySelector('[data-name="_toggle"]').querySelector('button'),
188 services_list
= map
.querySelector('[data-name="_services_list"]').querySelector('.cbi-value-field');
190 ddns_toggle
.innerHTML
= status
['_enabled'] ? _('Stop DDNS') : _('Start DDNS')
191 services_list
.innerHTML
= status
['_services_list'];
193 dom
.content(ddns_enabled
, function() {
195 E('div', {}, status
['_enabled'] ? _('DDNS Autostart enabled') : [
196 _('DDNS Autostart disabled'),
197 E('div', { 'class' : 'cbi-value-description' },
198 _("Currently DDNS updates are not started at boot or on interface events.") + "<br />" +
199 _("This is the default if you run DDNS scripts by yourself (i.e. via cron with force_interval set to '0')"))
203 for (var i
= 0; i
< rows
.length
; i
++) {
204 section_id
= rows
[i
].getAttribute('data-sid');
205 cfg_detail_ip
= rows
[i
].querySelector('[data-name="_cfg_detail_ip"]');
206 cfg_update
= rows
[i
].querySelector('[data-name="_cfg_update"]');
207 cfg_status
= rows
[i
].querySelector('[data-name="_cfg_status"]');
208 reload
= rows
[i
].querySelector('.cbi-section-actions .reload');
209 stop
= rows
[i
].querySelector('.cbi-section-actions .stop');
210 cfg_enabled
= uci
.get('ddns', section_id
, 'enabled');
212 reload
.disabled
= (status
['_enabled'] == 0 || cfg_enabled
== 0);
214 host
= uci
.get('ddns', section_id
, 'lookup_host') || _('Configuration Error');
216 last_update
= _('Never');
217 next_update
= _('Unknown');
218 service_status
= '<b>' + _('Not Running') + '</b>';
220 if (service
[section_id
]) {
221 stop
.disabled
= (!service
[section_id
].pid
|| (service
[section_id
].pid
&& cfg_enabled
== '1'));
222 if (service
[section_id
].ip
)
223 ip
= service
[section_id
].ip
;
224 if (service
[section_id
].last_update
)
225 last_update
= service
[section_id
].last_update
;
226 if (service
[section_id
].next_update
)
227 next_update
= this.NextUpdateStrings
[service
[section_id
].next_update
] || service
[section_id
].next_update
;
228 if (service
[section_id
].pid
)
229 service_status
= '<b>' + _('Running') + '</b> : ' + service
[section_id
].pid
;
232 cfg_detail_ip
.innerHTML
= host
+ '<br />' + ip
;
233 cfg_update
.innerHTML
= last_update
+ '<br />' + next_update
;
234 cfg_status
.innerHTML
= service_status
;
242 this.callDDnsGetServicesStatus(),
243 this.callDDnsGetStatus(),
244 this.callDDnsGetEnv(),
245 this.callGenServiceList(),
250 render: function(data
) {
251 var resolved
= data
[0] || [];
252 var status
= data
[1] || [];
253 var env
= data
[2] || [];
254 var logdir
= uci
.get('ddns', 'global', 'ddns_logdir') || "/var/log/ddns";
260 m
= new form
.Map('ddns', _('Dynamic DNS'));
262 s
= m
.section(form
.NamedSection
, 'global', 'ddns',);
264 s
.tab('info', _('Information'));
265 s
.tab('global', _('Global Settings'));
267 o
= s
.taboption('info', form
.DummyValue
, '_version', _('Dynamic DNS Version'));
268 o
.cfgvalue = function() {
269 return status
[this.option
];
272 o
= s
.taboption('info', form
.DummyValue
, '_enabled', _('State'));
273 o
.cfgvalue = function() {
274 var res
= status
[this.option
];
276 this.description
= _("Currently DDNS updates are not started at boot or on interface events.") + "<br />" +
277 _("This is the default if you run DDNS scripts by yourself (i.e. via cron with force_interval set to '0')")
279 return res
? _('DDNS Autostart enabled') : _('DDNS Autostart disabled')
282 o
= s
.taboption('info', form
.Button
, '_toggle');
284 o
.inputtitle
= _((status
['_enabled'] ? 'stop' : 'start').toUpperCase() + ' DDns');
285 o
.inputstyle
= 'apply';
286 o
.onclick
= L
.bind(this.handleToggleDDns
, this, m
);
288 o
= s
.taboption('info', form
.Button
, '_restart');
290 o
.inputtitle
= _('Restart DDns');
291 o
.inputstyle
= 'apply';
292 o
.onclick
= L
.bind(this.handleRestartDDns
, this, m
);
294 o
= s
.taboption('info', form
.DummyValue
, '_services_list', _('Services list last update'));
295 o
.cfgvalue = function() {
296 return status
[this.option
];
299 o
= s
.taboption('info', form
.Button
, '_refresh_services');
301 o
.inputtitle
= _('Update DDns Services List');
302 o
.inputstyle
= 'apply';
303 o
.onclick
= L
.bind(this.handleRefreshServicesList
, this, m
);
307 if (!env
['has_ipv6']) {
308 o
= s
.taboption('info', form
.DummyValue
, '_no_ipv6');
310 o
.title
= '<b>' + _("IPv6 not supported") + '</b>';
311 o
.cfgvalue = function() { return _("IPv6 is currently not (fully) supported by this system") + "<br />" +
312 _("Please follow the instructions on OpenWrt's homepage to enable IPv6 support") + "<br />" +
313 _("or update your system to the latest OpenWrt Release")};
316 if (!env
['has_ssl']) {
317 o
= s
.taboption('info', form
.DummyValue
, '_no_https');
318 o
.titleref
= L
.url("admin", "system", "opkg")
320 o
.title
= '<b>' + _("HTTPS not supported") + '</b>';
321 o
.cfgvalue = function() { return _("Neither GNU Wget with SSL nor cURL installed to support secure updates via HTTPS protocol.") +
323 _("You should install 'wget' or 'curl' or 'uclient-fetch' with 'libustream-*ssl' package.") +
325 _("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.")};
328 if (!env
['has_bindnet']) {
329 o
= s
.taboption('info', form
.DummyValue
, '_no_bind_network');
330 o
.titleref
= L
.url("admin", "system", "opkg")
332 o
.title
= '<b>' + _("Binding to a specific network not supported") + '</b>';
333 o
.cfgvalue = function() { return _("Neither GNU Wget with SSL nor cURL installed to select a network to use for communication.") +
335 _("You should install 'wget' or 'curl' package.") +
337 _("GNU Wget will use the IP of given network, cURL will use the physical interface.") +
339 _("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.")};
342 if (!env
['has_proxy']) {
343 o
= s
.taboption('info', form
.DummyValue
, '_no_proxy');
344 o
.titleref
= L
.url("admin", "system", "opkg")
346 o
.title
= '<b>' + _("cURL without Proxy Support") + '</b>';
347 o
.cfgvalue = function() { return _("cURL is installed, but libcurl was compiled without proxy support.") +
349 _("You should install 'wget' or 'uclient-fetch' package or replace libcurl.") +
351 _("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.")};
354 if (!env
['has_forceip']) {
355 o
= s
.taboption('info', form
.DummyValue
, '_no_force_ip');
356 o
.titleref
= L
.url("admin", "system", "opkg")
358 o
.title
= '<b>' + _("Force IP Version not supported") + '</b>';
359 o
.cfgvalue = function() { return _("BusyBox's nslookup and Wget do not support to specify " +
360 "the IP version to use for communication with DDNS Provider!") +
361 "<br />- " + _("You should install 'wget' or 'curl' or 'uclient-fetch' package.")
365 if (!env
['has_bindhost']) {
366 o
= s
.taboption('info', form
.DummyValue
, '_no_dnstcp');
367 o
.titleref
= L
.url("admin", "system", "opkg")
369 o
.title
= '<b>' + _("DNS requests via TCP not supported") + '</b>';
370 o
.cfgvalue = function() { return _("BusyBox's nslookup and hostip do not support to specify to use TCP " +
371 "instead of default UDP when requesting DNS server!") +
373 _("You should install 'bind-host' or 'knot-host' or 'drill' package for DNS requests.")};
376 if (!env
['has_dnsserver']) {
377 o
= s
.taboption('info', form
.DummyValue
, '_no_dnsserver');
378 o
.titleref
= L
.url("admin", "system", "opkg")
380 o
.title
= '<b>' + _("Using specific DNS Server not supported") + '</b>';
381 o
.cfgvalue = function() { return _("BusyBox's nslookup in the current compiled version " +
382 "does not handle given DNS Servers correctly!") +
384 _("You should install 'bind-host' or 'knot-host' or 'drill' or 'hostip' package, " +
385 "if you need to specify a DNS server to detect your registered IP.")};
388 if (env
['has_ssl'] && !env
['has_cacerts']) {
389 o
= s
.taboption('info', form
.DummyValue
, '_no_certs');
390 o
.titleref
= L
.url("admin", "system", "opkg")
392 o
.title
= '<b>' + _("No certificates found") + '</b>';
393 o
.cfgvalue = function() { return _("If using secure communication you should verify server certificates!") +
395 _("Install 'ca-certificates' package or needed certificates " +
396 "by hand into /etc/ssl/certs default directory")};
399 // Advanced Configuration Section
401 o
= s
.taboption('global', form
.Flag
, 'upd_privateip', _("Allow non-public IP's"));
402 o
.description
= _("Non-public and by default blocked IP's") + ':'
403 + '<br /><strong>IPv4: </strong>'
404 + '0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16'
405 + '<br /><strong>IPv6: </strong>'
406 + '::/32, f000::/4"';
410 o
= s
.taboption('global', form
.Value
, 'ddns_dateformat', _('Date format'));
411 o
.description
= '<a href="http://www.cplusplus.com/reference/ctime/strftime/" target="_blank">'
412 + _("For supported codes look here")
414 _('Current setting: ') + '<b>' + status
['_curr_dateformat'] + '</b>';
419 o
= s
.taboption('global', form
.Value
, 'ddns_rundir', _('Status directory'));
420 o
.description
= _('Directory contains PID and other status information for each running section.');
421 o
.default = "/var/run/ddns";
425 o
= s
.taboption('global', form
.Value
, 'ddns_logdir', _('Log directory'));
426 o
.description
= _('Directory contains Log files for each running section.');
427 o
.default = "/var/log/ddns";
430 o
.validate = function(section_id
, formvalue
) {
431 if (formvalue
.indexOf('../') !== -1)
432 return _('"../" not allowed in path for Security Reason.')
437 o
= s
.taboption('global', form
.Value
, 'ddns_loglines', _('Log length'));
438 o
.description
= _('Number of last lines stored in log files');
439 o
.datatype
= 'min(1)';
442 if (env
['has_wget'] && env
['has_curl']) {
444 o
= s
.taboption('global', form
.Flag
, 'use_curl', _('Use cURL'));
445 o
.description
= _('If Wget and cURL package are installed, Wget is used for communication by default.');
452 o
= s
.taboption('global', form
.Value
, 'cacert', _('Ca Certs path'));
453 o
.description
= _('Ca Certs path that will be used to download services data. Set IGNORE to skip certificate validation.');
454 o
.placeholder
= 'IGNORE';
456 o
= s
.taboption('global', form
.Value
, 'services_url', _('Services URL Download'));
457 o
.description
= _('Url used to download services file. By default is the master openwrt ddns package repo.');
458 o
.placeholder
= 'https://raw.githubusercontent.com/openwrt/packages/master/net/ddns-scripts/files';
461 s
= m
.section(form
.GridSection
, 'service', _('Services'));
464 s
.addbtntitle
= _('Add new services...');
470 s
.handleCreateDDnsRule = function(m
, name
, service_name
, ipv6
, ev
) {
471 var section_id
= name
.isValid('_new_') ? name
.formvalue('_new_') : null,
472 service_value
= service_name
.isValid('_new_') ? service_name
.formvalue('_new_') : null,
473 ipv6_value
= ipv6
.isValid('_new_') ? ipv6
.formvalue('_new_') : null;
475 if (section_id
== null || section_id
== '' || service_value
== null || section_id
== '' || ipv6_value
== null || ipv6_value
== '')
478 return m
.save(function() {
479 uci
.add('ddns', 'service', section_id
);
480 if (service_value
!= '-') {
481 uci
.set('ddns', section_id
, 'service_name', service_value
);
483 uci
.set('ddns', section_id
, 'use_ipv6', ipv6_value
);
484 }).then(L
.bind(m
.children
[1].renderMoreOptionsModal
, m
.children
[1], section_id
));
487 s
.handleAdd = function(ev
) {
488 var m2
= new form
.Map('ddns'),
489 s2
= m2
.section(form
.NamedSection
, '_new_'),
490 name
, ipv6
, service_name
;
492 s2
.render = function() {
495 this.renderUCISection('_new_')
496 ]).then(this.renderContents
.bind(this));
499 name
= s2
.option(form
.Value
, 'name', _('Name'));
500 name
.rmempty
= false;
501 name
.datatype
= 'uciname';
502 name
.placeholder
= _('New DDns Service…');
503 name
.validate = function(section_id
, value
) {
504 if (uci
.get('ddns', value
) != null)
505 return _('The service name is already used');
510 ipv6
= s2
.option( form
.ListValue
, 'use_ipv6',
511 _("IP address version"),
512 _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
514 ipv6
.value("0", _("IPv4-Address"))
515 if (env
["has_ipv6"]) {
516 ipv6
.value("1", _("IPv6-Address"))
519 service_name
= s2
.option(form
.ListValue
, 'service_name',
520 String
.format('%s', _("DDNS Service provider")));
521 service_name
.value('-',"-- " + _("custom") + " --");
522 Object
.keys(_this
.services
).sort().forEach(name
=> service_name
.value(name
));
523 service_name
.validate = function(section_id
, value
) {
524 if (value
== '') return _("Select a service");
525 if (s2
.service_supported
== null) return _("Checking the service support...");
526 if (!s2
.service_supported
) return _("Service doesn't support this ip type");
530 ipv6
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s2
, service_name
, ipv6
);
531 service_name
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s2
, service_name
, ipv6
);
533 m2
.render().then(L
.bind(function(nodes
) {
534 ui
.showModal(_('Add new services...'), [
536 E('div', { 'class': 'right' }, [
539 'click': ui
.hideModal
540 }, _('Cancel')), ' ',
542 'class': 'cbi-button cbi-button-positive important',
543 'click': ui
.createHandlerFn(this, 'handleCreateDDnsRule', m
, name
, service_name
, ipv6
)
544 }, _('Create service'))
548 nodes
.querySelector('[id="%s"] input[type="text"]'.format(name
.cbid('_new_'))).focus();
552 s
.renderRowActions = function(section_id
) {
553 var tdEl
= this.super('renderRowActions', [ section_id
, _('Edit') ]),
554 cfg_enabled
= uci
.get('ddns', section_id
, 'enabled'),
556 'class': 'cbi-button cbi-button-neutral reload',
557 'click': ui
.createHandlerFn(_this
, 'handleReloadDDnsRule', m
, section_id
),
558 'title': _('Reload this service'),
561 'class': 'cbi-button cbi-button-neutral stop',
562 'click': ui
.createHandlerFn(_this
, 'HandleStopDDnsRule', m
, section_id
),
563 'title': _('Stop this service'),
566 if (status
['_enabled'] == 0 || cfg_enabled
== 0)
567 reload_opt
['disabled'] = 'disabled';
569 if (!resolved
[section_id
] || !resolved
[section_id
].pid
||
570 (resolved
[section_id
].pid
&& cfg_enabled
== '1'))
571 stop_opt
['disabled'] = 'disabled';
573 dom
.content(tdEl
.lastChild
, [
574 E('button', stop_opt
, _('Stop')),
575 E('button', reload_opt
, _('Reload')),
576 tdEl
.lastChild
.childNodes
[0],
577 tdEl
.lastChild
.childNodes
[1],
578 tdEl
.lastChild
.childNodes
[2]
584 s
.modaltitle = function(section_id
) {
585 return _('DDns Service') + ' » ' + section_id
;
588 s
.addModalOptions = function(s
, section_id
) {
590 var service
= uci
.get('ddns', section_id
, 'service_name') || '-',
591 ipv6
= uci
.get('ddns', section_id
, 'use_ipv6'), service_name
, use_ipv6
;
593 return _this
.handleGetServiceData(service
).then(L
.bind(function (service_data
) {
594 s
.service_available
= true;
595 s
.service_supported
= true;
597 if (service
!= '-') {
599 s
.service_available
= false;
601 service_data
= JSON
.parse(service_data
);
602 if (ipv6
== "1" && !service_data
.ipv6
)
603 s
.service_supported
= false;
607 s
.tab('basic', _('Basic Settings'));
608 s
.tab('advanced', _('Advanced Settings'));
609 s
.tab('timer', _('Timer Settings'));
610 s
.tab('logview', _('Log File Viewer'));
612 o
= s
.taboption('basic', form
.Flag
, 'enabled',
614 _("If this service section is disabled it could not be started.")
616 _("Neither from LuCI interface nor from console."));
621 o
= s
.taboption('basic', form
.Value
, 'lookup_host',
622 _("Lookup Hostname"),
623 _("Hostname/FQDN to validate, if IP update happen or necessary"));
625 o
.placeholder
= "myhost.example.com";
626 o
.datatype
= 'and(minlength(3),hostname("strict"))';
629 use_ipv6
= s
.taboption('basic', form
.ListValue
, 'use_ipv6',
630 _("IP address version"),
631 _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
632 use_ipv6
.default = '0';
633 use_ipv6
.modalonly
= true;
634 use_ipv6
.rmempty
= false;
635 use_ipv6
.value("0", _("IPv4-Address"))
636 if (env
["has_ipv6"]) {
637 use_ipv6
.value("1", _("IPv6-Address"))
640 service_name
= s
.taboption('basic', form
.ListValue
, 'service_name',
641 String
.format('%s', _("DDNS Service provider")));
642 service_name
.modalonly
= true;
643 service_name
.value('-',"-- " + _("custom") + " --");
644 Object
.keys(_this
.services
).sort().forEach(name
=> service_name
.value(name
));
645 service_name
.cfgvalue = function(section_id
) {
646 return uci
.get('ddns', section_id
, 'service_name') || '-';
648 service_name
.write = function(section_id
, service
) {
649 if (service
!= '-') {
650 uci
.set('ddns', section_id
, 'update_url', null);
651 uci
.set('ddns', section_id
, 'update_script', null);
652 return uci
.set('ddns', section_id
, 'service_name', service
);
654 return uci
.set('ddns', section_id
, 'service_name', null);
656 service_name
.validate = function(section_id
, value
) {
657 if (value
== '') return _("Select a service");
658 if (s
.service_available
== null) return _("Checking the service support...");
659 if (!s
.service_available
) return _('Service not installed');
660 if (!s
.service_supported
) return _("Service doesn't support this ip type");
664 service_name
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s
, service_name
, use_ipv6
);
665 use_ipv6
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s
, service_name
, use_ipv6
);
667 if (!s
.service_available
) {
668 o
= s
.taboption('basic', form
.Button
, '_download_service');
670 o
.title
= _('Service not installed');
671 o
.inputtitle
= _('Install Service');
672 o
.inputstyle
= 'apply';
673 o
.onclick
= L
.bind(_this
.handleInstallService
,
674 this, m
, service_name
, section_id
, s
.section
, _this
)
677 if (!s
.service_supported
) {
678 o
= s
.taboption('basic', form
.DummyValue
, '_not_supported', ' ');
679 o
.cfgvalue = function () {
680 return _("Service doesn't support this ip type")
684 var service_switch
= s
.taboption('basic', form
.Button
, '_switch_proto');
685 service_switch
.modalonly
= true;
686 service_switch
.title
= _('Really switch service?');
687 service_switch
.inputtitle
= _('Switch service');
688 service_switch
.inputstyle
= 'apply';
689 service_switch
.onclick
= L
.bind(function(ev
) {
690 if (!s
.service_supported
) return;
693 .then(L
.bind(m
.load
, m
))
694 .then(L
.bind(m
.render
, m
))
695 .then(L
.bind(this.renderMoreOptionsModal
, this, s
.section
));
698 if (s
.service_available
&& s
.service_supported
) {
700 o
= s
.taboption('basic', form
.Value
, 'update_url',
701 _("Custom update-URL"),
702 _("Update URL to be used for updating your DDNS Provider.")
704 _("Follow instructions you will find on their WEB page."));
708 o
.depends("service_name","-");
709 o
.validate = function(section_id
, value
) {
710 var other
= this.section
.children
.filter(function(o
) { return o
.option
== 'update_script' })[0].formvalue(section_id
);
712 if ((value
== "" && other
== "") || (value
!= "" && other
!= "")) {
713 return _("Insert a Update Script OR a Update URL");
719 o
= s
.taboption('basic', form
.Value
, 'update_script',
720 _("Custom update-script"),
721 _("Custom update script to be used for updating your DDNS Provider."));
725 o
.depends("service_name","-");
726 o
.validate = function(section_id
, value
) {
727 var other
= this.section
.children
.filter(function(o
) { return o
.option
== 'update_url' })[0].formvalue(section_id
);
729 if ((value
== "" && other
== "") || (value
!= "" && other
!= "")) {
730 return _("Insert a Update Script OR a Update URL");
736 o
= s
.taboption('basic', form
.Value
, 'domain',
738 _("Replaces [DOMAIN] in Update-URL (URL-encoded)"));
742 o
= s
.taboption('basic', form
.Value
, 'username',
744 _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
748 o
= s
.taboption('basic', form
.Value
, 'password',
750 _("Replaces [PASSWORD] in Update-URL (URL-encoded)"));
755 o
= s
.taboption('basic', form
.Value
, 'param_enc',
756 _("Optional Encoded Parameter"),
757 _("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)"));
761 o
= s
.taboption('basic', form
.Value
, 'param_opt',
762 _("Optional Parameter"),
763 _("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)"));
767 if (env
['has_ssl']) {
768 o
= s
.taboption('basic', form
.Flag
, 'use_https',
769 _("Use HTTP Secure"),
770 _("Enable secure communication with DDNS provider"));
774 o
= s
.taboption('basic', form
.Value
, 'cacert',
775 _("Path to CA-Certificate"),
776 _("directory or path/file")
779 + '<b>' + " IGNORE " + '</b>' +
780 _("to run HTTPS without verification of server certificates (insecure)"));
782 o
.depends("use_https", "1");
783 o
.placeholder
= "/etc/ssl/certs";
788 o
= s
.taboption('advanced', form
.ListValue
, 'ip_source',
789 _("IP address source"),
790 _("Defines the source to read systems IP-Address from, that will be send to the DDNS provider"));
792 o
.default = "network";
793 o
.value("network", _("Network"));
794 o
.value("web", _("URL"));
795 o
.value("interface", _("Interface"));
796 o
.value("script", _("Script"));
797 o
.write = function(section_id
, formvalue
) {
800 uci
.set('ddns', section_id
, "ip_url",null);
801 uci
.set('ddns', section_id
, "ip_interface",null);
802 uci
.set('ddns', section_id
, "ip_script",null);
805 uci
.set('ddns', section_id
, "ip_network",null);
806 uci
.set('ddns', section_id
, "ip_interface",null);
807 uci
.set('ddns', section_id
, "ip_script",null);
810 uci
.set('ddns', section_id
, "ip_network",null);
811 uci
.set('ddns', section_id
, "ip_url",null);
812 uci
.set('ddns', section_id
, "ip_script",null);
815 uci
.set('ddns', section_id
, "ip_network",null);
816 uci
.set('ddns', section_id
, "ip_url",null);
817 uci
.set('ddns', section_id
, "ip_interface",null);
823 return uci
.set('ddns', section_id
, 'ip_source', formvalue
)
826 o
= s
.taboption('advanced', widgets
.NetworkSelect
, 'ip_network',
828 _("Defines the network to read systems IP-Address from"));
829 o
.depends('ip_source','network');
834 o
= s
.taboption('advanced', form
.Value
, 'ip_url',
836 _("Defines the Web page to read systems IP-Address from.")
838 String
.format('%s %s', _('Example for IPv4'), ': http://checkip.dyndns.com')
840 String
.format('%s %s', _('Example for IPv6'), ': http://checkipv6.dyndns.com'));
841 o
.depends("ip_source", "web")
844 o
= s
.taboption('advanced', widgets
.DeviceSelect
, 'ip_interface',
846 _("Defines the interface to read systems IP-Address from"));
848 o
.depends("ip_source", "interface")
852 o
= s
.taboption('advanced', form
.Value
, 'ip_script',
854 _("User defined script to read systems IP-Address"));
856 o
.depends("ip_source", "script")
857 o
.placeholder
= "/path/to/script.sh"
859 o
= s
.taboption('advanced', widgets
.DeviceSelect
, 'interface',
861 _("Network on which the ddns-updater scripts will be started"));
865 o
.depends("ip_source", "web");
866 o
.depends("ip_source", "script");
868 o
= s
.taboption('advanced', form
.DummyValue
, '_interface',
870 _("Network on which the ddns-updater scripts will be started"));
871 o
.depends("ip_source", "interface");
872 o
.depends("ip_source", "network");
875 o
.cfgvalue = function(section_id
) {
876 return uci
.get('ddns', section_id
, 'interface') || _('This will be autoset to the selected interface');
878 o
.write = function(section_id
) {
879 var opt
= this.section
.children
.filter(function(o
) { return o
.option
== 'ip_source' })[0].formvalue(section_id
);
880 var val
= this.section
.children
.filter(function(o
) { return o
.option
== 'ip_'+opt
})[0].formvalue(section_id
);
881 return uci
.set('ddns', section_id
, 'interface', val
);
884 if (env
['has_bindnet']) {
885 o
= s
.taboption('advanced', widgets
.NetworkSelect
, 'bind_network',
887 _('OPTIONAL: Network to use for communication')
889 _("Network on which the ddns-updater scripts will be started"));
890 o
.depends("ip_source", "web");
896 if (env
['has_forceip']) {
897 o
= s
.taboption('advanced', form
.Flag
, 'force_ipversion',
898 _("Force IP Version"),
899 _('OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.'));
905 if (env
['has_dnsserver']) {
906 o
= s
.taboption("advanced", form
.Value
, "dns_server",
908 _("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.")
910 _("Format: IP or FQDN"));
911 o
.placeholder
= "mydns.lan"
917 if (env
['has_bindhost']) {
918 o
= s
.taboption("advanced", form
.Flag
, "force_dnstcp",
919 _("Force TCP on DNS"),
920 _("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests."));
926 if (env
['has_proxy']) {
927 o
= s
.taboption("advanced", form
.Value
, "proxy",
929 _("OPTIONAL: Proxy-Server for detection and updates.")
931 String
.format('%s: <b>%s</b>', _("Format"), "[user:password@]proxyhost:port")
933 String
.format('%s: <b>%s</b>', _("IPv6 address must be given in square brackets"), "[2001:db8::1]:8080"));
939 o
= s
.taboption("advanced", form
.ListValue
, "use_syslog",
941 _("Writes log messages to syslog. Critical Errors will always be written to syslog."));
945 o
.value("0", _("No logging"))
946 o
.value("1", _("Info"))
947 o
.value("2", _("Notice"))
948 o
.value("3", _("Warning"))
949 o
.value("4", _("Error"))
951 o
= s
.taboption("advanced", form
.Flag
, "use_logfile",
956 o
.cfgvalue = function(section_id
) {
957 this.description
= _("Writes detailed messages to log file. File will be truncated automatically.") + "<br />" +
958 _("File") + ': "' + logdir
+ '/' + section_id
+ '.log"';
959 return uci
.get('ddns', section_id
, 'use_logfile');
963 o
= s
.taboption("timer", form
.Value
, "check_interval",
964 _("Check Interval"));
965 o
.placeholder
= "30";
967 o
.datatype
= 'uinteger';
968 o
.validate = function(section_id
, formvalue
) {
969 var unit
= this.section
.children
.filter(function(o
) { return o
.option
== 'check_unit' })[0].formvalue(section_id
),
970 time_to_sec
= _this
.time_res
[unit
|| 'minutes'] * formvalue
;
972 if (formvalue
&& time_to_sec
< 300)
973 return _('Values below 5 minutes == 300 seconds are not supported');
978 o
= s
.taboption("timer", form
.ListValue
, "check_unit",
980 _("Interval unit to check for changed IP"));
982 o
.default = "minutes"
983 o
.value("seconds", _("seconds"));
984 o
.value("minutes", _("minutes"));
985 o
.value("hours", _("hours"));
987 o
= s
.taboption("timer", form
.Value
, "force_interval",
989 _("Interval to force updates send to DDNS Provider")
991 _("Setting this parameter to 0 will force the script to only run once"));
992 o
.placeholder
= "72";
995 o
.datatype
= 'uinteger';
996 o
.validate = function(section_id
, formvalue
) {
1001 var check_unit
= this.section
.children
.filter(function(o
) { return o
.option
== 'check_unit' })[0].formvalue(section_id
),
1002 check_val
= this.section
.children
.filter(function(o
) { return o
.option
== 'check_interval' })[0].formvalue(section_id
),
1003 force_unit
= this.section
.children
.filter(function(o
) { return o
.option
== 'force_unit' })[0].formvalue(section_id
),
1004 check_to_sec
= _this
.time_res
[check_unit
|| 'minutes'] * ( check_val
|| '30'),
1005 force_to_sec
= _this
.time_res
[force_unit
|| 'minutes'] * formvalue
;
1007 if (force_to_sec
!= 0 && force_to_sec
< check_to_sec
)
1008 return _("Values lower 'Check Interval' except '0' are not supported");
1013 o
= s
.taboption("timer", form
.ListValue
, "force_unit",
1015 _("Interval unit to force updates sent to DDNS Provider."));
1018 o
.default = "minutes"
1019 o
.value("minutes", _("minutes"));
1020 o
.value("hours", _("hours"));
1021 o
.value("days", _("days"));
1023 o
= s
.taboption("timer", form
.Value
, "retry_max_count",
1024 _("Error Max Retry Counter"),
1025 _("On Error the script will stop execution after the given number of retries.")
1027 _("The default setting of '0' will retry infinitely."));
1028 o
.placeholder
= "0";
1031 o
.datatype
= 'uinteger';
1033 o
= s
.taboption("timer", form
.Value
, "retry_interval",
1034 _("Error Retry Interval"),
1035 _("The interval between which each successive retry commences."));
1036 o
.placeholder
= "60";
1039 o
.datatype
= 'uinteger';
1041 o
= s
.taboption("timer", form
.ListValue
, "retry_unit",
1043 _("Which time units to use for retry counters."));
1046 o
.default = "seconds"
1047 o
.value("seconds", _("seconds"));
1048 o
.value("minutes", _("minutes"));
1050 o
= s
.taboption('logview', form
.Button
, '_read_log');
1052 o
.depends('use_logfile','1');
1054 o
.inputtitle
= _('Read / Reread log file');
1055 o
.inputstyle
= 'apply';
1056 o
.onclick
= L
.bind(function(ev
, section_id
) {
1057 return _this
.callGetLogServices(section_id
).then(L
.bind(log_box
.update_log
, log_box
));
1060 var log_box
= s
.taboption("logview", form
.DummyValue
, "_logview");
1061 log_box
.depends('use_logfile','1');
1062 log_box
.modalonly
= true;
1064 log_box
.update_log
= L
.bind(function(view
, log_data
) {
1065 return document
.getElementById('log_area').textContent
= log_data
.result
;
1068 log_box
.render
= L
.bind(function() {
1070 E('p', {}, _('This is the current content of the log file in %h for this service.').format(logdir
)),
1071 E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20, 'readonly' : 'readonly', 'id' : 'log_area' }, _('Please press [Read] button') ))
1076 for (var i
= 0; i
< s
.children
.length
; i
++) {
1079 case '_switch_proto':
1080 o
.depends({ service_name
: service
, use_ipv6
: ipv6
, "!reverse": true })
1083 case 'service_name':
1085 case 'update_script':
1092 for (var j
= 0; j
< o
.deps
.length
; j
++) {
1093 o
.deps
[j
].service_name
= service
;
1094 o
.deps
[j
].use_ipv6
= ipv6
;
1097 o
.depends({service_name
: service
, use_ipv6
: ipv6
});
1103 o
= s
.option(form
.DummyValue
, '_cfg_status', _('Status'));
1104 o
.modalonly
= false;
1105 o
.textvalue = function(section_id
) {
1106 var text
= '<b>' + _('Not Running') + '</b>';
1108 if (resolved
[section_id
] && resolved
[section_id
].pid
)
1109 text
= '<b>' + _('Running') + '</b> : ' + resolved
[section_id
].pid
;
1114 o
= s
.option(form
.DummyValue
, '_cfg_name', _('Name'));
1115 o
.modalonly
= false;
1116 o
.textvalue = function(section_id
) {
1117 return '<b>' + section_id
+ '</b>';
1120 o
= s
.option(form
.DummyValue
, '_cfg_detail_ip', _('Lookup Hostname') + "<br />" + _('Registered IP'));
1122 o
.modalonly
= false;
1123 o
.textvalue = function(section_id
) {
1124 var host
= uci
.get('ddns', section_id
, 'lookup_host') || _('Configuration Error'),
1126 if (resolved
[section_id
] && resolved
[section_id
].ip
)
1127 ip
= resolved
[section_id
].ip
;
1129 return host
+ '<br />' + ip
;
1132 o
= s
.option(form
.Flag
, 'enabled', _('Enabled'));
1135 o
.modalonly
= false;
1137 o
= s
.option(form
.DummyValue
, '_cfg_update', _('Last Update') + "<br />" + _('Next Update'));
1139 o
.modalonly
= false;
1140 o
.textvalue = function(section_id
) {
1141 var last_update
= _('Never'), next_update
= _('Unknown');
1142 if (resolved
[section_id
]) {
1143 if (resolved
[section_id
].last_update
)
1144 last_update
= resolved
[section_id
].last_update
;
1145 if (resolved
[section_id
].next_update
)
1146 next_update
= _this
.NextUpdateStrings
[resolved
[section_id
].next_update
] || resolved
[section_id
].next_update
;
1149 return last_update
+ '<br />' + next_update
;
1152 return m
.render().then(L
.bind(function(m
, nodes
) {
1153 poll
.add(L
.bind(function() {
1154 return Promise
.all([
1155 this.callDDnsGetServicesStatus(),
1156 this.callDDnsGetStatus()
1157 ]).then(L
.bind(this.poll_status
, this, nodes
));