mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2024-12-22 07:17:05 +00:00
3fdfad27cd
This is an extension of #427. Building on that change it adds support in the aliases table for flagging aliases as: 1. Applicable to inbound and outbound mail. 2. Applicable to inbound mail only. 3. Applicable to outbound mail only. 4. Disabled. The aliases UI is also updated to allow administrators to set the direction of each alias. Using this extra information, the sqlite queries executed by Postfix are updated so only the relevant alias types are checked. The goal and result of this change is that outbound-only catch-all aliases can now be defined (in fact catch-all aliases of any type can be defined). This allow us to continue supporting relaying as described at https://mailinabox.email/advanced-configuration.html#relay without requiring that administrators either create regular aliases for each outbound *relay* address, or that they create a catch-all alias and then face a flood of spam. I have tested the code as it is in this commit and fixed every issue I found, so in that regard the change is complete. However I see room for improvement in terms of updating terminology to make the UI etc. easier to understand. I'll make those changes as subsequent commits so that this tested checkpoint is not lost, but also so they can be rejected independently of the actual change if not wanted.
261 lines
10 KiB
HTML
261 lines
10 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" 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="addaliasDirection" class="col-sm-1 control-label">Direction</label>
|
|
<div class="col-sm-10">
|
|
<select class="form-control" id="addaliasDirection">
|
|
<option value="disabled">Disabled</option>
|
|
<option value="outbound">Outbound only</option>
|
|
<option value="inbound">Inbound only</option>
|
|
<option value="bidirectional">Both</option>
|
|
</select>
|
|
</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>Direction</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='direction'> </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)
|
|
if (!alias.applies_inbound && !alias.applies_outbound) {
|
|
n.find('td.direction').text('')
|
|
n.attr('data-direction', 'disabled');
|
|
} else if (!alias.applies_inbound && alias.applies_outbound) {
|
|
n.find('td.direction').text('↤')
|
|
n.attr('data-direction', 'outbound');
|
|
} else if (alias.applies_inbound && !alias.applies_outbound) {
|
|
n.find('td.direction').text('↦')
|
|
n.attr('data-direction', 'inbound');
|
|
} else if (alias.applies_inbound && alias.applies_outbound) {
|
|
n.find('td.direction').text('↮')
|
|
n.attr('data-direction', 'bidirectional');
|
|
}
|
|
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)');
|
|
$('#addaliasDirection').val('bidirectional');
|
|
$('#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)');
|
|
$('#addaliasDirection').val('outbound');
|
|
$('#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)');
|
|
$('#addaliasDirection').val('inbound');
|
|
$('#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 direction = $("#addaliasDirection").val();
|
|
var targets = $("#addaliasTargets").val();
|
|
api(
|
|
"/mail/aliases/add",
|
|
"POST",
|
|
{
|
|
update_if_exists: is_alias_add_update ? '1' : '0',
|
|
source: email,
|
|
destination: targets,
|
|
applies_inbound: (direction == 'bidirectional' || direction == 'inbound') ? '1' : '0',
|
|
applies_outbound: (direction == 'bidirectional' || direction == 'outbound') ? '1' : '0'
|
|
},
|
|
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('')
|
|
if ($('#alias_type_buttons button').attr('data-mode') == "regular")
|
|
$('#addaliasDirection').val('bidirectional');
|
|
else if ($('#alias_type_buttons button').attr('data-mode') == "catchall")
|
|
$('#alias_type_buttons').val('outbound');
|
|
else if ($('#addaliasDirection button').attr('data-mode') == "domainalias")
|
|
$('#addaliasDirection').val('inbound');
|
|
$("#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";
|
|
var direction = $(elem).parents('tr').attr('data-direction')
|
|
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();
|
|
$('#alias-cancel').removeClass('hidden');
|
|
$("#addaliasEmail").prop('disabled', true);
|
|
$("#addaliasEmail").val(email);
|
|
$('#addaliasDirection').val(direction);
|
|
$("#addaliasTargets").val(targets);
|
|
$('#add-alias-button').text('Update');
|
|
$('body').animate({ scrollTop: 0 })
|
|
is_alias_add_update = true;
|
|
}
|
|
|
|
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>
|