mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2024-11-22 02:17:26 +00:00
support "domain aliases" (@domain => @domain aliases)
This seemed to already be technically supported but the validation is now stricter and the admin is more helpful: * Postfix seems to allow @domain.tld as an alias destination address but only if it is the only destination address (see the virtual man page). * Allow @domain.tld if it is the whole destination address string. * Otherwise, do not allow email addresses without local parts in the destination. * In the admin, add a third tab for making it clear how to add a domain alias. closes #265
This commit is contained in:
parent
9b9f5abf8f
commit
7e7abf3b53
@ -15,7 +15,7 @@ def validate_email(email, mode=None):
|
|||||||
if mode == 'user':
|
if mode == 'user':
|
||||||
# For Dovecot's benefit, only allow basic characters.
|
# For Dovecot's benefit, only allow basic characters.
|
||||||
ATEXT = r'[\w\-]'
|
ATEXT = r'[\w\-]'
|
||||||
elif mode == 'alias':
|
elif mode in (None, 'alias'):
|
||||||
# For aliases, we can allow any valid email address.
|
# For aliases, we can allow any valid email address.
|
||||||
# Based on RFC 2822 and https://github.com/SyrusAkbary/validate_email/blob/master/validate_email.py,
|
# Based on RFC 2822 and https://github.com/SyrusAkbary/validate_email/blob/master/validate_email.py,
|
||||||
# these characters are permitted in email addresses.
|
# these characters are permitted in email addresses.
|
||||||
@ -27,7 +27,8 @@ def validate_email(email, mode=None):
|
|||||||
DOT_ATOM_TEXT_LOCAL = ATEXT + r'+(?:\.' + ATEXT + r'+)*'
|
DOT_ATOM_TEXT_LOCAL = ATEXT + r'+(?:\.' + ATEXT + r'+)*'
|
||||||
if mode == 'alias':
|
if mode == 'alias':
|
||||||
# For aliases, Postfix accepts '@domain.tld' format for
|
# For aliases, Postfix accepts '@domain.tld' format for
|
||||||
# catch-all addresses. Make the local part optional.
|
# catch-all addresses on the source side and domain aliases
|
||||||
|
# on the destination side. Make the local part optional.
|
||||||
DOT_ATOM_TEXT_LOCAL = '(?:' + DOT_ATOM_TEXT_LOCAL + ')?'
|
DOT_ATOM_TEXT_LOCAL = '(?:' + DOT_ATOM_TEXT_LOCAL + ')?'
|
||||||
|
|
||||||
# as above, but we can require that the host part have at least
|
# as above, but we can require that the host part have at least
|
||||||
@ -356,19 +357,28 @@ def add_mail_alias(source, destination, env, update_if_exists=False, do_kick=Tru
|
|||||||
if not validate_email(source, mode='alias'):
|
if not validate_email(source, mode='alias'):
|
||||||
return ("Invalid incoming email address (%s)." % source, 400)
|
return ("Invalid incoming email address (%s)." % source, 400)
|
||||||
|
|
||||||
# parse comma and \n-separated destination emails & validate
|
# validate destination
|
||||||
dests = []
|
dests = []
|
||||||
|
destination = destination.strip()
|
||||||
|
if validate_email(destination, mode='alias'):
|
||||||
|
# Oostfix allows a single @domain.tld as the destination, which means
|
||||||
|
# the local part on the address is preserved in the rewrite.
|
||||||
|
dests.append(destination)
|
||||||
|
else:
|
||||||
|
# Parse comma and \n-separated destination emails & validate. In this
|
||||||
|
# case, the recipients must be complete email addresses.
|
||||||
for line in destination.split("\n"):
|
for line in destination.split("\n"):
|
||||||
for email in line.split(","):
|
for email in line.split(","):
|
||||||
email = email.strip()
|
email = email.strip()
|
||||||
if email == "": continue
|
if email == "": continue
|
||||||
if not validate_email(email, mode='alias'):
|
if not validate_email(email):
|
||||||
return ("Invalid destination email address (%s)." % email, 400)
|
return ("Invalid destination email address (%s)." % email, 400)
|
||||||
dests.append(email)
|
dests.append(email)
|
||||||
if len(destination) == 0:
|
if len(destination) == 0:
|
||||||
return ("No destination email address(es) provided.", 400)
|
return ("No destination email address(es) provided.", 400)
|
||||||
destination = ",".join(dests)
|
destination = ",".join(dests)
|
||||||
|
|
||||||
|
# save to db
|
||||||
conn, c = open_database(env, with_connection=True)
|
conn, c = open_database(env, with_connection=True)
|
||||||
try:
|
try:
|
||||||
c.execute("INSERT INTO aliases (source, destination) VALUES (?, ?)", (source, destination))
|
c.execute("INSERT INTO aliases (source, destination) VALUES (?, ?)", (source, destination))
|
||||||
|
@ -13,22 +13,26 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-1 col-sm-11">
|
<div class="col-sm-offset-1 col-sm-11">
|
||||||
<div id="alias_type_buttons" class="btn-group btn-group-xs">
|
<div id="alias_type_buttons" class="btn-group btn-group-xs">
|
||||||
<button type="button" class="btn btn-default active">Regular</button>
|
<button type="button" class="btn btn-default active" data-mode="regular">Regular</button>
|
||||||
<button type="button" class="btn btn-default">Catch-All</button>
|
<button type="button" class="btn btn-default" data-mode="catchall">Catch-All</button>
|
||||||
|
<button type="button" class="btn btn-default" data-mode="domainalias">Domain Alias</button>
|
||||||
|
</div>
|
||||||
|
<div id="alias_mode_info" class="text-info small" style="display: none; margin: .5em 0 0 0;">
|
||||||
|
<span class="catchall hidden">A catch-all alias captures all otherwise unmatched email to a domain. Enter just a part of an email address starting with the @-sign.</span>
|
||||||
|
<span class="domainalias hidden">A domain alias forwards all otherwise unmatched mail from one domain to another domain, preserving the part before the @-sign.</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="alias_catchall_info" class="text-info small" style="display: none; margin: .5em 0 0 0;">A catch-all alias captures all otherwise unmatched email to a domain. Enter just a part of an email address starting with the @-sign.</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="addaliasEmail" class="col-sm-1 control-label">Alias</label>
|
<label for="addaliasEmail" class="col-sm-1 control-label">Alias</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" id="addaliasEmail" placeholder="incoming email address (you@yourdomain.com)">
|
<input type="email" class="form-control" id="addaliasEmail">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="addaliasTargets" class="col-sm-1 control-label">Forward To</label>
|
<label for="addaliasTargets" class="col-sm-1 control-label">Forward To</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea class="form-control" rows="3" id="addaliasTargets" placeholder="forward to these email addresses (one per line or separated by commas)"></textarea>
|
<textarea class="form-control" rows="3" id="addaliasTargets"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -106,16 +110,28 @@ function show_aliases() {
|
|||||||
$('#alias_type_buttons button').off('click').click(function() {
|
$('#alias_type_buttons button').off('click').click(function() {
|
||||||
$('#alias_type_buttons button').removeClass('active');
|
$('#alias_type_buttons button').removeClass('active');
|
||||||
$(this).addClass('active');
|
$(this).addClass('active');
|
||||||
if ($(this).text() == "Regular") {
|
if ($(this).attr('data-mode') == "regular") {
|
||||||
$('#addaliasEmail').attr('type', 'email');
|
$('#addaliasEmail').attr('type', 'email');
|
||||||
$('#addaliasEmail').attr('placeholder', 'incoming email address (you@yourdomain.com)');
|
$('#addaliasEmail').attr('placeholder', 'incoming email address (e.g. you@yourdomain.com)');
|
||||||
$('#alias_catchall_info').slideUp();
|
$('#addaliasTargets').attr('placeholder', 'forward to these email addresses (one per line or separated by commas)');
|
||||||
} else {
|
$('#alias_mode_info').slideUp();
|
||||||
|
} else if ($(this).attr('data-mode') == "catchall") {
|
||||||
$('#addaliasEmail').attr('type', 'text');
|
$('#addaliasEmail').attr('type', 'text');
|
||||||
$('#addaliasEmail').attr('placeholder', 'incoming catch-all address (@yourdomain.com)');
|
$('#addaliasEmail').attr('placeholder', 'incoming catch-all address (e.g. @yourdomain.com)');
|
||||||
$('#alias_catchall_info').slideDown();
|
$('#addaliasTargets').attr('placeholder', 'forward to these email addresses (one per line or separated by commas)');
|
||||||
|
$('#alias_mode_info').slideDown();
|
||||||
|
$('#alias_mode_info span').addClass('hidden');
|
||||||
|
$('#alias_mode_info span.catchall').removeClass('hidden');
|
||||||
|
} else if ($(this).attr('data-mode') == "domainalias") {
|
||||||
|
$('#addaliasEmail').attr('type', 'text');
|
||||||
|
$('#addaliasEmail').attr('placeholder', 'incoming domain (@yourdomain.com)');
|
||||||
|
$('#addaliasTargets').attr('placeholder', 'forward to domain (@yourdomain.com)');
|
||||||
|
$('#alias_mode_info').slideDown();
|
||||||
|
$('#alias_mode_info span').addClass('hidden');
|
||||||
|
$('#alias_mode_info span.domainalias').removeClass('hidden');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
$('#alias_type_buttons button[data-mode="regular"]').click(); // init
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +182,12 @@ function aliases_edit(elem) {
|
|||||||
$("#addaliasEmail").val(email);
|
$("#addaliasEmail").val(email);
|
||||||
$("#addaliasTargets").val(targets);
|
$("#addaliasTargets").val(targets);
|
||||||
$('#add-alias-button').text('Update');
|
$('#add-alias-button').text('Update');
|
||||||
|
if (email.charAt(0) == '@' && targets.charAt(0) == '@')
|
||||||
|
$('#alias_type_buttons button[data-mode="domainalias"]').click();
|
||||||
|
else if (email.charAt(0) == '@')
|
||||||
|
$('#alias_type_buttons button[data-mode="catchall"]').click();
|
||||||
|
else
|
||||||
|
$('#alias_type_buttons button[data-mode="regular"]').click();
|
||||||
$('body').animate({ scrollTop: 0 })
|
$('body').animate({ scrollTop: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user