From 4b21ffb16b85d925249f8b9262c80325a62af75d Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Wed, 13 Feb 2019 04:43:27 +0530 Subject: [PATCH] dynamicdns: Break up dynamicdns.py into forms.py and views.py Signed-off-by: Joseph Nuthalapati Reviewed-by: Sunil Mohan Adapa --- plinth/modules/dynamicdns/__init__.py | 99 +++++- plinth/modules/dynamicdns/dynamicdns.py | 402 ------------------------ plinth/modules/dynamicdns/forms.py | 166 ++++++++++ plinth/modules/dynamicdns/urls.py | 4 +- plinth/modules/dynamicdns/views.py | 177 +++++++++++ 5 files changed, 438 insertions(+), 410 deletions(-) delete mode 100644 plinth/modules/dynamicdns/dynamicdns.py create mode 100644 plinth/modules/dynamicdns/forms.py create mode 100644 plinth/modules/dynamicdns/views.py diff --git a/plinth/modules/dynamicdns/__init__.py b/plinth/modules/dynamicdns/__init__.py index 7a41f5c2d..c22e5ddae 100644 --- a/plinth/modules/dynamicdns/__init__.py +++ b/plinth/modules/dynamicdns/__init__.py @@ -20,12 +20,13 @@ FreedomBox app to configure ez-ipupdate client. from django.utils.translation import ugettext_lazy as _ -from plinth import cfg +from plinth import actions, cfg from plinth.menu import main_menu +from plinth.modules import firewall +from plinth.modules.names import SERVICES from plinth.signals import domain_added from plinth.utils import format_lazy -from . import dynamicdns from .manifest import backup version = 1 @@ -59,10 +60,9 @@ def init(): """Initialize the module.""" menu = main_menu.get('system') menu.add_urlname(name, 'fa-refresh', 'dynamicdns:index') - current_status = dynamicdns.get_status() + current_status = get_status() if current_status['enabled']: - services = dynamicdns.get_enabled_services( - current_status['dynamicdns_domain']) + services = get_enabled_services(current_status['dynamicdns_domain']) domain_added.send_robust( sender='dynamicdns', domain_type='dynamicdnsservice', name=current_status['dynamicdns_domain'], @@ -72,3 +72,92 @@ def init(): def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) + + +def get_enabled_services(domain_name): + """ Get enabled services for the domain name""" + if domain_name != None and domain_name != '': + try: + domainname_services = firewall.get_enabled_services( + zone='external') + except actions.ActionError: + domainname_services = [service[0] for service in SERVICES] + else: + domainname_services = None + return domainname_services + + +def get_status(): + """Return the current status.""" + # TODO: use key/value instead of hard coded value list + status = {} + output = actions.superuser_run('dynamicdns', ['status']) + details = output.split() + status['enabled'] = (output.split()[0] == 'enabled') + + if len(details) > 1: + if details[1] == 'disabled': + status['dynamicdns_server'] = '' + else: + status['dynamicdns_server'] = details[1].replace("'", "") + else: + status['dynamicdns_server'] = '' + + if len(details) > 2: + if details[2] == 'disabled': + status['dynamicdns_domain'] = '' + else: + status['dynamicdns_domain'] = details[2].replace("'", "") + else: + status['dynamicdns_domain'] = '' + + if len(details) > 3: + if details[3] == 'disabled': + status['dynamicdns_user'] = '' + else: + status['dynamicdns_user'] = details[3].replace("'", "") + else: + status['dynamicdns_user'] = '' + + if len(details) > 4: + if details[4] == 'disabled': + status['dynamicdns_secret'] = '' + else: + status['dynamicdns_secret'] = details[4].replace("'", "") + else: + status['dynamicdns_secret'] = '' + + if len(details) > 5: + if details[5] == 'disabled': + status['dynamicdns_ipurl'] = '' + else: + status['dynamicdns_ipurl'] = details[5].replace("'", "") + else: + status['dynamicdns_ipurl'] = '' + + if len(details) > 6: + if details[6] == 'disabled': + status['dynamicdns_update_url'] = '' + else: + status['dynamicdns_update_url'] = details[6].replace("'", "") + else: + status['dynamicdns_update_url'] = '' + + if len(details) > 7: + status['disable_SSL_cert_check'] = (output.split()[7] == 'enabled') + else: + status['disable_SSL_cert_check'] = False + + if len(details) > 8: + status['use_http_basic_auth'] = (output.split()[8] == 'enabled') + else: + status['use_http_basic_auth'] = False + + if not status['dynamicdns_server'] and not status['dynamicdns_update_url']: + status['service_type'] = 'GnuDIP' + elif not status['dynamicdns_server'] and status['dynamicdns_update_url']: + status['service_type'] = 'other' + else: + status['service_type'] = 'GnuDIP' + + return status diff --git a/plinth/modules/dynamicdns/dynamicdns.py b/plinth/modules/dynamicdns/dynamicdns.py deleted file mode 100644 index 131fa041b..000000000 --- a/plinth/modules/dynamicdns/dynamicdns.py +++ /dev/null @@ -1,402 +0,0 @@ -# -# This file is part of FreedomBox. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -""" -Forms and views for the dynamicsdns module. -""" - -import logging - -from django import forms -from django.contrib import messages -from django.core import validators -from django.template.response import TemplateResponse -from django.urls import reverse_lazy -from django.utils.translation import ugettext as _ -from django.utils.translation import ugettext_lazy - -from plinth import actions, cfg -from plinth.modules import dynamicdns, firewall -from plinth.modules.names import SERVICES -from plinth.signals import domain_added, domain_removed -from plinth.utils import format_lazy - -logger = logging.getLogger(__name__) -EMPTYSTRING = 'none' - -subsubmenu = [{ - 'url': reverse_lazy('dynamicdns:index'), - 'text': ugettext_lazy('About') -}, { - 'url': reverse_lazy('dynamicdns:configure'), - 'text': ugettext_lazy('Configure') -}, { - 'url': reverse_lazy('dynamicdns:statuspage'), - 'text': ugettext_lazy('Status') -}] - - -def index(request): - """Serve Dynamic DNS page.""" - return TemplateResponse( - request, 'dynamicdns.html', { - 'title': dynamicdns.name, - 'description': dynamicdns.description, - 'manual_page': dynamicdns.manual_page, - 'subsubmenu': subsubmenu - }) - - -class TrimmedCharField(forms.CharField): - """Trim the contents of a CharField.""" - - def clean(self, value): - """Clean and validate the field value""" - if value: - value = value.strip() - - return super(TrimmedCharField, self).clean(value) - - -class ConfigureForm(forms.Form): - """Form to configure the Dynamic DNS client.""" - help_update_url = \ - ugettext_lazy('The Variables <User>, <Pass>, <Ip>, ' - '<Domain> may be used within the URL. For details ' - 'see the update URL templates of the example providers.') - help_services = \ - ugettext_lazy('Please choose an update protocol according to your ' - 'provider. If your provider does not support the GnuDIP ' - 'protocol or your provider is not listed you may use the ' - 'update URL of your provider.') - help_server = \ - ugettext_lazy('Please do not enter a URL here (like ' - '"https://example.com/") but only the hostname of the ' - 'GnuDIP server (like "example.com").') - help_domain = format_lazy( - ugettext_lazy('The public domain name you want to use to reach your ' - '{box_name}.'), box_name=ugettext_lazy(cfg.box_name)) - help_disable_ssl = \ - ugettext_lazy('Use this option if your provider uses self signed ' - 'certificates.') - help_http_auth = \ - ugettext_lazy('If this option is selected, your username and password ' - 'will be used for HTTP basic authentication.') - help_secret = \ - ugettext_lazy('Leave this field empty if you want to keep your ' - 'current password.') - help_ip_url = format_lazy( - ugettext_lazy('Optional Value. If your {box_name} is not connected ' - 'directly to the Internet (i.e. connected to a NAT ' - 'router) this URL is used to determine the real ' - 'IP address. The URL should simply return the IP where ' - 'the client comes from (example: ' - 'http://myip.datasystems24.de).'), - box_name=ugettext_lazy(cfg.box_name)) - help_user = \ - ugettext_lazy('The username that was used when the account was ' - 'created.') - """ToDo: sync this list with the html template file""" - provider_choices = (('GnuDIP', 'GnuDIP'), ('noip', 'noip.com'), - ('selfhost', 'selfhost.bz'), ('freedns', - 'freedns.afraid.org'), - ('other', 'other update URL')) - - enabled = forms.BooleanField( - label=ugettext_lazy('Enable Dynamic DNS'), required=False) - - service_type = forms.ChoiceField( - label=ugettext_lazy('Service Type'), help_text=help_services, - choices=provider_choices) - - dynamicdns_server = TrimmedCharField( - label=ugettext_lazy('GnuDIP Server Address'), required=False, - help_text=help_server, validators=[ - validators.RegexValidator(r'^[\w-]{1,63}(\.[\w-]{1,63})*$', - ugettext_lazy('Invalid server name')) - ]) - - dynamicdns_update_url = TrimmedCharField( - label=ugettext_lazy('Update URL'), required=False, - help_text=help_update_url) - - disable_SSL_cert_check = forms.BooleanField( - label=ugettext_lazy('Accept all SSL certificates'), - help_text=help_disable_ssl, required=False) - - use_http_basic_auth = forms.BooleanField( - label=ugettext_lazy('Use HTTP basic authentication'), - help_text=help_http_auth, required=False) - - dynamicdns_domain = TrimmedCharField( - label=ugettext_lazy('Domain Name'), help_text=help_domain, - required=False, validators=[ - validators.RegexValidator(r'^[\w-]{1,63}(\.[\w-]{1,63})*$', - ugettext_lazy('Invalid domain name')) - ]) - - dynamicdns_user = TrimmedCharField( - label=ugettext_lazy('Username'), required=False, help_text=help_user) - - dynamicdns_secret = TrimmedCharField( - label=ugettext_lazy('Password'), widget=forms.PasswordInput(), - required=False, help_text=help_secret) - - showpw = forms.BooleanField( - label=ugettext_lazy('Show password'), required=False) - - dynamicdns_ipurl = TrimmedCharField( - label=ugettext_lazy('URL to look up public IP'), required=False, - help_text=help_ip_url, - validators=[validators.URLValidator(schemes=['http', 'https', 'ftp'])]) - - def clean(self): - cleaned_data = super(ConfigureForm, self).clean() - dynamicdns_secret = cleaned_data.get('dynamicdns_secret') - dynamicdns_update_url = cleaned_data.get('dynamicdns_update_url') - dynamicdns_user = cleaned_data.get('dynamicdns_user') - dynamicdns_domain = cleaned_data.get('dynamicdns_domain') - dynamicdns_server = cleaned_data.get('dynamicdns_server') - service_type = cleaned_data.get('service_type') - old_dynamicdns_secret = self.initial['dynamicdns_secret'] - - # Clear the fields which are not in use - if service_type == 'GnuDIP': - dynamicdns_update_url = '' - else: - dynamicdns_server = '' - - if cleaned_data.get('enabled'): - # Check if gnudip server or update URL is filled - if not dynamicdns_update_url and not dynamicdns_server: - raise forms.ValidationError( - _('Please provide an update URL or a GnuDIP server ' - 'address')) - - if dynamicdns_server and not dynamicdns_user: - raise forms.ValidationError( - _('Please provide a GnuDIP username')) - - if dynamicdns_server and not dynamicdns_domain: - raise forms.ValidationError( - _('Please provide a GnuDIP domain name')) - - # Check if a password was set before or a password is set now - if dynamicdns_server and \ - not dynamicdns_secret and not old_dynamicdns_secret: - raise forms.ValidationError(_('Please provide a password')) - - -def configure(request): - """Serve the configuration form.""" - status = get_status() - form = None - - if request.method == 'POST': - form = ConfigureForm(request.POST, initial=status) - if form.is_valid(): - _apply_changes(request, status, form.cleaned_data) - status = get_status() - form = ConfigureForm(initial=status) - else: - form = ConfigureForm(initial=status) - - return TemplateResponse( - request, 'dynamicdns_configure.html', { - 'title': _('Configure Dynamic DNS'), - 'form': form, - 'subsubmenu': subsubmenu - }) - - -def statuspage(request): - """Serve the status page.""" - check_nat = _run(['get-nat']) - last_update = _run(['get-last-success']) - - no_nat = check_nat.strip() == 'no' - nat_unchecked = check_nat.strip() == 'unknown' - timer = _run(['get-timer']) - - if no_nat: - logger.info('Not behind a NAT') - - if nat_unchecked: - logger.info('Did not check if behind a NAT') - - return TemplateResponse( - request, 'dynamicdns_status.html', { - 'title': _('Dynamic DNS Status'), - 'no_nat': no_nat, - 'nat_unchecked': nat_unchecked, - 'timer': timer, - 'last_update': last_update, - 'subsubmenu': subsubmenu - }) - - -def get_status(): - """Return the current status.""" - # TODO: use key/value instead of hard coded value list - status = {} - output = _run(['status']) - details = output.split() - status['enabled'] = (output.split()[0] == 'enabled') - - if len(details) > 1: - if details[1] == 'disabled': - status['dynamicdns_server'] = '' - else: - status['dynamicdns_server'] = details[1].replace("'", "") - else: - status['dynamicdns_server'] = '' - - if len(details) > 2: - if details[2] == 'disabled': - status['dynamicdns_domain'] = '' - else: - status['dynamicdns_domain'] = details[2].replace("'", "") - else: - status['dynamicdns_domain'] = '' - - if len(details) > 3: - if details[3] == 'disabled': - status['dynamicdns_user'] = '' - else: - status['dynamicdns_user'] = details[3].replace("'", "") - else: - status['dynamicdns_user'] = '' - - if len(details) > 4: - if details[4] == 'disabled': - status['dynamicdns_secret'] = '' - else: - status['dynamicdns_secret'] = details[4].replace("'", "") - else: - status['dynamicdns_secret'] = '' - - if len(details) > 5: - if details[5] == 'disabled': - status['dynamicdns_ipurl'] = '' - else: - status['dynamicdns_ipurl'] = details[5].replace("'", "") - else: - status['dynamicdns_ipurl'] = '' - - if len(details) > 6: - if details[6] == 'disabled': - status['dynamicdns_update_url'] = '' - else: - status['dynamicdns_update_url'] = details[6].replace("'", "") - else: - status['dynamicdns_update_url'] = '' - - if len(details) > 7: - status['disable_SSL_cert_check'] = (output.split()[7] == 'enabled') - else: - status['disable_SSL_cert_check'] = False - - if len(details) > 8: - status['use_http_basic_auth'] = (output.split()[8] == 'enabled') - else: - status['use_http_basic_auth'] = False - - if not status['dynamicdns_server'] and not status['dynamicdns_update_url']: - status['service_type'] = 'GnuDIP' - elif not status['dynamicdns_server'] and status['dynamicdns_update_url']: - status['service_type'] = 'other' - else: - status['service_type'] = 'GnuDIP' - - return status - - -def _apply_changes(request, old_status, new_status): - """Apply the changes to Dynamic DNS client.""" - logger.info('New status is - %s', new_status) - logger.info('Old status was - %s', old_status) - - if new_status['dynamicdns_secret'] == '': - new_status['dynamicdns_secret'] = old_status['dynamicdns_secret'] - - if new_status['dynamicdns_ipurl'] == '': - new_status['dynamicdns_ipurl'] = EMPTYSTRING - - if new_status['dynamicdns_update_url'] == '': - new_status['dynamicdns_update_url'] = EMPTYSTRING - - if new_status['dynamicdns_server'] == '': - new_status['dynamicdns_server'] = EMPTYSTRING - - if new_status['service_type'] == 'GnuDIP': - new_status['dynamicdns_update_url'] = EMPTYSTRING - else: - new_status['dynamicdns_server'] = EMPTYSTRING - - if old_status != new_status: - disable_ssl_check = "disabled" - use_http_basic_auth = "disabled" - - if new_status['disable_SSL_cert_check']: - disable_ssl_check = "enabled" - - if new_status['use_http_basic_auth']: - use_http_basic_auth = "enabled" - - _run([ - 'configure', '-s', new_status['dynamicdns_server'], '-d', - new_status['dynamicdns_domain'], '-u', - new_status['dynamicdns_user'], '-p', '-I', - new_status['dynamicdns_ipurl'], '-U', - new_status['dynamicdns_update_url'], '-c', disable_ssl_check, '-b', - use_http_basic_auth - ], input=new_status['dynamicdns_secret'].encode()) - - if old_status['enabled']: - domain_removed.send_robust(sender='dynamicdns', - domain_type='dynamicdnsservice', - name=old_status['dynamicdns_domain']) - _run(['stop']) - - if new_status['enabled']: - services = get_enabled_services(new_status['dynamicdns_domain']) - domain_added.send_robust( - sender='dynamicdns', domain_type='dynamicdnsservice', - name=new_status['dynamicdns_domain'], - description=_('Dynamic DNS Service'), services=services) - _run(['start']) - - messages.success(request, _('Configuration updated')) - else: - logger.info('Nothing changed') - - -def _run(arguments, input=None): - """Run a given command and raise exception if there was an error.""" - return actions.superuser_run('dynamicdns', arguments, input=input) - - -def get_enabled_services(domain_name): - """ Get enabled services for the domain name""" - if domain_name != None and domain_name != '': - try: - domainname_services = firewall.get_enabled_services( - zone='external') - except actions.ActionError: - domainname_services = [service[0] for service in SERVICES] - else: - domainname_services = None - return domainname_services diff --git a/plinth/modules/dynamicdns/forms.py b/plinth/modules/dynamicdns/forms.py new file mode 100644 index 000000000..d22197d7b --- /dev/null +++ b/plinth/modules/dynamicdns/forms.py @@ -0,0 +1,166 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Forms for the dynamicsdns module. +""" + +from django import forms +from django.core import validators +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy +from plinth import cfg +from plinth.utils import format_lazy + + +class TrimmedCharField(forms.CharField): + """Trim the contents of a CharField.""" + + def clean(self, value): + """Clean and validate the field value""" + if value: + value = value.strip() + + return super(TrimmedCharField, self).clean(value) + + +class ConfigureForm(forms.Form): + """Form to configure the Dynamic DNS client.""" + help_update_url = \ + ugettext_lazy('The Variables <User>, <Pass>, <Ip>, ' + '<Domain> may be used within the URL. For details ' + 'see the update URL templates of the example providers.') + help_services = \ + ugettext_lazy('Please choose an update protocol according to your ' + 'provider. If your provider does not support the GnuDIP ' + 'protocol or your provider is not listed you may use ' + 'the update URL of your provider.') + help_server = \ + ugettext_lazy('Please do not enter a URL here (like ' + '"https://example.com/") but only the hostname of the ' + 'GnuDIP server (like "example.com").') + help_domain = format_lazy( + ugettext_lazy('The public domain name you want to use to reach your ' + '{box_name}.'), box_name=ugettext_lazy(cfg.box_name)) + help_disable_ssl = \ + ugettext_lazy('Use this option if your provider uses self signed ' + 'certificates.') + help_http_auth = \ + ugettext_lazy('If this option is selected, your username and password ' + 'will be used for HTTP basic authentication.') + help_secret = \ + ugettext_lazy('Leave this field empty if you want to keep your ' + 'current password.') + help_ip_url = format_lazy( + ugettext_lazy('Optional Value. If your {box_name} is not connected ' + 'directly to the Internet (i.e. connected to a NAT ' + 'router) this URL is used to determine the real ' + 'IP address. The URL should simply return the IP where ' + 'the client comes from (example: ' + 'http://myip.datasystems24.de).'), + box_name=ugettext_lazy(cfg.box_name)) + help_user = \ + ugettext_lazy('The username that was used when the account was ' + 'created.') + """ToDo: sync this list with the html template file""" + provider_choices = (('GnuDIP', 'GnuDIP'), ('noip', 'noip.com'), + ('selfhost', 'selfhost.bz'), ('freedns', + 'freedns.afraid.org'), + ('other', 'other update URL')) + + enabled = forms.BooleanField( + label=ugettext_lazy('Enable Dynamic DNS'), required=False) + + service_type = forms.ChoiceField( + label=ugettext_lazy('Service Type'), help_text=help_services, + choices=provider_choices) + + dynamicdns_server = TrimmedCharField( + label=ugettext_lazy('GnuDIP Server Address'), required=False, + help_text=help_server, validators=[ + validators.RegexValidator(r'^[\w-]{1,63}(\.[\w-]{1,63})*$', + ugettext_lazy('Invalid server name')) + ]) + + dynamicdns_update_url = TrimmedCharField( + label=ugettext_lazy('Update URL'), required=False, + help_text=help_update_url) + + disable_SSL_cert_check = forms.BooleanField( + label=ugettext_lazy('Accept all SSL certificates'), + help_text=help_disable_ssl, required=False) + + use_http_basic_auth = forms.BooleanField( + label=ugettext_lazy('Use HTTP basic authentication'), + help_text=help_http_auth, required=False) + + dynamicdns_domain = TrimmedCharField( + label=ugettext_lazy('Domain Name'), help_text=help_domain, + required=False, validators=[ + validators.RegexValidator(r'^[\w-]{1,63}(\.[\w-]{1,63})*$', + ugettext_lazy('Invalid domain name')) + ]) + + dynamicdns_user = TrimmedCharField( + label=ugettext_lazy('Username'), required=False, help_text=help_user) + + dynamicdns_secret = TrimmedCharField( + label=ugettext_lazy('Password'), widget=forms.PasswordInput(), + required=False, help_text=help_secret) + + showpw = forms.BooleanField( + label=ugettext_lazy('Show password'), required=False) + + dynamicdns_ipurl = TrimmedCharField( + label=ugettext_lazy('URL to look up public IP'), required=False, + help_text=help_ip_url, + validators=[validators.URLValidator(schemes=['http', 'https', 'ftp'])]) + + def clean(self): + cleaned_data = super(ConfigureForm, self).clean() + dynamicdns_secret = cleaned_data.get('dynamicdns_secret') + dynamicdns_update_url = cleaned_data.get('dynamicdns_update_url') + dynamicdns_user = cleaned_data.get('dynamicdns_user') + dynamicdns_domain = cleaned_data.get('dynamicdns_domain') + dynamicdns_server = cleaned_data.get('dynamicdns_server') + service_type = cleaned_data.get('service_type') + old_dynamicdns_secret = self.initial['dynamicdns_secret'] + + # Clear the fields which are not in use + if service_type == 'GnuDIP': + dynamicdns_update_url = '' + else: + dynamicdns_server = '' + + if cleaned_data.get('enabled'): + # Check if gnudip server or update URL is filled + if not dynamicdns_update_url and not dynamicdns_server: + raise forms.ValidationError( + _('Please provide an update URL or a GnuDIP server ' + 'address')) + + if dynamicdns_server and not dynamicdns_user: + raise forms.ValidationError( + _('Please provide a GnuDIP username')) + + if dynamicdns_server and not dynamicdns_domain: + raise forms.ValidationError( + _('Please provide a GnuDIP domain name')) + + # Check if a password was set before or a password is set now + if dynamicdns_server and \ + not dynamicdns_secret and not old_dynamicdns_secret: + raise forms.ValidationError(_('Please provide a password')) diff --git a/plinth/modules/dynamicdns/urls.py b/plinth/modules/dynamicdns/urls.py index 1e43acbb7..ccd209e45 100644 --- a/plinth/modules/dynamicdns/urls.py +++ b/plinth/modules/dynamicdns/urls.py @@ -14,15 +14,13 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the dynamicdns module """ from django.conf.urls import url -from . import dynamicdns as views - +from . import views urlpatterns = [ url(r'^sys/dynamicdns/$', views.index, name='index'), diff --git a/plinth/modules/dynamicdns/views.py b/plinth/modules/dynamicdns/views.py new file mode 100644 index 000000000..737650549 --- /dev/null +++ b/plinth/modules/dynamicdns/views.py @@ -0,0 +1,177 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Views for the dynamicsdns module. +""" + +import logging + +from django.contrib import messages +from django.template.response import TemplateResponse +from django.urls import reverse_lazy +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy + +from plinth import actions +from plinth.modules import dynamicdns +from plinth.signals import domain_added, domain_removed + +from .forms import ConfigureForm + +logger = logging.getLogger(__name__) + +EMPTYSTRING = 'none' + +subsubmenu = [{ + 'url': reverse_lazy('dynamicdns:index'), + 'text': ugettext_lazy('About') +}, { + 'url': reverse_lazy('dynamicdns:configure'), + 'text': ugettext_lazy('Configure') +}, { + 'url': reverse_lazy('dynamicdns:statuspage'), + 'text': ugettext_lazy('Status') +}] + + +def index(request): + """Serve Dynamic DNS page.""" + return TemplateResponse( + request, 'dynamicdns.html', { + 'title': dynamicdns.name, + 'description': dynamicdns.description, + 'manual_page': dynamicdns.manual_page, + 'subsubmenu': subsubmenu + }) + + +def configure(request): + """Serve the configuration form.""" + status = dynamicdns.get_status() + form = None + + if request.method == 'POST': + form = ConfigureForm(request.POST, initial=status) + if form.is_valid(): + _apply_changes(request, status, form.cleaned_data) + status = dynamicdns.get_status() + form = ConfigureForm(initial=status) + else: + form = ConfigureForm(initial=status) + + return TemplateResponse( + request, 'dynamicdns_configure.html', { + 'title': _('Configure Dynamic DNS'), + 'description': dynamicdns.description, + 'manual_page': dynamicdns.manual_page, + 'form': form, + 'subsubmenu': subsubmenu + }) + + +def statuspage(request): + """Serve the status page.""" + check_nat = _run(['get-nat']) + last_update = _run(['get-last-success']) + + no_nat = check_nat.strip() == 'no' + nat_unchecked = check_nat.strip() == 'unknown' + timer = _run(['get-timer']) + + if no_nat: + logger.info('Not behind a NAT') + + if nat_unchecked: + logger.info('Did not check if behind a NAT') + + return TemplateResponse( + request, 'dynamicdns_status.html', { + 'title': _('Dynamic DNS Status'), + 'description': dynamicdns.description, + 'manual_page': dynamicdns.manual_page, + 'no_nat': no_nat, + 'nat_unchecked': nat_unchecked, + 'timer': timer, + 'last_update': last_update, + 'subsubmenu': subsubmenu + }) + + +def _apply_changes(request, old_status, new_status): + """Apply the changes to Dynamic DNS client.""" + logger.info('New status is - %s', new_status) + logger.info('Old status was - %s', old_status) + + if new_status['dynamicdns_secret'] == '': + new_status['dynamicdns_secret'] = old_status['dynamicdns_secret'] + + if new_status['dynamicdns_ipurl'] == '': + new_status['dynamicdns_ipurl'] = EMPTYSTRING + + if new_status['dynamicdns_update_url'] == '': + new_status['dynamicdns_update_url'] = EMPTYSTRING + + if new_status['dynamicdns_server'] == '': + new_status['dynamicdns_server'] = EMPTYSTRING + + if new_status['service_type'] == 'GnuDIP': + new_status['dynamicdns_update_url'] = EMPTYSTRING + else: + new_status['dynamicdns_server'] = EMPTYSTRING + + if old_status != new_status: + disable_ssl_check = "disabled" + use_http_basic_auth = "disabled" + + if new_status['disable_SSL_cert_check']: + disable_ssl_check = "enabled" + + if new_status['use_http_basic_auth']: + use_http_basic_auth = "enabled" + + _run([ + 'configure', '-s', new_status['dynamicdns_server'], '-d', + new_status['dynamicdns_domain'], '-u', + new_status['dynamicdns_user'], '-p', '-I', + new_status['dynamicdns_ipurl'], '-U', + new_status['dynamicdns_update_url'], '-c', disable_ssl_check, '-b', + use_http_basic_auth + ], input=new_status['dynamicdns_secret'].encode()) + + if old_status['enabled']: + domain_removed.send_robust(sender='dynamicdns', + domain_type='dynamicdnsservice', + name=old_status['dynamicdns_domain']) + _run(['stop']) + + if new_status['enabled']: + services = dynamicdns.get_enabled_services( + new_status['dynamicdns_domain']) + domain_added.send_robust( + sender='dynamicdns', domain_type='dynamicdnsservice', + name=new_status['dynamicdns_domain'], + description=_('Dynamic DNS Service'), services=services) + _run(['start']) + + messages.success(request, _('Configuration updated')) + else: + logger.info('Nothing changed') + + +def _run(arguments, input=None): + """Run a given command and raise exception if there was an error.""" + return actions.superuser_run('dynamicdns', arguments, input=input)