# SPDX-License-Identifier: AGPL-3.0-or-later import io import itertools import pwd import plinth.actions import plinth.utils from django.core.exceptions import ValidationError from django.http import HttpResponseBadRequest from django.shortcuts import redirect from django.utils.html import escape from django.utils.translation import ugettext_lazy as _ from django.views.generic.base import TemplateView, View from plinth.views import AppView, render_tabs from . import aliases from . import audit from . import forms class TabMixin(View): admin_tabs = [ ('', _('Home')), ('my_mail', _('My Mail')), ('my_aliases', _('My Aliases')), ('security', _('Security')), ('domains', _('Domains')) ] user_tabs = [ ('my_mail', _('Home')), ('my_aliases', _('My Aliases')) ] def get_context_data(self, *args, **kwargs): # Retrieve context data from the next method in the MRO context = super().get_context_data(*args, **kwargs) # Populate context with customized data context['tabs'] = self.render_dynamic_tabs() return context def render_dynamic_tabs(self): if plinth.utils.is_user_admin(self.request): return render_tabs(self.request.path, self.admin_tabs) else: return render_tabs(self.request.path, self.user_tabs) def render_validation_error(self, validation_error, status=400): context = self.get_context_data() context['error'] = validation_error return self.render_to_response(context, status=status) def render_exception(self, exception, status=500): context = self.get_context_data() context['error'] = [str(exception)] return self.render_to_response(context, status=status) def catch_exceptions(self, function, request): try: return function(request) except ValidationError as validation_error: return self.render_validation_error(validation_error) except Exception as error: return self.render_exception(error) def find_button(self, post): key_filter = (k for k in post.keys() if k.startswith('btn_')) lst = list(itertools.islice(key_filter, 2)) if len(lst) != 1: raise ValidationError('Bad post data') if not isinstance(lst[0], str): raise ValidationError('Bad post data') return lst[0][len('btn_'):] def find_form(self, post): form_name = post.get('form') for cls in self.form_classes: if cls.__name__ == form_name: return cls(post) raise ValidationError('Form was unspecified') class EmailServerView(TabMixin, AppView): """Server configuration page""" app_id = 'email_server' template_name = 'email_server.html' audit_modules = ('domain', 'tls', 'rcube') def get_context_data(self, *args, **kwargs): dlist = [] for module_name in self.audit_modules: self._get_audit_results(module_name, dlist) dlist.sort(key=audit.models.Diagnosis.sorting_key) context = super().get_context_data(*args, **kwargs) context['related_diagnostics'] = dlist return context def _get_audit_results(self, module_name, dlist): try: results = getattr(audit, module_name).get() except Exception as e: title = _('Internal error in {0}').format('audit.' + module_name) diagnosis = audit.models.Diagnosis(title) diagnosis.critical(str(e)) diagnosis.critical(_('Check syslog for more information')) results = [diagnosis] for diagnosis in results: if diagnosis.action: diagnosis.action = '%s.%s' % (module_name, diagnosis.action) if diagnosis.has_failed: dlist.append(diagnosis) def post(self, request): repair_field = request.POST.get('repair') module_name, sep, action_name = repair_field.partition('.') if not sep or module_name not in self.audit_modules: return HttpResponseBadRequest('Bad post data') self._repair(module_name, action_name) return redirect(request.path) def _repair(self, module_name, action_name): module = getattr(audit, module_name) if not hasattr(module, 'repair_component'): return reload_list = [] try: reload_list = module.repair_component(action_name) except Exception: pass for service in reload_list: # plinth.action_utils.service_reload(service) plinth.actions.superuser_run('service', ['reload', service]) class MyMailView(TabMixin, TemplateView): template_name = 'my_mail.html' def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) nam = self.request.user.username context['has_homedir'] = audit.home.exists_nam(nam) return context def post(self, request): return self.catch_exceptions(self._post, request) def _post(self, request): if 'btn_mkhome' not in request.POST: raise ValidationError('Bad post data') audit.home.put_nam(request.user.username) return self.render_to_response(self.get_context_data()) class AliasView(TabMixin, TemplateView): class Checkboxes: def __init__(self, post=None, initial=None): self.models = initial self.post = post self.cleaned_data = {} # HTML rendering self.sb = io.StringIO() self.counter = 0 def render(self): if self.models is None: raise RuntimeError('Uninitialized form') if self.sb.tell() > 0: raise RuntimeError('render has been called') enabled = [a.email_name for a in self.models if a.enabled] disabled = [a.email_name for a in self.models if not a.enabled] self._render_fieldset(enabled, _('Enabled aliases')) self._render_fieldset(disabled, _('Disabled aliases')) return self.sb.getvalue() def _render_fieldset(self, email_names, legend): if len(email_names) > 0: self.sb.write('
') def _render_boxes(self, email_names): for email_name in email_names: input_id = 'cb_alias_%d' % self._count() value = escape(email_name) self.sb.write('