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 list_service
.forEach(function (service
) {
95 if (!_this
.services
[service
])
96 _this
.services
[service
] = false;
102 * Check if the service is supported.
103 * If the script doesn't find any json assume a 'service on demand' install.
104 * If a json is found check if the ip type is supported.
105 * Invalidate the service_name if is not supported.
107 handleCheckService : function(s
, service_name
, ipv6
, ev
, section_id
) {
109 var value
= service_name
.formvalue(section_id
);
110 s
.service_supported
= null;
111 service_name
.triggerValidation(section_id
);
113 return this.handleGetServiceData(value
)
114 .then(L
.bind(function (service_data
) {
115 if (value
!= '-' && service_data
) {
116 service_data
= JSON
.parse(service_data
);
117 if (ipv6
.formvalue(section_id
) == "1" && !service_data
.ipv6
) {
118 s
.service_supported
= false;
122 s
.service_supported
= true;
124 .then(L
.bind(service_name
.triggerValidation
, service_name
, section_id
))
127 handleGetServiceData: function(service
) {
129 L
.resolveDefault(fs
.read('/usr/share/ddns/custom/'+service
+'.json'), null),
130 L
.resolveDefault(fs
.read('/usr/share/ddns/default/'+service
+'.json'), null)
131 ]).then(function(data
) {
132 return data
[0] || data
[1] || null;
136 handleInstallService: function(m
, service_name
, section_id
, section
, _this
, ev
) {
137 var service
= service_name
.formvalue(section_id
)
138 return fs
.exec('/usr/bin/ddns', ['service', 'install', service
])
139 .then(L
.bind(_this
.callGenServiceList
, _this
))
140 .then(L
.bind(m
.render
, m
))
141 .then(L
.bind(this.renderMoreOptionsModal
, this, section
))
142 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
145 handleRefreshServicesList: function(m
, ev
) {
146 return fs
.exec('/usr/bin/ddns', ['service', 'update'])
147 .then(L
.bind(this.load
, this))
148 .then(L
.bind(this.render
, this))
149 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
152 handleReloadDDnsRule: function(m
, section_id
, ev
) {
153 return fs
.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
154 [ '-S', section_id
, '--', 'start' ])
155 .then(L
.bind(m
.load
, m
))
156 .then(L
.bind(m
.render
, m
))
157 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
160 HandleStopDDnsRule: function(m
, section_id
, ev
) {
161 return fs
.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
162 [ '-S', section_id
, '--', 'start' ])
163 .then(L
.bind(m
.render
, m
))
164 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
167 handleToggleDDns: function(m
, ev
) {
168 return this.callInitAction('ddns', 'enabled')
169 .then(L
.bind(function (action
) { return this.callInitAction('ddns', action
? 'disable' : 'enable')}, this))
170 .then(L
.bind(function (action
) { return this.callInitAction('ddns', action
? 'stop' : 'start')}, this))
171 .then(L
.bind(m
.render
, m
))
172 .catch(function(e
) { ui
.addNotification(null, E('p', e
.message
)) });
175 handleRestartDDns: function(m
, ev
) {
176 return this.callInitAction('ddns', 'restart')
177 .then(L
.bind(m
.render
, m
));
180 poll_status: function(map
, data
) {
181 var status
= data
[1] || [], service
= data
[0] || [], rows
= map
.querySelectorAll('.cbi-section-table-row[data-sid]'),
182 section_id
, cfg_detail_ip
, cfg_update
, cfg_status
, host
, ip
, last_update
,
183 next_update
, service_status
, reload
, cfg_enabled
, stop
,
184 ddns_enabled
= map
.querySelector('[data-name="_enabled"]').querySelector('.cbi-value-field'),
185 ddns_toggle
= map
.querySelector('[data-name="_toggle"]').querySelector('button'),
186 services_list
= map
.querySelector('[data-name="_services_list"]').querySelector('.cbi-value-field');
188 ddns_toggle
.innerHTML
= status
['_enabled'] ? _('Stop DDNS') : _('Start DDNS')
189 services_list
.innerHTML
= status
['_services_list'];
191 dom
.content(ddns_enabled
, function() {
193 E('div', {}, status
['_enabled'] ? _('DDNS Autostart enabled') : [
194 _('DDNS Autostart disabled'),
195 E('div', { 'class' : 'cbi-value-description' },
196 _("Currently DDNS updates are not started at boot or on interface events.") + "<br />" +
197 _("This is the default if you run DDNS scripts by yourself (i.e. via cron with force_interval set to '0')"))
201 for (var i
= 0; i
< rows
.length
; i
++) {
202 section_id
= rows
[i
].getAttribute('data-sid');
203 cfg_detail_ip
= rows
[i
].querySelector('[data-name="_cfg_detail_ip"]');
204 cfg_update
= rows
[i
].querySelector('[data-name="_cfg_update"]');
205 cfg_status
= rows
[i
].querySelector('[data-name="_cfg_status"]');
206 reload
= rows
[i
].querySelector('.cbi-section-actions .reload');
207 stop
= rows
[i
].querySelector('.cbi-section-actions .stop');
208 cfg_enabled
= uci
.get('ddns', section_id
, 'enabled');
210 reload
.disabled
= (status
['_enabled'] == 0 || cfg_enabled
== 0);
212 host
= uci
.get('ddns', section_id
, 'lookup_host') || _('Configuration Error');
214 last_update
= _('Never');
215 next_update
= _('Unknown');
216 service_status
= '<b>' + _('Not Running') + '</b>';
218 if (service
[section_id
]) {
219 stop
.disabled
= (!service
[section_id
].pid
|| (service
[section_id
].pid
&& cfg_enabled
== '1'));
220 if (service
[section_id
].ip
)
221 ip
= service
[section_id
].ip
;
222 if (service
[section_id
].last_update
)
223 last_update
= service
[section_id
].last_update
;
224 if (service
[section_id
].next_update
)
225 next_update
= this.NextUpdateStrings
[service
[section_id
].next_update
] || service
[section_id
].next_update
;
226 if (service
[section_id
].pid
)
227 service_status
= '<b>' + _('Running') + '</b> : ' + service
[section_id
].pid
;
230 cfg_detail_ip
.innerHTML
= host
+ '<br />' + ip
;
231 cfg_update
.innerHTML
= last_update
+ '<br />' + next_update
;
232 cfg_status
.innerHTML
= service_status
;
240 this.callDDnsGetServicesStatus(),
241 this.callDDnsGetStatus(),
242 this.callDDnsGetEnv(),
243 this.callGenServiceList(),
248 render: function(data
) {
249 var resolved
= data
[0] || [];
250 var status
= data
[1] || [];
251 var env
= data
[2] || [];
252 var logdir
= uci
.get('ddns', 'global', 'ddns_logdir') || "/var/log/ddns";
258 m
= new form
.Map('ddns', _('Dynamic DNS'));
260 s
= m
.section(form
.NamedSection
, 'global', 'ddns',);
262 s
.tab('info', _('Information'));
263 s
.tab('global', _('Global Settings'));
265 o
= s
.taboption('info', form
.DummyValue
, '_version', _('Dynamic DNS Version'));
266 o
.cfgvalue = function() {
267 return status
[this.option
];
270 o
= s
.taboption('info', form
.DummyValue
, '_enabled', _('State'));
271 o
.cfgvalue = function() {
272 var res
= status
[this.option
];
274 this.description
= _("Currently DDNS updates are not started at boot or on interface events.") + "<br />" +
275 _("This is the default if you run DDNS scripts by yourself (i.e. via cron with force_interval set to '0')")
277 return res
? _('DDNS Autostart enabled') : _('DDNS Autostart disabled')
280 o
= s
.taboption('info', form
.Button
, '_toggle');
282 o
.inputtitle
= _((status
['_enabled'] ? 'stop' : 'start').toUpperCase() + ' DDns');
283 o
.inputstyle
= 'apply';
284 o
.onclick
= L
.bind(this.handleToggleDDns
, this, m
);
286 o
= s
.taboption('info', form
.Button
, '_restart');
288 o
.inputtitle
= _('Restart DDns');
289 o
.inputstyle
= 'apply';
290 o
.onclick
= L
.bind(this.handleRestartDDns
, this, m
);
292 o
= s
.taboption('info', form
.DummyValue
, '_services_list', _('Services list last update'));
293 o
.cfgvalue = function() {
294 return status
[this.option
];
297 o
= s
.taboption('info', form
.Button
, '_refresh_services');
299 o
.inputtitle
= _('Update DDns Services List');
300 o
.inputstyle
= 'apply';
301 o
.onclick
= L
.bind(this.handleRefreshServicesList
, this, m
);
305 if (!env
['has_ipv6']) {
306 o
= s
.taboption('info', form
.DummyValue
, '_no_ipv6');
308 o
.title
= '<b>' + _("IPv6 not supported") + '</b>';
309 o
.cfgvalue = function() { return _("IPv6 is currently not (fully) supported by this system") + "<br />" +
310 _("Please follow the instructions on OpenWrt's homepage to enable IPv6 support") + "<br />" +
311 _("or update your system to the latest OpenWrt Release")};
314 if (!env
['has_ssl']) {
315 o
= s
.taboption('info', form
.DummyValue
, '_no_https');
316 o
.titleref
= L
.url("admin", "system", "opkg")
318 o
.title
= '<b>' + _("HTTPS not supported") + '</b>';
319 o
.cfgvalue = function() { return _("Neither GNU Wget with SSL nor cURL installed to support secure updates via HTTPS protocol.") +
321 _("You should install 'wget' or 'curl' or 'uclient-fetch' with 'libustream-*ssl' package.") +
323 _("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.")};
326 if (!env
['has_bindnet']) {
327 o
= s
.taboption('info', form
.DummyValue
, '_no_bind_network');
328 o
.titleref
= L
.url("admin", "system", "opkg")
330 o
.title
= '<b>' + _("Binding to a specific network not supported") + '</b>';
331 o
.cfgvalue = function() { return _("Neither GNU Wget with SSL nor cURL installed to select a network to use for communication.") +
333 _("You should install 'wget' or 'curl' package.") +
335 _("GNU Wget will use the IP of given network, cURL will use the physical interface.") +
337 _("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.")};
340 if (!env
['has_proxy']) {
341 o
= s
.taboption('info', form
.DummyValue
, '_no_proxy');
342 o
.titleref
= L
.url("admin", "system", "opkg")
344 o
.title
= '<b>' + _("cURL without Proxy Support") + '</b>';
345 o
.cfgvalue = function() { return _("cURL is installed, but libcurl was compiled without proxy support.") +
347 _("You should install 'wget' or 'uclient-fetch' package or replace libcurl.") +
349 _("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.")};
352 if (!env
['has_forceip']) {
353 o
= s
.taboption('info', form
.DummyValue
, '_no_force_ip');
354 o
.titleref
= L
.url("admin", "system", "opkg")
356 o
.title
= '<b>' + _("Force IP Version not supported") + '</b>';
357 o
.cfgvalue = function() { return _("BusyBox's nslookup and Wget do not support to specify " +
358 "the IP version to use for communication with DDNS Provider!") +
359 "<br />- " + _("You should install 'wget' or 'curl' or 'uclient-fetch' package.")
363 if (!env
['has_bindhost']) {
364 o
= s
.taboption('info', form
.DummyValue
, '_no_dnstcp');
365 o
.titleref
= L
.url("admin", "system", "opkg")
367 o
.title
= '<b>' + _("DNS requests via TCP not supported") + '</b>';
368 o
.cfgvalue = function() { return _("BusyBox's nslookup and hostip do not support to specify to use TCP " +
369 "instead of default UDP when requesting DNS server!") +
371 _("You should install 'bind-host' or 'knot-host' or 'drill' package for DNS requests.")};
374 if (!env
['has_dnsserver']) {
375 o
= s
.taboption('info', form
.DummyValue
, '_no_dnsserver');
376 o
.titleref
= L
.url("admin", "system", "opkg")
378 o
.title
= '<b>' + _("Using specific DNS Server not supported") + '</b>';
379 o
.cfgvalue = function() { return _("BusyBox's nslookup in the current compiled version " +
380 "does not handle given DNS Servers correctly!") +
382 _("You should install 'bind-host' or 'knot-host' or 'drill' or 'hostip' package, " +
383 "if you need to specify a DNS server to detect your registered IP.")};
386 if (env
['has_ssl'] && !env
['has_cacerts']) {
387 o
= s
.taboption('info', form
.DummyValue
, '_no_certs');
388 o
.titleref
= L
.url("admin", "system", "opkg")
390 o
.title
= '<b>' + _("No certificates found") + '</b>';
391 o
.cfgvalue = function() { return _("If using secure communication you should verify server certificates!") +
393 _("Install 'ca-certificates' package or needed certificates " +
394 "by hand into /etc/ssl/certs default directory")};
397 // Advanced Configuration Section
399 o
= s
.taboption('global', form
.Flag
, 'upd_privateip', _("Allow non-public IP's"));
400 o
.description
= _("Non-public and by default blocked IP's") + ':'
401 + '<br /><strong>IPv4: </strong>'
402 + '0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16'
403 + '<br /><strong>IPv6: </strong>'
404 + '::/32, f000::/4"';
408 o
= s
.taboption('global', form
.Value
, 'ddns_dateformat', _('Date format'));
409 o
.description
= '<a href="http://www.cplusplus.com/reference/ctime/strftime/" target="_blank">'
410 + _("For supported codes look here")
412 _('Current setting: ') + '<b>' + status
['_curr_dateformat'] + '</b>';
417 o
= s
.taboption('global', form
.Value
, 'ddns_rundir', _('Status directory'));
418 o
.description
= _('Directory contains PID and other status information for each running section.');
419 o
.default = "/var/run/ddns";
423 o
= s
.taboption('global', form
.Value
, 'ddns_logdir', _('Log directory'));
424 o
.description
= _('Directory contains Log files for each running section.');
425 o
.default = "/var/log/ddns";
428 o
.validate = function(section_id
, formvalue
) {
429 if (formvalue
.indexOf('../') !== -1)
430 return _('"../" not allowed in path for Security Reason.')
435 o
= s
.taboption('global', form
.Value
, 'ddns_loglines', _('Log length'));
436 o
.description
= _('Number of last lines stored in log files');
437 o
.datatype
= 'min(1)';
440 if (env
['has_wget'] && env
['has_curl']) {
442 o
= s
.taboption('global', form
.Flag
, 'use_curl', _('Use cURL'));
443 o
.description
= _('If Wget and cURL package are installed, Wget is used for communication by default.');
450 o
= s
.taboption('global', form
.Value
, 'cacert', _('Ca Certs path'));
451 o
.description
= _('Ca Certs path that will be used to download services data. Set IGNORE to skip certificate validation.');
452 o
.placeholder
= 'IGNORE';
454 o
= s
.taboption('global', form
.Value
, 'services_url', _('Services URL Download'));
455 o
.description
= _('Url used to download services file. By default is the master openwrt ddns package repo.');
456 o
.placeholder
= 'https://raw.githubusercontent.com/openwrt/packages/master/net/ddns-scripts/files';
459 s
= m
.section(form
.GridSection
, 'service', _('Services'));
462 s
.addbtntitle
= _('Add new services...');
468 s
.handleCreateDDnsRule = function(m
, name
, service_name
, ipv6
, ev
) {
469 var section_id
= name
.isValid('_new_') ? name
.formvalue('_new_') : null,
470 service_value
= service_name
.isValid('_new_') ? service_name
.formvalue('_new_') : null,
471 ipv6_value
= ipv6
.isValid('_new_') ? ipv6
.formvalue('_new_') : null;
473 if (section_id
== null || section_id
== '' || service_value
== null || section_id
== '' || ipv6_value
== null || ipv6_value
== '')
476 return m
.save(function() {
477 uci
.add('ddns', 'service', section_id
);
478 uci
.set('ddns', section_id
, 'service_name', service_value
);
479 uci
.set('ddns', section_id
, 'use_ipv6', ipv6_value
);
480 }).then(L
.bind(m
.children
[1].renderMoreOptionsModal
, m
.children
[1], section_id
));
483 s
.handleAdd = function(ev
) {
484 var m2
= new form
.Map('ddns'),
485 s2
= m2
.section(form
.NamedSection
, '_new_'),
486 name
, ipv6
, service_name
;
488 s2
.render = function() {
491 this.renderUCISection('_new_')
492 ]).then(this.renderContents
.bind(this));
495 name
= s2
.option(form
.Value
, 'name', _('Name'));
496 name
.rmempty
= false;
497 name
.datatype
= 'uciname';
498 name
.placeholder
= _('New DDns Service…');
499 name
.validate = function(section_id
, value
) {
500 if (uci
.get('ddns', value
) != null)
501 return _('The service name is already used');
506 ipv6
= s2
.option( form
.ListValue
, 'use_ipv6',
507 _("IP address version"),
508 _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
510 ipv6
.value("0", _("IPv4-Address"))
511 if (env
["has_ipv6"]) {
512 ipv6
.value("1", _("IPv6-Address"))
515 service_name
= s2
.option(form
.ListValue
, 'service_name',
516 String
.format('%s', _("DDNS Service provider")));
517 service_name
.value('-',"-- " + _("custom") + " --");
518 for (var elem
in _this
.services
)
519 service_name
.value(elem
);
520 service_name
.validate = function(section_id
, value
) {
521 if (value
== '') return _("Select a service");
522 if (s2
.service_supported
== null) return _("Checking the service support...");
523 if (!s2
.service_supported
) return _("Service doesn't support this ip type");
527 ipv6
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s2
, service_name
, ipv6
);
528 service_name
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s2
, service_name
, ipv6
);
530 m2
.render().then(L
.bind(function(nodes
) {
531 ui
.showModal(_('Add new services...'), [
533 E('div', { 'class': 'right' }, [
536 'click': ui
.hideModal
537 }, _('Cancel')), ' ',
539 'class': 'cbi-button cbi-button-positive important',
540 'click': ui
.createHandlerFn(this, 'handleCreateDDnsRule', m
, name
, service_name
, ipv6
)
541 }, _('Create service'))
545 nodes
.querySelector('[id="%s"] input[type="text"]'.format(name
.cbid('_new_'))).focus();
549 s
.renderRowActions = function(section_id
) {
550 var tdEl
= this.super('renderRowActions', [ section_id
, _('Edit') ]),
551 cfg_enabled
= uci
.get('ddns', section_id
, 'enabled'),
553 'class': 'cbi-button cbi-button-neutral reload',
554 'click': ui
.createHandlerFn(_this
, 'handleReloadDDnsRule', m
, section_id
),
555 'title': _('Reload this service'),
558 'class': 'cbi-button cbi-button-neutral stop',
559 'click': ui
.createHandlerFn(_this
, 'HandleStopDDnsRule', m
, section_id
),
560 'title': _('Stop this service'),
563 if (status
['_enabled'] == 0 || cfg_enabled
== 0)
564 reload_opt
['disabled'] = 'disabled';
566 if (!resolved
[section_id
] || !resolved
[section_id
].pid
||
567 (resolved
[section_id
].pid
&& cfg_enabled
== '1'))
568 stop_opt
['disabled'] = 'disabled';
570 dom
.content(tdEl
.lastChild
, [
571 E('button', stop_opt
, _('Stop')),
572 E('button', reload_opt
, _('Reload')),
573 tdEl
.lastChild
.childNodes
[0],
574 tdEl
.lastChild
.childNodes
[1],
575 tdEl
.lastChild
.childNodes
[2]
581 s
.modaltitle = function(section_id
) {
582 return _('DDns Service') + ' » ' + section_id
;
585 s
.addModalOptions = function(s
, section_id
) {
587 var service
= uci
.get('ddns', section_id
, 'service_name') || '-',
588 ipv6
= uci
.get('ddns', section_id
, 'use_ipv6'), service_name
, use_ipv6
;
590 return _this
.handleGetServiceData(service
).then(L
.bind(function (service_data
) {
591 s
.service_available
= true;
592 s
.service_supported
= true;
594 if (service
!= '-') {
596 s
.service_available
= false;
598 service_data
= JSON
.parse(service_data
);
599 if (ipv6
== "1" && !service_data
.ipv6
)
600 s
.service_supported
= false;
604 s
.tab('basic', _('Basic Settings'));
605 s
.tab('advanced', _('Advanced Settings'));
606 s
.tab('timer', _('Timer Settings'));
607 s
.tab('logview', _('Log File Viewer'));
609 o
= s
.taboption('basic', form
.Flag
, 'enabled',
611 _("If this service section is disabled it could not be started.")
613 _("Neither from LuCI interface nor from console."));
618 o
= s
.taboption('basic', form
.Value
, 'lookup_host',
619 _("Lookup Hostname"),
620 _("Hostname/FQDN to validate, if IP update happen or necessary"));
622 o
.placeholder
= "myhost.example.com";
623 o
.datatype
= 'and(minlength(3),hostname("strict"))';
626 use_ipv6
= s
.taboption('basic', form
.ListValue
, 'use_ipv6',
627 _("IP address version"),
628 _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
629 use_ipv6
.default = '0';
630 use_ipv6
.modalonly
= true;
631 use_ipv6
.rmempty
= false;
632 use_ipv6
.value("0", _("IPv4-Address"))
633 if (env
["has_ipv6"]) {
634 use_ipv6
.value("1", _("IPv6-Address"))
637 service_name
= s
.taboption('basic', form
.ListValue
, 'service_name',
638 String
.format('%s', _("DDNS Service provider")));
639 service_name
.modalonly
= true;
640 service_name
.value('-',"-- " + _("custom") + " --");
641 for (var elem
in _this
.services
)
642 service_name
.value(elem
);
643 service_name
.cfgvalue = function(section_id
) {
644 return uci
.get('ddns', section_id
, 'service_name') || '-';
646 service_name
.write = function(section_id
, service
) {
647 if (service
!= '-') {
648 uci
.set('ddns', section_id
, 'update_url', null);
649 uci
.set('ddns', section_id
, 'update_script', null);
650 return uci
.set('ddns', section_id
, 'service_name', service
);
652 return uci
.set('ddns', section_id
, 'service_name', null);
654 service_name
.validate = function(section_id
, value
) {
655 if (value
== '') return _("Select a service");
656 if (s
.service_available
== null) return _("Checking the service support...");
657 if (!s
.service_available
) return _('Service not installed');
658 if (!s
.service_supported
) return _("Service doesn't support this ip type");
662 service_name
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s
, service_name
, use_ipv6
);
663 use_ipv6
.onchange
= L
.bind(_this
.handleCheckService
, _this
, s
, service_name
, use_ipv6
);
665 if (!s
.service_available
) {
666 o
= s
.taboption('basic', form
.Button
, '_download_service');
668 o
.title
= _('Service not installed');
669 o
.inputtitle
= _('Install Service');
670 o
.inputstyle
= 'apply';
671 o
.onclick
= L
.bind(_this
.handleInstallService
,
672 this, m
, service_name
, section_id
, s
.section
, _this
)
675 if (!s
.service_supported
) {
676 o
= s
.taboption('basic', form
.DummyValue
, '_not_supported', ' ');
677 o
.cfgvalue = function () {
678 return _("Service doesn't support this ip type")
682 var service_switch
= s
.taboption('basic', form
.Button
, '_switch_proto');
683 service_switch
.modalonly
= true;
684 service_switch
.title
= _('Really switch service?');
685 service_switch
.inputtitle
= _('Switch service');
686 service_switch
.inputstyle
= 'apply';
687 service_switch
.onclick
= L
.bind(function(ev
) {
688 if (!s
.service_supported
) return;
691 .then(L
.bind(m
.load
, m
))
692 .then(L
.bind(m
.render
, m
))
693 .then(L
.bind(this.renderMoreOptionsModal
, this, s
.section
));
696 if (s
.service_available
&& s
.service_supported
) {
698 o
= s
.taboption('basic', form
.Value
, 'update_url',
699 _("Custom update-URL"),
700 _("Update URL to be used for updating your DDNS Provider.")
702 _("Follow instructions you will find on their WEB page."));
706 o
.depends("service_name","-");
707 o
.validate = function(section_id
, value
) {
708 var other
= this.section
.children
.filter(function(o
) { return o
.option
== 'update_script' })[0].formvalue(section_id
);
710 if ((value
== "" && other
== "") || (value
!= "" && other
!= "")) {
711 return _("Insert a Update Script OR a Update URL");
717 o
= s
.taboption('basic', form
.Value
, 'update_script',
718 _("Custom update-script"),
719 _("Custom update script to be used for updating your DDNS Provider."));
723 o
.depends("service_name","-");
724 o
.validate = function(section_id
, value
) {
725 var other
= this.section
.children
.filter(function(o
) { return o
.option
== 'update_url' })[0].formvalue(section_id
);
727 if ((value
== "" && other
== "") || (value
!= "" && other
!= "")) {
728 return _("Insert a Update Script OR a Update URL");
734 o
= s
.taboption('basic', form
.Value
, 'domain',
736 _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
740 o
= s
.taboption('basic', form
.Value
, 'username',
742 _("Replaces [USERNAME] in Update-URL (URL-encoded)"));
746 o
= s
.taboption('basic', form
.Value
, 'password',
748 _("Replaces [PASSWORD] in Update-URL (URL-encoded)"));
753 o
= s
.taboption('basic', form
.Value
, 'param_enc',
754 _("Optional Encoded Parameter"),
755 _("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)"));
759 o
= s
.taboption('basic', form
.Value
, 'param_opt',
760 _("Optional Parameter"),
761 _("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)"));
765 if (env
['has_ssl']) {
766 o
= s
.taboption('basic', form
.Flag
, 'use_https',
767 _("Use HTTP Secure"),
768 _("Enable secure communication with DDNS provider"));
772 o
= s
.taboption('basic', form
.Value
, 'cacert',
773 _("Path to CA-Certificate"),
774 _("directory or path/file")
777 + '<b>' + " IGNORE " + '</b>' +
778 _("to run HTTPS without verification of server certificates (insecure)"));
780 o
.depends("use_https", "1");
781 o
.placeholder
= "/etc/ssl/certs";
786 o
= s
.taboption('advanced', form
.ListValue
, 'ip_source',
787 _("IP address source"),
788 _("Defines the source to read systems IP-Address from, that will be send to the DDNS provider"));
790 o
.default = "network";
791 o
.value("network", _("Network"));
792 o
.value("web", _("URL"));
793 o
.value("interface", _("Interface"));
794 o
.value("script", _("Script"));
795 o
.write = function(section_id
, formvalue
) {
798 uci
.set('ddns', section_id
, "ip_url",null);
799 uci
.set('ddns', section_id
, "ip_interface",null);
800 uci
.set('ddns', section_id
, "ip_script",null);
803 uci
.set('ddns', section_id
, "ip_network",null);
804 uci
.set('ddns', section_id
, "ip_interface",null);
805 uci
.set('ddns', section_id
, "ip_script",null);
808 uci
.set('ddns', section_id
, "ip_network",null);
809 uci
.set('ddns', section_id
, "ip_url",null);
810 uci
.set('ddns', section_id
, "ip_script",null);
813 uci
.set('ddns', section_id
, "ip_network",null);
814 uci
.set('ddns', section_id
, "ip_url",null);
815 uci
.set('ddns', section_id
, "ip_interface",null);
821 return uci
.set('ddns', section_id
, 'ip_source', formvalue
)
824 o
= s
.taboption('advanced', widgets
.NetworkSelect
, 'ip_network',
826 _("Defines the network to read systems IP-Address from"));
827 o
.depends('ip_source','network');
832 o
= s
.taboption('advanced', form
.Value
, 'ip_url',
834 _("Defines the Web page to read systems IP-Address from.")
836 String
.format('%s %s', _('Example for IPv4'), ': http://checkip.dyndns.com')
838 String
.format('%s %s', _('Example for IPv6'), ': http://checkipv6.dyndns.com'));
839 o
.depends("ip_source", "web")
842 o
= s
.taboption('advanced', widgets
.DeviceSelect
, 'ip_interface',
844 _("Defines the interface to read systems IP-Address from"));
846 o
.depends("ip_source", "interface")
850 o
= s
.taboption('advanced', form
.Value
, 'ip_script',
852 _("User defined script to read systems IP-Address"));
854 o
.depends("ip_source", "script")
855 o
.placeholder
= "/path/to/script.sh"
857 o
= s
.taboption('advanced', widgets
.DeviceSelect
, 'interface',
859 _("Network on which the ddns-updater scripts will be started"));
863 o
.depends("ip_source", "web");
864 o
.depends("ip_source", "script");
866 o
= s
.taboption('advanced', form
.DummyValue
, '_interface',
868 _("Network on which the ddns-updater scripts will be started"));
869 o
.depends("ip_source", "interface");
870 o
.depends("ip_source", "network");
873 o
.cfgvalue = function(section_id
) {
874 return uci
.get('ddns', section_id
, 'interface') || _('This will be autoset to the selected interface');
876 o
.write = function(section_id
) {
877 var opt
= this.section
.children
.filter(function(o
) { return o
.option
== 'ip_source' })[0].formvalue(section_id
);
878 var val
= this.section
.children
.filter(function(o
) { return o
.option
== 'ip_'+opt
})[0].formvalue(section_id
);
879 return uci
.set('ddns', section_id
, 'interface', val
);
882 if (env
['has_bindnet']) {
883 o
= s
.taboption('advanced', widgets
.ZoneSelect
, 'bind_network',
885 _('OPTIONAL: Network to use for communication')
887 _("Network on which the ddns-updater scripts will be started"));
888 o
.depends("ip_source", "web");
894 if (env
['has_forceip']) {
895 o
= s
.taboption('advanced', form
.Flag
, 'force_ipversion',
896 _("Force IP Version"),
897 _('OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.'));
903 if (env
['has_dnsserver']) {
904 o
= s
.taboption("advanced", form
.Value
, "dns_server",
906 _("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.")
908 _("Format: IP or FQDN"));
909 o
.placeholder
= "mydns.lan"
915 if (env
['has_bindhost']) {
916 o
= s
.taboption("advanced", form
.Flag
, "force_dnstcp",
917 _("Force TCP on DNS"),
918 _("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests."));
924 if (env
['has_proxy']) {
925 o
= s
.taboption("advanced", form
.Value
, "proxy",
927 _("OPTIONAL: Proxy-Server for detection and updates.")
929 String
.format('%s: <b>%s</b>', _("Format"), "[user:password@]proxyhost:port")
931 String
.format('%s: <b>%s</b>', _("IPv6 address must be given in square brackets"), "[2001:db8::1]:8080"));
937 o
= s
.taboption("advanced", form
.ListValue
, "use_syslog",
939 _("Writes log messages to syslog. Critical Errors will always be written to syslog."));
943 o
.value("0", _("No logging"))
944 o
.value("1", _("Info"))
945 o
.value("2", _("Notice"))
946 o
.value("3", _("Warning"))
947 o
.value("4", _("Error"))
949 o
= s
.taboption("advanced", form
.Flag
, "use_logfile",
954 o
.cfgvalue = function(section_id
) {
955 this.description
= _("Writes detailed messages to log file. File will be truncated automatically.") + "<br />" +
956 _("File") + ': "' + logdir
+ '/' + section_id
+ '.log"';
957 return uci
.get('ddns', section_id
, 'use_logfile');
961 o
= s
.taboption("timer", form
.Value
, "check_interval",
962 _("Check Interval"));
963 o
.placeholder
= "30";
965 o
.datatype
= 'uinteger';
966 o
.validate = function(section_id
, formvalue
) {
967 var unit
= this.section
.children
.filter(function(o
) { return o
.option
== 'check_unit' })[0].formvalue(section_id
),
968 time_to_sec
= _this
.time_res
[unit
|| 'minutes'] * formvalue
;
970 if (formvalue
&& time_to_sec
< 300)
971 return _('Values below 5 minutes == 300 seconds are not supported');
976 o
= s
.taboption("timer", form
.ListValue
, "check_unit",
978 _("Interval unit to check for changed IP"));
980 o
.default = "minutes"
981 o
.value("seconds", _("seconds"));
982 o
.value("minutes", _("minutes"));
983 o
.value("hours", _("hours"));
985 o
= s
.taboption("timer", form
.Value
, "force_interval",
987 _("Interval to force updates send to DDNS Provider")
989 _("Setting this parameter to 0 will force the script to only run once"));
990 o
.placeholder
= "72";
993 o
.datatype
= 'uinteger';
994 o
.validate = function(section_id
, formvalue
) {
999 var check_unit
= this.section
.children
.filter(function(o
) { return o
.option
== 'check_unit' })[0].formvalue(section_id
),
1000 check_val
= this.section
.children
.filter(function(o
) { return o
.option
== 'check_interval' })[0].formvalue(section_id
),
1001 force_unit
= this.section
.children
.filter(function(o
) { return o
.option
== 'force_unit' })[0].formvalue(section_id
),
1002 check_to_sec
= _this
.time_res
[check_unit
|| 'minutes'] * ( check_val
|| '30'),
1003 force_to_sec
= _this
.time_res
[force_unit
|| 'minutes'] * formvalue
;
1005 if (force_to_sec
!= 0 && force_to_sec
< check_to_sec
)
1006 return _("Values lower 'Check Interval' except '0' are not supported");
1011 o
= s
.taboption("timer", form
.ListValue
, "force_unit",
1013 _("Interval unit to force updates send to DDNS Provider"));
1016 o
.default = "minutes"
1017 o
.value("minutes", _("minutes"));
1018 o
.value("hours", _("hours"));
1019 o
.value("days", _("days"));
1021 o
= s
.taboption("timer", form
.Value
, "retry_count",
1022 _("Error Retry Counter"),
1023 _("On Error the script will stop execution after given number of retrys")
1025 _("The default setting of '0' will retry infinite."));
1026 o
.placeholder
= "0";
1029 o
.datatype
= 'uinteger';
1031 o
= s
.taboption("timer", form
.Value
, "retry_interval",
1032 _("Error Retry Interval"),
1033 _("On Error the script will stop execution after given number of retrys")
1035 _("The default setting of '0' will retry infinite."));
1036 o
.placeholder
= "60";
1039 o
.datatype
= 'uinteger';
1041 o
= s
.taboption("timer", form
.ListValue
, "retry_unit",
1043 _("On Error the script will retry the failed action after given time"));
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 ') + logdir
+ ' for this service.'),
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
));