From d684dac7d7b98b8d418e6abf555d09712fa344d3 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Tue, 16 Sep 2025 17:45:50 +0200 Subject: [PATCH] Use plistlib to generate mobileconfig --- .../templates/autoconfig/autoconfig.xml | 4 +- .../templates/autoconfig/mobileconfig.xml | 37 ---------- modoboa/autoconfig/tests.py | 2 +- modoboa/autoconfig/views.py | 67 +++++++++++++------ 4 files changed, 49 insertions(+), 61 deletions(-) delete mode 100644 modoboa/autoconfig/templates/autoconfig/mobileconfig.xml diff --git a/modoboa/autoconfig/templates/autoconfig/autoconfig.xml b/modoboa/autoconfig/templates/autoconfig/autoconfig.xml index e8af4ac61..2477d2d38 100644 --- a/modoboa/autoconfig/templates/autoconfig/autoconfig.xml +++ b/modoboa/autoconfig/templates/autoconfig/autoconfig.xml @@ -9,7 +9,7 @@ {{ connection_settings.imap.HOSTNAME }} {{ connection_settings.imap.PORT }} {{ connection_settings.imap.SOCKET_TYPE }} - password-cleartext + plain {{ emailaddress }} @@ -17,7 +17,7 @@ {{ connection_settings.smtp.HOSTNAME }} {{ connection_settings.smtp.PORT }} {{ connection_settings.smtp.SOCKET_TYPE }} - password-cleartext + plain {{ emailaddress }} diff --git a/modoboa/autoconfig/templates/autoconfig/mobileconfig.xml b/modoboa/autoconfig/templates/autoconfig/mobileconfig.xml deleted file mode 100644 index 832efe86d..000000000 --- a/modoboa/autoconfig/templates/autoconfig/mobileconfig.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - com.apple.mail.managed - 1 - {{ reverse_domain }}.email - {{ content_uuid }} - Mail - - Modoboa - EmailTypeIMAP - {{ emailaddress }} - {{ connection_settings.imap.HOSTNAME }} - {{ connection_settings.imap.PORT }} - true - {{ emailaddress }} - - {{ connection_settings.smtp.HOSTNAME }} - {{ connection_settings.smtp.PORT }} - true - {{ emailaddress }} - true - - - - Modoboa - Mail Configuration - {{ reverse_domain }}.mailconfig - {{ global_uuid }} - 1 - Configuration - - diff --git a/modoboa/autoconfig/tests.py b/modoboa/autoconfig/tests.py index bec5b0c69..6dbe76419 100644 --- a/modoboa/autoconfig/tests.py +++ b/modoboa/autoconfig/tests.py @@ -32,7 +32,7 @@ class ViewsTestCase(TestCase): resp = self.client.get(url) self.assertEqual(resp.status_code, 404) - resp = self.client.get(f"{url}?email=test@test.com") + resp = self.client.get(f"{url}?emailaddress=test@test.com") self.assertEqual(resp.status_code, 200) self.assertIn(b"test@test.com", resp.content) diff --git a/modoboa/autoconfig/views.py b/modoboa/autoconfig/views.py index 112a94d57..2bb99e958 100644 --- a/modoboa/autoconfig/views.py +++ b/modoboa/autoconfig/views.py @@ -1,8 +1,8 @@ +import plistlib import uuid from django.conf import settings from django.http import Http404, HttpResponse -from django.template.loader import render_to_string from django.utils.decorators import method_decorator from django.views import generic from django.views.decorators.csrf import csrf_exempt @@ -56,31 +56,56 @@ class AutoDiscoverView(ConfigBaseMixin, generic.TemplateView): return self.render_to_response(context) -class MobileConfigView(ConfigBaseMixin, generic.TemplateView): +class MobileConfigView(generic.View): - content_type = "application/x-apple-aspen-config" + databases = "__all__" http_method_names = ["get"] - template_name = "autoconfig/mobileconfig.xml" - def get_context_data(self, **kwargs): - emailaddress = self.request.GET.get("email") + def get(self, request, *args, **kwargs): + emailaddress = self.request.GET.get("emailaddress") if not emailaddress: raise Http404 - context = super().get_context_data(**kwargs) - context.update(self.get_common_context(emailaddress)) + local_part, domain = split_address(emailaddress) + context = self.get_common_context(emailaddress) parts = context["domain"].split(".") parts.reverse() - context.update( - { - "reverse_domain": ".".join(parts), - "content_uuid": uuid.uuid4(), - "global_uuid": uuid.uuid4(), - } + reverse_domain = ".".join(parts) + imap_settings = settings.EMAIL_CLIENT_CONNECTION_SETTINGS["imap"] + smtp_settings = settings.EMAIL_CLIENT_CONNECTION_SETTINGS["smtp"] + profile = { + "PayloadType": "Configuration", + "PayloadVersion": 1, + "PayloadIdentifier": f"{reverse_domain}.mailprofile", + "PayloadUUID": uuid.uuid4(), + "PayloadDisplayName": f"{domain} Mail Configuration", + "PayloadOrganization": domain, + "PayloadContent": [ + { + "PayloadType": "com.apple.mail.managed", + "PayloadVersion": 1, + "PayloadIdentifier": f"{reverse_domain}.mailprofile.mail", + "PayloadUUID": uuid.uuid4(), + "PayloadDisplayName": "Mail", + "EmailAccountDescription": f"{domain} Mail", + "EmailAccountType": "EmailTypeIMAP", + "EmailAddress": emailaddress, + # incoming + "IncomingMailServerHostName": imap_settings["HOSTNAME"], + "IncomingMailServerPortNumber": imap_settings["PORT"], + "IncomingMailServerUseSSL": True, + "IncomingMailServerUsername": emailaddress, + # outgoing + "OutgoingMailServerHostName": smtp_settings["HOSTNAME"], + "OutgoingMailServerPortNumber": smtp_settings["PORT"], + "OutgoingMailServerUseSSL": True, + "OutgoingMailServerUsername": emailaddress, + "OutgoingPasswordSameAsIncomingPassword": True, + } + ], + } + plist_bytes = plistlib.dumps(profile, fmt=plistlib.FMT_XML) + resp = HttpResponse( + plist_bytes, content_type="application/x-apple-aspen-config" ) - return context - - def render_to_response(self, context, **responsse_kwargs): - content = render_to_string(self.template_name, context) - response = HttpResponse(content, content_type=self.content_type) - response["Content-Disposition"] = 'attachment; filename="mail.mobileconfig"' - return response + resp["Content-Disposition"] = 'attachment; filename="mail.mobileconfig"' + return resp