Skip to content

Commit

Permalink
try clarifying that aliases are lists, not to be used for simply addi…
Browse files Browse the repository at this point in the history
…ng an address to an account

for issue #244 by exander77
  • Loading branch information
mjl- committed Dec 7, 2024
1 parent f7b58c8 commit cbe418e
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 22 deletions.
18 changes: 11 additions & 7 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1056,25 +1056,29 @@ error too, for reference.
# mox config alias list
List aliases for domain.
Show aliases (lists) for domain.
usage: mox config alias list domain
# mox config alias print
Print settings and members of alias.
Print settings and members of alias (list).
usage: mox config alias print alias
# mox config alias add
Add new alias with one or more addresses and public posting enabled.
Add new alias (list) with one or more addresses and public posting enabled.
An alias is used for delivering incoming email to multiple recipients. If you
want to add an address to an account, don't use an alias, just add the address
to the account.
usage: mox config alias add alias@domain rcpt1@domain ...
# mox config alias update
Update alias configuration.
Update alias (list) configuration.
usage: mox config alias update alias@domain [-postpublic false|true -listmembers false|true -allowmsgfrom false|true]
-allowmsgfrom string
Expand All @@ -1086,19 +1090,19 @@ Update alias configuration.
# mox config alias rm
Remove alias.
Remove alias (list).
usage: mox config alias rm alias@domain
# mox config alias addaddr
Add addresses to alias.
Add addresses to alias (list).
usage: mox config alias addaddr alias@domain rcpt1@domain ...
# mox config alias rmaddr
Remove addresses from alias.
Remove addresses from alias (list).
usage: mox config alias rmaddr alias@domain rcpt1@domain ...
Expand Down
19 changes: 12 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ func ctlcmdConfigDomainRemove(ctl *ctl, d dns.Domain) {

func cmdConfigAliasList(c *cmd) {
c.params = "domain"
c.help = `List aliases for domain.`
c.help = `Show aliases (lists) for domain.`
args := c.Parse()
if len(args) != 1 {
c.Usage()
Expand All @@ -756,7 +756,7 @@ func ctlcmdConfigAliasList(ctl *ctl, address string) {

func cmdConfigAliasPrint(c *cmd) {
c.params = "alias"
c.help = `Print settings and members of alias.`
c.help = `Print settings and members of alias (list).`
args := c.Parse()
if len(args) != 1 {
c.Usage()
Expand All @@ -775,7 +775,12 @@ func ctlcmdConfigAliasPrint(ctl *ctl, address string) {

func cmdConfigAliasAdd(c *cmd) {
c.params = "alias@domain rcpt1@domain ..."
c.help = `Add new alias with one or more addresses and public posting enabled.`
c.help = `Add new alias (list) with one or more addresses and public posting enabled.
An alias is used for delivering incoming email to multiple recipients. If you
want to add an address to an account, don't use an alias, just add the address
to the account.
`
args := c.Parse()
if len(args) < 2 {
c.Usage()
Expand All @@ -796,7 +801,7 @@ func ctlcmdConfigAliasAdd(ctl *ctl, address string, alias config.Alias) {

func cmdConfigAliasUpdate(c *cmd) {
c.params = "alias@domain [-postpublic false|true -listmembers false|true -allowmsgfrom false|true]"
c.help = `Update alias configuration.`
c.help = `Update alias (list) configuration.`
var postpublic, listmembers, allowmsgfrom string
c.flag.StringVar(&postpublic, "postpublic", "", "whether anyone or only list members can post")
c.flag.StringVar(&listmembers, "listmembers", "", "whether list members can list members")
Expand All @@ -822,7 +827,7 @@ func ctlcmdConfigAliasUpdate(ctl *ctl, alias, postpublic, listmembers, allowmsgf

func cmdConfigAliasRemove(c *cmd) {
c.params = "alias@domain"
c.help = "Remove alias."
c.help = "Remove alias (list)."
args := c.Parse()
if len(args) != 1 {
c.Usage()
Expand All @@ -840,7 +845,7 @@ func ctlcmdConfigAliasRemove(ctl *ctl, alias string) {

func cmdConfigAliasAddaddr(c *cmd) {
c.params = "alias@domain rcpt1@domain ..."
c.help = `Add addresses to alias.`
c.help = `Add addresses to alias (list).`
args := c.Parse()
if len(args) < 2 {
c.Usage()
Expand All @@ -859,7 +864,7 @@ func ctlcmdConfigAliasAddaddr(ctl *ctl, alias string, addresses []string) {

func cmdConfigAliasRemoveaddr(c *cmd) {
c.params = "alias@domain rcpt1@domain ..."
c.help = `Remove addresses from alias.`
c.help = `Remove addresses from alias (list).`
args := c.Parse()
if len(args) < 2 {
c.Usage()
Expand Down
12 changes: 8 additions & 4 deletions webadmin/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2175,7 +2175,7 @@ const account = async (name) => {
await check(fieldset, client.AddressAdd(address, name));
form.reset();
window.location.reload(); // todo: only reload the destinations
}, fieldset = dom.fieldset(dom.label(style({ display: 'inline-block' }), dom.span('Localpart', attr.title('The localpart is the part before the "@"-sign of an email address. If empty, a catchall address is configured for the domain.')), dom.br(), localpart = dom.input()), '@', dom.label(style({ display: 'inline-block' }), dom.span('Domain'), dom.br(), domain = dom.select((domains || []).map(d => dom.option(domainName(d), domainName(d) === config.Domain ? attr.selected('') : [])))), ' ', dom.submitbutton('Add address'))), dom.br(), dom.h2('Aliases/lists'), dom.table(dom.thead(dom.tr(dom.th('Alias address', attr.title('Messages sent to this address will be delivered to all members of the alias/list. A member does not receive a message if their address is in the message From header.')), dom.th('Subscription address'), dom.th('Allowed senders', attr.title('Whether only members can send through the alias/list, or anyone.')), dom.th('Send as alias address', attr.title('If enabled, messages can be sent with the alias address in the message "From" header.')), dom.th('Members visible', attr.title('If enabled, members can see the addresses of other members.')))), (config.Aliases || []).length === 0 ? dom.tr(dom.td(attr.colspan('6'), 'None')) : [], (config.Aliases || []).sort((a, b) => a.Alias.LocalpartStr < b.Alias.LocalpartStr ? -1 : (domainName(a.Alias.Domain) < domainName(b.Alias.Domain) ? -1 : 1)).map(a => dom.tr(dom.td(dom.a(prewrap(a.Alias.LocalpartStr, '@', domainName(a.Alias.Domain)), attr.href('#domains/' + domainName(a.Alias.Domain) + '/alias/' + encodeURIComponent(a.Alias.LocalpartStr)))), dom.td(prewrap(a.SubscriptionAddress)), dom.td(a.Alias.PostPublic ? 'Anyone' : 'Members only'), dom.td(a.Alias.AllowMsgFrom ? 'Yes' : 'No'), dom.td(a.Alias.ListMembers ? 'Yes' : 'No'), dom.td(dom.clickbutton('Remove', async function click(e) {
}, fieldset = dom.fieldset(dom.label(style({ display: 'inline-block' }), dom.span('Localpart', attr.title('The localpart is the part before the "@"-sign of an email address. If empty, a catchall address is configured for the domain.')), dom.br(), localpart = dom.input()), '@', dom.label(style({ display: 'inline-block' }), dom.span('Domain'), dom.br(), domain = dom.select((domains || []).map(d => dom.option(domainName(d), domainName(d) === config.Domain ? attr.selected('') : [])))), ' ', dom.submitbutton('Add address'))), dom.br(), dom.h2('Alias (list) membership'), dom.table(dom.thead(dom.tr(dom.th('Alias address', attr.title('Messages sent to this address will be delivered to all members of the alias/list. A member does not receive a message if their address is in the message From header.')), dom.th('Subscription address'), dom.th('Allowed senders', attr.title('Whether only members can send through the alias/list, or anyone.')), dom.th('Send as alias address', attr.title('If enabled, messages can be sent with the alias address in the message "From" header.')), dom.th('Members visible', attr.title('If enabled, members can see the addresses of other members.')))), (config.Aliases || []).length === 0 ? dom.tr(dom.td(attr.colspan('6'), 'None')) : [], (config.Aliases || []).sort((a, b) => a.Alias.LocalpartStr < b.Alias.LocalpartStr ? -1 : (domainName(a.Alias.Domain) < domainName(b.Alias.Domain) ? -1 : 1)).map(a => dom.tr(dom.td(dom.a(prewrap(a.Alias.LocalpartStr, '@', domainName(a.Alias.Domain)), attr.href('#domains/' + domainName(a.Alias.Domain) + '/alias/' + encodeURIComponent(a.Alias.LocalpartStr)))), dom.td(prewrap(a.SubscriptionAddress)), dom.td(a.Alias.PostPublic ? 'Anyone' : 'Members only'), dom.td(a.Alias.AllowMsgFrom ? 'Yes' : 'No'), dom.td(a.Alias.ListMembers ? 'Yes' : 'No'), dom.td(dom.clickbutton('Remove', async function click(e) {
await check(e.target, client.AliasAddressesRemove(a.Alias.LocalpartStr, domainName(a.Alias.Domain), [a.SubscriptionAddress]));
window.location.reload(); // todo: reload less
}))))), dom.br(), dom.h2('Settings'), dom.form(fieldsetSettings = dom.fieldset(dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Maximum outgoing messages per day', attr.title('Maximum number of outgoing messages for this account in a 24 hour window. This limits the damage to recipients and the reputation of this mail server in case of account compromise. Default 1000. MaxOutgoingMessagesPerDay in configuration file.')), dom.br(), maxOutgoingMessagesPerDay = dom.input(attr.type('number'), attr.required(''), attr.value('' + (config.MaxOutgoingMessagesPerDay || 1000)))), dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Maximum first-time recipients per day', attr.title('Maximum number of first-time recipients in outgoing messages for this account in a 24 hour window. This limits the damage to recipients and the reputation of this mail server in case of account compromise. Default 200. MaxFirstTimeRecipientsPerDay in configuration file.')), dom.br(), maxFirstTimeRecipientsPerDay = dom.input(attr.type('number'), attr.required(''), attr.value('' + (config.MaxFirstTimeRecipientsPerDay || 200)))), dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Disk usage quota: Maximum total message size ', attr.title('Default maximum total message size in bytes for the account, overriding any globally configured default maximum size if non-zero. A negative value can be used to have no limit in case there is a limit by default. Attempting to add new messages to an account beyond its maximum total size will result in an error. Useful to prevent a single account from filling storage. Use units "k" for kilobytes, or "m", "g", "t".')), dom.br(), quotaMessageSize = dom.input(attr.value(formatQuotaSize(config.QuotaMessageSize))), ' Current usage is ', formatQuotaSize(Math.floor(diskUsage / (1024 * 1024)) * 1024 * 1024), '.'), dom.div(style({ display: 'block', marginBottom: '.5ex' }), dom.label(firstTimeSenderDelay = dom.input(attr.type('checkbox'), config.NoFirstTimeSenderDelay ? [] : attr.checked('')), ' ', dom.span('Delay deliveries from first-time senders.', attr.title('To slow down potential spammers, when the message is misclassified as non-junk. Turning off the delay can be useful when the account processes messages automatically and needs fast responses.')))), dom.submitbutton('Save')), async function submit(e) {
Expand Down Expand Up @@ -2289,6 +2289,7 @@ const domain = async (d) => {
let aliasFieldset;
let aliasLocalpart;
let aliasAddresses;
let aliasAddText;
let descrFieldset;
let descrText;
let clientSettingsDomainFieldset;
Expand Down Expand Up @@ -2364,9 +2365,9 @@ const domain = async (d) => {
await check(addrFieldset, client.AddressAdd(addrLocalpart.value + '@' + d, addrAccount.value));
addrForm.reset();
window.location.reload(); // todo: only reload the addresses
}, addrFieldset = dom.fieldset(dom.label(style({ display: 'inline-block' }), dom.span('Localpart', attr.title('The localpart is the part before the "@"-sign of an address. An empty localpart is the catchall destination/address for the domain.')), dom.br(), addrLocalpart = dom.input()), '@', domainName(dnsdomain), ' ', dom.label(style({ display: 'inline-block' }), dom.span('Account', attr.title('Account to assign the address to.')), dom.br(), addrAccount = dom.select(attr.required(''), (accounts || []).map(a => dom.option(a)))), ' ', dom.submitbutton('Add address', attr.title('Address will be added and the config reloaded.')))), dom.br(), dom.h2('Aliases/lists'), dom.table(dom.thead(dom.tr(dom.th('Address'), dom.th('Allowed senders', attr.title('Whether only members can send through the alias/list, or anyone.')), dom.th('Send as alias address', attr.title('If enabled, messages can be sent with the alias address in the message "From" header.')), dom.th('Members visible', attr.title('If enabled, members can see the addresses of other members.')))), Object.values(localpartAliases).length === 0 ? dom.tr(dom.td(attr.colspan('4'), 'None')) : [], Object.values(localpartAliases).sort((a, b) => a.LocalpartStr < b.LocalpartStr ? -1 : 1).map(a => {
}, addrFieldset = dom.fieldset(dom.label(style({ display: 'inline-block' }), dom.span('Localpart', attr.title('The localpart is the part before the "@"-sign of an address. An empty localpart is the catchall destination/address for the domain.')), dom.br(), addrLocalpart = dom.input()), '@', domainName(dnsdomain), ' ', dom.label(style({ display: 'inline-block' }), dom.span('Account', attr.title('Account to assign the address to.')), dom.br(), addrAccount = dom.select(attr.required(''), (accounts || []).map(a => dom.option(a)))), ' ', dom.submitbutton('Add address', attr.title('Address will be added and the config reloaded.')))), dom.br(), dom.h2('Aliases (lists)'), dom.table(dom.thead(dom.tr(dom.th('Address'), dom.th('Allowed senders', attr.title('Whether only members can send through the alias/list, or anyone.')), dom.th('Send as alias address', attr.title('If enabled, messages can be sent with the alias address in the message "From" header.')), dom.th('Members visible', attr.title('If enabled, members can see the addresses of other members.')))), Object.values(localpartAliases).length === 0 ? dom.tr(dom.td(attr.colspan('4'), 'None')) : [], Object.values(localpartAliases).sort((a, b) => a.LocalpartStr < b.LocalpartStr ? -1 : 1).map(a => {
return dom.tr(dom.td(dom.a(prewrap(a.LocalpartStr), attr.href('#domains/' + d + '/alias/' + encodeURIComponent(a.LocalpartStr)))), dom.td(a.PostPublic ? 'Anyone' : 'Members only'), dom.td(a.AllowMsgFrom ? 'Yes' : 'No'), dom.td(a.ListMembers ? 'Yes' : 'No'));
})), dom.br(), dom.h2('Add alias'), dom.form(async function submit(e) {
})), dom.br(), dom.h2('Add alias (list)'), dom.form(async function submit(e) {
e.preventDefault();
e.stopPropagation();
const alias = {
Expand All @@ -2380,7 +2381,10 @@ const domain = async (d) => {
};
await check(aliasFieldset, client.AliasAdd(aliasLocalpart.value, d, alias));
window.location.hash = '#domains/' + d + '/alias/' + encodeURIComponent(aliasLocalpart.value);
}, aliasFieldset = dom.fieldset(style({ display: 'flex', alignItems: 'flex-start', gap: '1em' }), dom.label(dom.div('Localpart', attr.title('The localpart is the part before the "@"-sign of an address.')), aliasLocalpart = dom.input(attr.required('')), '@', domainName(dnsdomain), ' '), dom.label(dom.div('Addresses', attr.title('One members address per line, full address of form localpart@domain. At least one address required.')), aliasAddresses = dom.textarea(attr.required(''), attr.rows('1'), function focus() { aliasAddresses.setAttribute('rows', '5'); })), dom.div(dom.div('\u00a0'), dom.submitbutton('Add alias', attr.title('Alias will be added and the config reloaded.'))))), dom.br(), RoutesEditor('domain-specific', transports, domainConfig.Routes || [], async (routes) => await client.DomainRoutesSave(d, routes)), dom.br(), dom.h2('Settings'), dom.form(async function submit(e) {
}, aliasFieldset = dom.fieldset(style({ display: 'flex', alignItems: 'flex-start', gap: '1em' }), dom.label(dom.div('Localpart', attr.title('The localpart is the part before the "@"-sign of an address.')), aliasLocalpart = dom.input(attr.required('')), '@', domainName(dnsdomain), ' '), dom.label(dom.div('Addresses', attr.title('One members address per line, full address of form localpart@domain. At least one address required.')), aliasAddresses = dom.textarea(attr.required(''), attr.rows('1'), function focus() {
aliasAddresses.setAttribute('rows', '5');
aliasAddText.style.visibility = 'visible';
})), dom.div(dom.div('\u00a0'), dom.submitbutton('Add alias', attr.title('Alias will be added and the config reloaded.')), aliasAddText = dom.p(style({ visibility: 'hidden', fontStyle: 'italic' }), 'Messages sent to aliases are delivered to each member address of the alias, like a mailing list. For an additional address for an account, add it as regular address (see above).')))), dom.br(), RoutesEditor('domain-specific', transports, domainConfig.Routes || [], async (routes) => await client.DomainRoutesSave(d, routes)), dom.br(), dom.h2('Settings'), dom.form(async function submit(e) {
e.preventDefault();
e.stopPropagation();
await check(descrFieldset, client.DomainDescriptionSave(d, descrText.value));
Expand Down
13 changes: 9 additions & 4 deletions webadmin/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ const account = async (name: string) => {
),
dom.br(),

dom.h2('Aliases/lists'),
dom.h2('Alias (list) membership'),
dom.table(
dom.thead(
dom.tr(
Expand Down Expand Up @@ -1094,6 +1094,7 @@ const domain = async (d: string) => {
let aliasFieldset: HTMLFieldSetElement
let aliasLocalpart: HTMLInputElement
let aliasAddresses: HTMLTextAreaElement
let aliasAddText: HTMLElement

let descrFieldset: HTMLFieldSetElement
let descrText: HTMLInputElement
Expand Down Expand Up @@ -1347,7 +1348,7 @@ const domain = async (d: string) => {
),
dom.br(),

dom.h2('Aliases/lists'),
dom.h2('Aliases (lists)'),
dom.table(
dom.thead(
dom.tr(
Expand All @@ -1368,7 +1369,7 @@ const domain = async (d: string) => {
}),
),
dom.br(),
dom.h2('Add alias'),
dom.h2('Add alias (list)'),
dom.form(
async function submit(e: SubmitEvent) {
e.preventDefault()
Expand All @@ -1395,11 +1396,15 @@ const domain = async (d: string) => {
),
dom.label(
dom.div('Addresses', attr.title('One members address per line, full address of form localpart@domain. At least one address required.')),
aliasAddresses=dom.textarea(attr.required(''), attr.rows('1'), function focus() { aliasAddresses.setAttribute('rows', '5') }),
aliasAddresses=dom.textarea(attr.required(''), attr.rows('1'), function focus() {
aliasAddresses.setAttribute('rows', '5')
aliasAddText.style.visibility = 'visible'
}),
),
dom.div(
dom.div('\u00a0'),
dom.submitbutton('Add alias', attr.title('Alias will be added and the config reloaded.')),
aliasAddText=dom.p(style({visibility: 'hidden', fontStyle: 'italic'}), 'Messages sent to aliases are delivered to each member address of the alias, like a mailing list. For an additional address for an account, add it as regular address (see above).'),
),
),
),
Expand Down

0 comments on commit cbe418e

Please sign in to comment.