mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2024-11-30 03:37:05 +00:00
322a5779f1
I changed my mind. In1bf8f1991f
I allowed Unicode domain names to go into the database. I thought that was nice because it's what the user *means*. But it's not how the web works. Web and DNS were working, but mail wasn't. Postfix (as shipped with Ubuntu 14.04 without support for SMTPUTF8) exists in an ASCII-only world. When it goes to the users/aliases table, it queries in ASCII (IDNA) only and had no hope of delivering mail if the domain was in full Unicode in the database. I was thinking ahead to SMTPUTF8, where we *could* put Unicode in the database (though that would prevent IDNA-encoded addressing from being deliverable) not realizing it isn't well supported yet anyway. It's IDNA that goes on the wire in most places anyway (SMTP without SMTPUTF8 (and therefore how Postfix queries our users/aliases tables), DNS zone files, nginx config, CSR 'CN' field, X509 Common Name and Subject Alternative Names fields), so we should really be talking in terms of IDNA (i.e. ASCII). This partially reverts commit1bf8f1991f
, where I added a lot of Unicode=>IDNA conversions when writing configuration files. Instead I'm doing Unicode=>IDNA before email addresses get into the users/aliases table. Now we assume the database uses IDNA-encoded ASCII domain names. When adding/removing aliases, addresses are converted to ASCII (w/ IDNA). User accounts must be ASCII-only anyway because of Dovecot's auth limitations, so we don't do any IDNA conversion (don't want to change the user's login info behind their back!). The aliases control panel page converts domains back to Unicode for display to be nice. The status checks converts the domains to Unicode just for the output headings. A migration is added to convert existing aliases with Unicode domains into IDNA. Any custom DNS or web settings with Unicode may need to be changed. Future support for SMTPUTF8 will probably need to add columns in the users/aliases table so that it lists both IDNA and Unicode forms.
222 lines
8.1 KiB
HTML
222 lines
8.1 KiB
HTML
<style>
|
|
#alias_table .actions > * { padding-right: 3px; }
|
|
#alias_table .alias-required .remove { display: none }
|
|
</style>
|
|
|
|
<h2>Aliases</h2>
|
|
|
|
<h3>Add a mail alias</h3>
|
|
|
|
<p>Aliases are email forwarders. An alias can forward email to a <a href="javascript:show_panel('users')">mail user</a> or to any email address.</p>
|
|
|
|
<form class="form-horizontal" role="form" onsubmit="do_add_alias(); return false;">
|
|
<div class="form-group">
|
|
<div class="col-sm-offset-1 col-sm-11">
|
|
<div id="alias_type_buttons" class="btn-group btn-group-xs">
|
|
<button type="button" class="btn btn-default active" data-mode="regular">Regular</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>
|
|
<div class="form-group">
|
|
<label for="addaliasEmail" class="col-sm-1 control-label">Alias</label>
|
|
<div class="col-sm-10">
|
|
<input type="email" class="form-control" id="addaliasEmail">
|
|
<div style="margin-top: 3px; padding-left: 3px; font-size: 90%" class="text-muted">You may use international (non-ASCII) characters for the domain part of the email address only.</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="addaliasTargets" class="col-sm-1 control-label">Forward To</label>
|
|
<div class="col-sm-10">
|
|
<textarea class="form-control" rows="3" id="addaliasTargets"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="col-sm-offset-1 col-sm-11">
|
|
<button id="add-alias-button" type="submit" class="btn btn-primary">Add Alias</button>
|
|
<button id="alias-cancel" class="btn btn-default hidden" onclick="aliases_reset_form(); return false;">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<h3>Existing mail aliases</h3>
|
|
<table id="alias_table" class="table" style="width: auto">
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>Alias<br></th>
|
|
<th>Forwards To</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p style="margin-top: 1.5em"><small>Hostmaster@, postmaster@, and admin@ email addresses are required on some domains.</small></p>
|
|
|
|
<div style="display: none">
|
|
<table>
|
|
<tr id="alias-template">
|
|
<td class='actions'>
|
|
<a href="#" onclick="aliases_edit(this); scroll_top(); return false;" class='edit' title="Edit Alias">
|
|
<span class="glyphicon glyphicon-pencil"></span>
|
|
</a>
|
|
<a href="#" onclick="aliases_remove(this); return false;" class='remove' title="Remove Alias">
|
|
<span class="glyphicon glyphicon-trash"></span>
|
|
</a>
|
|
</td>
|
|
<td class='email'> </td>
|
|
<td class='target'> </td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
|
|
<script>
|
|
function show_aliases() {
|
|
$('#alias_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
|
api(
|
|
"/mail/aliases",
|
|
"GET",
|
|
{ format: 'json' },
|
|
function(r) {
|
|
$('#alias_table tbody').html("");
|
|
for (var i = 0; i < r.length; i++) {
|
|
var hdr = $("<tr><td colspan='3'><h4/></td></tr>");
|
|
hdr.find('h4').text(r[i].domain);
|
|
$('#alias_table tbody').append(hdr);
|
|
|
|
for (var k = 0; k < r[i].aliases.length; k++) {
|
|
var alias = r[i].aliases[k];
|
|
|
|
var n = $("#alias-template").clone();
|
|
n.attr('id', '');
|
|
|
|
if (alias.required) n.addClass('alias-required');
|
|
n.attr('data-email', alias.source_display); // this is decoded from IDNA, but will get re-coded to IDNA on the backend
|
|
n.find('td.email').text(alias.source_display)
|
|
for (var j = 0; j < alias.destination.length; j++)
|
|
n.find('td.target').append($("<div></div>").text(alias.destination[j]))
|
|
$('#alias_table tbody').append(n);
|
|
}
|
|
}
|
|
})
|
|
|
|
$(function() {
|
|
$('#alias_type_buttons button').off('click').click(function() {
|
|
$('#alias_type_buttons button').removeClass('active');
|
|
$(this).addClass('active');
|
|
if ($(this).attr('data-mode') == "regular") {
|
|
$('#addaliasEmail').attr('type', 'email');
|
|
$('#addaliasEmail').attr('placeholder', 'incoming email address (e.g. you@yourdomain.com)');
|
|
$('#addaliasTargets').attr('placeholder', 'forward to these email addresses (one per line or separated by commas)');
|
|
$('#alias_mode_info').slideUp();
|
|
} else if ($(this).attr('data-mode') == "catchall") {
|
|
$('#addaliasEmail').attr('type', 'text');
|
|
$('#addaliasEmail').attr('placeholder', 'incoming catch-all address (e.g. @yourdomain.com)');
|
|
$('#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
|
|
})
|
|
}
|
|
|
|
var is_alias_add_update = false;
|
|
function do_add_alias() {
|
|
var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias";
|
|
var email = $("#addaliasEmail").val();
|
|
var targets = $("#addaliasTargets").val();
|
|
api(
|
|
"/mail/aliases/add",
|
|
"POST",
|
|
{
|
|
update_if_exists: is_alias_add_update ? '1' : '0',
|
|
source: email,
|
|
destination: targets
|
|
},
|
|
function(r) {
|
|
// Responses are multiple lines of pre-formatted text.
|
|
show_modal_error(title, $("<pre/>").text(r));
|
|
show_aliases()
|
|
aliases_reset_form();
|
|
},
|
|
function(r) {
|
|
show_modal_error(title, r);
|
|
});
|
|
return false;
|
|
}
|
|
|
|
function aliases_reset_form() {
|
|
$("#addaliasEmail").prop('disabled', false);
|
|
$("#addaliasEmail").val('')
|
|
$("#addaliasTargets").val('')
|
|
$('#alias-cancel').addClass('hidden');
|
|
$('#add-alias-button').text('Add Alias');
|
|
is_alias_add_update = false;
|
|
}
|
|
|
|
function aliases_edit(elem) {
|
|
var email = $(elem).parents('tr').attr('data-email');
|
|
var targetdivs = $(elem).parents('tr').find('.target div');
|
|
var targets = "";
|
|
for (var i = 0; i < targetdivs.length; i++)
|
|
targets += $(targetdivs[i]).text() + "\n";
|
|
|
|
is_alias_add_update = true;
|
|
$('#alias-cancel').removeClass('hidden');
|
|
$("#addaliasEmail").prop('disabled', true);
|
|
$("#addaliasEmail").val(email);
|
|
$("#addaliasTargets").val(targets);
|
|
$('#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 })
|
|
}
|
|
|
|
function aliases_remove(elem) {
|
|
var email = $(elem).parents('tr').attr('data-email');
|
|
show_modal_confirm(
|
|
"Remove Alias",
|
|
"Remove " + email + "?",
|
|
"Remove",
|
|
function() {
|
|
api(
|
|
"/mail/aliases/remove",
|
|
"POST",
|
|
{
|
|
source: email
|
|
},
|
|
function(r) {
|
|
// Responses are multiple lines of pre-formatted text.
|
|
show_modal_error("Remove User", $("<pre/>").text(r));
|
|
show_aliases();
|
|
});
|
|
});
|
|
}
|
|
|
|
function scroll_top() {
|
|
$('html, body').animate({
|
|
scrollTop: $("#panel_aliases").offset().top
|
|
}, 1000);
|
|
}
|
|
</script>
|