diff --git a/core/admin/mailu/__init__.py b/core/admin/mailu/__init__.py
index e32d2e7d..1183a174 100644
--- a/core/admin/mailu/__init__.py
+++ b/core/admin/mailu/__init__.py
@@ -27,6 +27,7 @@ default_config = {
'BOOTSTRAP_SERVE_LOCAL': True,
'RATELIMIT_STORAGE_URL': 'redis://redis',
'DEBUG': False,
+ 'DOMAIN_REGISTRATION': False,
# Statistics management
'INSTANCE_ID_PATH': '/data/instance',
'STATS_ENDPOINT': '0.{}.stats.mailu.io',
diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py
index c17da724..c5ce5798 100644
--- a/core/admin/mailu/ui/forms.py
+++ b/core/admin/mailu/ui/forms.py
@@ -52,6 +52,15 @@ class DomainForm(flask_wtf.FlaskForm):
submit = fields.SubmitField(_('Create'))
+class DomainSignupForm(flask_wtf.FlaskForm):
+ name = fields.StringField(_('Domain name'), [validators.DataRequired()])
+ localpart = fields.StringField(_('Initial admin'), [validators.DataRequired()])
+ pw = fields.PasswordField(_('Admin password'), [validators.DataRequired()])
+ pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])
+ captcha = flask_wtf.RecaptchaField()
+ submit = fields.SubmitField(_('Create'))
+
+
class AlternativeForm(flask_wtf.FlaskForm):
name = fields.StringField(_('Alternative name'), [validators.DataRequired()])
submit = fields.SubmitField(_('Create'))
diff --git a/core/admin/mailu/ui/templates/domain/signup.html b/core/admin/mailu/ui/templates/domain/signup.html
new file mode 100644
index 00000000..c8a52f6c
--- /dev/null
+++ b/core/admin/mailu/ui/templates/domain/signup.html
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+
+{% block title %}
+{% trans %}Register a domain{% endtrans %}
+{% endblock %}
+
+{% block content %}
+
+
+{% endblock %}
diff --git a/core/admin/mailu/ui/templates/sidebar.html b/core/admin/mailu/ui/templates/sidebar.html
index e700064c..2520fa33 100644
--- a/core/admin/mailu/ui/templates/sidebar.html
+++ b/core/admin/mailu/ui/templates/sidebar.html
@@ -92,6 +92,13 @@
{% trans %}Help{% endtrans %}
+ {% if config['DOMAIN_REGISTRATION'] %}
+
+
+ {% trans %}Register a domain{% endtrans %}
+
+
+ {% endif %}
{% if current_user.is_authenticated %}
diff --git a/core/admin/mailu/ui/views/domains.py b/core/admin/mailu/ui/views/domains.py
index 048e1829..459902e2 100644
--- a/core/admin/mailu/ui/views/domains.py
+++ b/core/admin/mailu/ui/views/domains.py
@@ -2,7 +2,9 @@ from mailu import app, db, models
from mailu.ui import ui, forms, access
import flask
+import flask_login
import wtforms_components
+import dns.resolver
@ui.route('/domain', methods=['GET'])
@@ -73,3 +75,52 @@ def domain_genkeys(domain_name):
domain.generate_dkim_key()
return flask.redirect(
flask.url_for(".domain_details", domain_name=domain_name))
+
+
+@ui.route('/domain/signup', methods=['GET', 'POST'])
+def domain_signup(domain_name=None):
+ if not app.config['DOMAIN_REGISTRATION']:
+ flask.abort(403)
+ form = forms.DomainSignupForm()
+ if flask_login.current_user.is_authenticated:
+ del form.localpart
+ del form.pw
+ del form.pw2
+ if form.validate_on_submit():
+ conflicting_domain = models.Domain.query.get(form.name.data)
+ conflicting_alternative = models.Alternative.query.get(form.name.data)
+ conflicting_relay = models.Relay.query.get(form.name.data)
+ hostnames = app.config['HOSTNAMES'].split(',')
+ if conflicting_domain or conflicting_alternative or conflicting_relay:
+ flask.flash('Domain %s is already used' % form.name.data, 'error')
+ else:
+ # Check if the domain MX actually points to this server
+ try:
+ mxok = any(str(rset).split()[-1][:-1] in hostnames
+ for rset in dns.resolver.query(form.name.data, 'MX'))
+ except Exception as e:
+ mxok = False
+ if mxok:
+ # Actually create the domain
+ domain = models.Domain()
+ form.populate_obj(domain)
+ domain.max_quota_bytes = app.config['DEFAULT_QUOTA']
+ domain.max_users = 10
+ domain.max_aliases = 10
+ db.session.add(domain)
+ if flask_login.current_user.is_authenticated:
+ user = models.User.query.get(flask_login.current_user.email)
+ else:
+ user = models.User()
+ user.domain = domain
+ form.populate_obj(user)
+ user.set_password(form.pw.data)
+ user.quota_bytes = domain.max_quota_bytes
+ db.session.add(user)
+ domain.managers.append(user)
+ db.session.commit()
+ flask.flash('Domain %s created' % domain)
+ return flask.redirect(flask.url_for('.domain_list'))
+ else:
+ flask.flash('The MX record was not properly set', 'error')
+ return flask.render_template('domain/signup.html', form=form)
diff --git a/core/admin/requirements.txt b/core/admin/requirements.txt
index 3d06b34b..a40e6eb5 100644
--- a/core/admin/requirements.txt
+++ b/core/admin/requirements.txt
@@ -16,3 +16,4 @@ docker-py
tabulate
PyYAML
PyOpenSSL
+dnspython