commit 530fbab51c1799182a1d53e58121e6832b7fd46f Author: Julian Rother Date: Thu Jan 16 02:45:17 2025 +0100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..d5c6296 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# E-Mail Autodiscovery + +Based on https://github.com/Monogramm/autodiscover-email-settings (MIT) diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..8b4667c --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,19 @@ +mail_autodiscover: + info: + name: Example Org + url: https://autodiscover.example.com + domain: example.com + imap: + host: imap.example.com + port: 993 + socket: SSL # plain/SSL/STARTTLS + smtp: + host: smtp.example.com + port: 587 + socket: STARTTLS # plain/SSL/STARTTLS + # Apple mobile config identifiers + mobile: + identifier: com.example.autodiscover # Replace with reverse of your domain + uuid: 92943D26-CAB3-4086-897D-DC6C0D8B1E86 # Replace with random UUID + mail: + uuid: 7A981A9E-D5D0-4EF8-87FE-39FD6A506FAC # Replace with random UUID diff --git a/files/app/__init__.py b/files/app/__init__.py new file mode 100644 index 0000000..a0dd398 --- /dev/null +++ b/files/app/__init__.py @@ -0,0 +1,52 @@ +import json + +from flask import Flask, request, Response, abort, render_template +from defusedxml import ElementTree as ET + +app = Flask(__name__) + +with open('/etc/mail-autodiscover.json') as f: + settings = json.load(f) + +@app.route('/autodiscover/autodiscover.xml', methods=['GET', 'POST']) +@app.route('/Autodiscover/Autodiscover.xml', methods=['GET', 'POST']) +def outlook_autodiscover(): + data = request.get_data() + try: + root = ET.fromstring(data) + schema = root.find('./{*}Request/{*}AcceptableResponseSchema').text + email = root.find('./{*}Request/{*}EMailAddress').text + email_username = email.rsplit('@', 1)[0] + email_domain = email.rsplit('@', 1)[-1] + except (SyntaxError, ValueError): + schema = 'http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006' + email = '' + email_username = '' + email_domain = '' + ctx = settings.copy() + ctx['email'] = email + ctx['email_username'] = email_username + ctx['email_domain'] = email_domain + return Response( + render_template('autodiscover.xml', **ctx), + content_type='application/xml', + ) + +@app.route('/mail/config-v1.1.xml') +def thunderbird_autoconfig(): + ctx = settings.copy() + ctx['email'] = request.args.get('email', '') + return Response( + render_template('autoconfig.xml', **ctx), + content_type='application/xml', + ) + +@app.route('/email.mobileconfig') +def apple_mobileconfig(): + ctx = settings.copy() + ctx['email'] = request.args['email'] + return Response( + render_template('mobileconfig.xml', **ctx), + content_type='application/x-apple-aspen-config; charset=utf-8', + headers={'Content-Disposition': 'attachment; filename="email.mobileconfig"'}, + ) diff --git a/files/app/templates/autoconfig.xml b/files/app/templates/autoconfig.xml new file mode 100644 index 0000000..ba4dce4 --- /dev/null +++ b/files/app/templates/autoconfig.xml @@ -0,0 +1,37 @@ + + + + {{domain}} + + {{info.name}} Email + %EMAILLOCALPART% + + {%- if imap.host %} + + {{imap.host}} + {{imap.port}} + {{imap.socket}} + password-cleartext + %EMAILADDRESS% + + {% endif -%} + + {%- if smtp.host %} + + {{smtp.host}} + {{smtp.port}} + {{smtp.socket}} + password-cleartext + %EMAILADDRESS% + + {% endif -%} + + + Generic settings page + Paramètres généraux + Configuraciones genéricas + Allgemeine Beschreibung der Einstellungen + Страница общих настроек + + + diff --git a/files/app/templates/autodiscover.xml b/files/app/templates/autodiscover.xml new file mode 100644 index 0000000..aa92395 --- /dev/null +++ b/files/app/templates/autodiscover.xml @@ -0,0 +1,48 @@ + + + + + {{info.name}} + + + email + settings + {{info.url}} + {%- if imap.host %} + + IMAP + {{imap.host}} + {{imap.port}} + {%- if email %} + {{email}} + {% endif -%} + on + {{domain}} + on + {{ 'on' if imap.socket in ('SSL', 'STARTTLS') else 'off' }} + {{ 'TLS' if imap.socket == 'STARTTLS' else imap.socket }} + on + + {% endif -%} + + {%- if smtp.host %} + + SMTP + {{smtp.host}} + {{smtp.port}} + {%- if email %} + {{email}} + {% endif -%} + on + {{domain}} + on + {{ 'on' if smtp.socket in ('SSL', 'STARTTLS') else 'off' }} + {{ 'TLS' if imap.socket == 'STARTTLS' else imap.socket }} + on + + {% endif -%} + + + diff --git a/files/app/templates/mobileconfig.xml b/files/app/templates/mobileconfig.xml new file mode 100644 index 0000000..a85f1f7 --- /dev/null +++ b/files/app/templates/mobileconfig.xml @@ -0,0 +1,81 @@ + + + + + HasRemovalPasscode + + PayloadContent + +{% if imap.host %} + + EmailAccountDescription + {{email}} + EmailAccountName + {{email}} + EmailAccountType + {% if imap.host %}EmailTypeIMAP{% else %}EmailTypePOP{% endif %} + EmailAddress + {{email}} + IncomingMailServerAuthentication + EmailAuthPassword + IncomingMailServerHostName + {{imap.host}} + IncomingMailServerPortNumber + {{imap.port}} + IncomingMailServerUseSSL + <{{ 'true' if imap.socket in ['SSL', 'STARTTLS'] else 'false' }}/> + IncomingMailServerUsername + {{email}} + OutgoingPasswordSameAsIncomingPassword + + OutgoingMailServerAuthentication + EmailAuthPassword + OutgoingMailServerHostName + {{smtp.host}} + OutgoingMailServerPortNumber + {{smtp.port}} + OutgoingMailServerUseSSL + <{{ 'true' if smtp.socket in ['SSL', 'STARTTLS'] else 'false' }}/> + OutgoingMailServerUsername + {{email}} + SMIMEEnabled + + SMIMEEnablePerMessageSwitch + + SMIMEEnableEncryptionPerMessageSwitch + + disableMailRecentsSyncing + + PayloadDescription + {{info.name}} Email + PayloadDisplayName + {{email}} + PayloadIdentifier + {{mobile.identifier}}.com.apple.mail.managed.{{mobile.mail.uuid}} + PayloadType + com.apple.mail.managed + PayloadUUID + {{mobile.mail.uuid}} + PayloadVersion + 1 + +{% endif %} + + PayloadDescription + {{info.name}} + PayloadDisplayName + {{email}} + PayloadIdentifier + {{mobile.identifier}} + PayloadOrganization + {{domain}} + PayloadRemovalDisallowed + + PayloadType + Configuration + PayloadUUID + {{mobile.uuid}} + PayloadVersion + 2 + + diff --git a/files/mail-autodiscover.service b/files/mail-autodiscover.service new file mode 100644 index 0000000..ccfecf8 --- /dev/null +++ b/files/mail-autodiscover.service @@ -0,0 +1,20 @@ +[Unit] +Description=Mail Autodiscover Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=notify +DynamicUser=true +WorkingDirectory=/usr/local/lib/mail-autodiscover +Environment="PYTHONUNBUFFERED=1" +ExecStart=/usr/bin/gunicorn --workers=2 --log-level=info -b 127.0.0.1:8749 app:app +Restart=on-failure +RestartSec=30 +PrivateTmp=true +ProtectSystem=strict +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..c0c282c --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,5 @@ +- name: Restart mail-autodiscover.service + ansible.builtin.systemd_service: + name: mail-autodiscover.service + state: restarted + daemon_reload: yes diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..ab5cd87 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,32 @@ +- name: Install server dependencies + ansible.builtin.apt: + pkg: + - python3-flask + - python3-defusedxml + - gunicorn + +- name: Create directory for app + ansible.builtin.file: + path: /usr/local/lib/mail-autodiscover + state: directory + +- name: Install app + ansible.builtin.copy: + src: app + dest: /usr/local/lib/mail-autodiscover/ + notify: Restart mail-autodiscover.service + +- name: Install config + ansible.builtin.copy: + content: '{{ mail_autodiscover|tojson }}' + dest: /etc/mail-autodiscover.json + owner: root + group: root + mode: 0644 + notify: Restart mail-autodiscover.service + +- name: Install systemd units + ansible.builtin.copy: + src: mail-autodiscover.service + dest: /etc/systemd/system/mail-autodiscover.service + notify: Restart mail-autodiscover.service