mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 17:44:35 +03:00
add aliases/lists: when sending to an alias, the message gets delivered to all members
the members must currently all be addresses of local accounts. a message sent to an alias is accepted if at least one of the members accepts it. if no members accepts it (e.g. due to bad reputation of sender), the message is rejected. if a message is submitted to both an alias addresses and to recipients that are members of the alias in an smtp transaction, the message will be delivered to such members only once. the same applies if the address in the message from-header is the address of a member: that member won't receive the message (they sent it). this prevents duplicate messages. aliases have three configuration options: - PostPublic: whether anyone can send through the alias, or only members. members-only lists can be useful inside organizations for internal communication. public lists can be useful for support addresses. - ListMembers: whether members can see the addresses of other members. this can be seen in the account web interface. in the future, we could export this in other ways, so clients can expand the list. - AllowMsgFrom: whether messages can be sent through the alias with the alias address used in the message from-header. the webmail knows it can use that address, and will use it as from-address when replying to a message sent to that address. ideas for the future: - allow external addresses as members. still with some restrictions, such as requiring a valid dkim-signature so delivery has a chance to succeed. will also need configuration of an admin that can receive any bounces. - allow specifying specific members who can sent through the list (instead of all members). for github issue #57 by hmfaysal. also relevant for #99 by naturalethic. thanks to damir & marin from sartura for discussing requirements/features.
This commit is contained in:
@ -255,11 +255,11 @@ var api;
|
||||
// per-outgoing-message address used for sending.
|
||||
OutgoingEvent["EventUnrecognized"] = "unrecognized";
|
||||
})(OutgoingEvent = api.OutgoingEvent || (api.OutgoingEvent = {}));
|
||||
api.structTypes = { "Account": true, "AutomaticJunkFlags": true, "Destination": true, "Domain": true, "ImportProgress": true, "Incoming": true, "IncomingMeta": true, "IncomingWebhook": true, "JunkFilter": true, "NameAddress": true, "Outgoing": true, "OutgoingWebhook": true, "Route": true, "Ruleset": true, "Structure": true, "SubjectPass": true, "Suppression": true };
|
||||
api.stringsTypes = { "CSRFToken": true, "OutgoingEvent": true };
|
||||
api.structTypes = { "Account": true, "Address": true, "AddressAlias": true, "Alias": true, "AliasAddress": true, "AutomaticJunkFlags": true, "Destination": true, "Domain": true, "ImportProgress": true, "Incoming": true, "IncomingMeta": true, "IncomingWebhook": true, "JunkFilter": true, "NameAddress": true, "Outgoing": true, "OutgoingWebhook": true, "Route": true, "Ruleset": true, "Structure": true, "SubjectPass": true, "Suppression": true };
|
||||
api.stringsTypes = { "CSRFToken": true, "Localpart": true, "OutgoingEvent": true };
|
||||
api.intsTypes = {};
|
||||
api.types = {
|
||||
"Account": { "Name": "Account", "Docs": "", "Fields": [{ "Name": "OutgoingWebhook", "Docs": "", "Typewords": ["nullable", "OutgoingWebhook"] }, { "Name": "IncomingWebhook", "Docs": "", "Typewords": ["nullable", "IncomingWebhook"] }, { "Name": "FromIDLoginAddresses", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "KeepRetiredMessagePeriod", "Docs": "", "Typewords": ["int64"] }, { "Name": "KeepRetiredWebhookPeriod", "Docs": "", "Typewords": ["int64"] }, { "Name": "Domain", "Docs": "", "Typewords": ["string"] }, { "Name": "Description", "Docs": "", "Typewords": ["string"] }, { "Name": "FullName", "Docs": "", "Typewords": ["string"] }, { "Name": "Destinations", "Docs": "", "Typewords": ["{}", "Destination"] }, { "Name": "SubjectPass", "Docs": "", "Typewords": ["SubjectPass"] }, { "Name": "QuotaMessageSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "KeepRejects", "Docs": "", "Typewords": ["bool"] }, { "Name": "AutomaticJunkFlags", "Docs": "", "Typewords": ["AutomaticJunkFlags"] }, { "Name": "JunkFilter", "Docs": "", "Typewords": ["nullable", "JunkFilter"] }, { "Name": "MaxOutgoingMessagesPerDay", "Docs": "", "Typewords": ["int32"] }, { "Name": "MaxFirstTimeRecipientsPerDay", "Docs": "", "Typewords": ["int32"] }, { "Name": "NoFirstTimeSenderDelay", "Docs": "", "Typewords": ["bool"] }, { "Name": "Routes", "Docs": "", "Typewords": ["[]", "Route"] }, { "Name": "DNSDomain", "Docs": "", "Typewords": ["Domain"] }] },
|
||||
"Account": { "Name": "Account", "Docs": "", "Fields": [{ "Name": "OutgoingWebhook", "Docs": "", "Typewords": ["nullable", "OutgoingWebhook"] }, { "Name": "IncomingWebhook", "Docs": "", "Typewords": ["nullable", "IncomingWebhook"] }, { "Name": "FromIDLoginAddresses", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "KeepRetiredMessagePeriod", "Docs": "", "Typewords": ["int64"] }, { "Name": "KeepRetiredWebhookPeriod", "Docs": "", "Typewords": ["int64"] }, { "Name": "Domain", "Docs": "", "Typewords": ["string"] }, { "Name": "Description", "Docs": "", "Typewords": ["string"] }, { "Name": "FullName", "Docs": "", "Typewords": ["string"] }, { "Name": "Destinations", "Docs": "", "Typewords": ["{}", "Destination"] }, { "Name": "SubjectPass", "Docs": "", "Typewords": ["SubjectPass"] }, { "Name": "QuotaMessageSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "KeepRejects", "Docs": "", "Typewords": ["bool"] }, { "Name": "AutomaticJunkFlags", "Docs": "", "Typewords": ["AutomaticJunkFlags"] }, { "Name": "JunkFilter", "Docs": "", "Typewords": ["nullable", "JunkFilter"] }, { "Name": "MaxOutgoingMessagesPerDay", "Docs": "", "Typewords": ["int32"] }, { "Name": "MaxFirstTimeRecipientsPerDay", "Docs": "", "Typewords": ["int32"] }, { "Name": "NoFirstTimeSenderDelay", "Docs": "", "Typewords": ["bool"] }, { "Name": "Routes", "Docs": "", "Typewords": ["[]", "Route"] }, { "Name": "DNSDomain", "Docs": "", "Typewords": ["Domain"] }, { "Name": "Aliases", "Docs": "", "Typewords": ["[]", "AddressAlias"] }] },
|
||||
"OutgoingWebhook": { "Name": "OutgoingWebhook", "Docs": "", "Fields": [{ "Name": "URL", "Docs": "", "Typewords": ["string"] }, { "Name": "Authorization", "Docs": "", "Typewords": ["string"] }, { "Name": "Events", "Docs": "", "Typewords": ["[]", "string"] }] },
|
||||
"IncomingWebhook": { "Name": "IncomingWebhook", "Docs": "", "Fields": [{ "Name": "URL", "Docs": "", "Typewords": ["string"] }, { "Name": "Authorization", "Docs": "", "Typewords": ["string"] }] },
|
||||
"Destination": { "Name": "Destination", "Docs": "", "Fields": [{ "Name": "Mailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Rulesets", "Docs": "", "Typewords": ["[]", "Ruleset"] }, { "Name": "FullName", "Docs": "", "Typewords": ["string"] }] },
|
||||
@ -269,6 +269,10 @@ var api;
|
||||
"AutomaticJunkFlags": { "Name": "AutomaticJunkFlags", "Docs": "", "Fields": [{ "Name": "Enabled", "Docs": "", "Typewords": ["bool"] }, { "Name": "JunkMailboxRegexp", "Docs": "", "Typewords": ["string"] }, { "Name": "NeutralMailboxRegexp", "Docs": "", "Typewords": ["string"] }, { "Name": "NotJunkMailboxRegexp", "Docs": "", "Typewords": ["string"] }] },
|
||||
"JunkFilter": { "Name": "JunkFilter", "Docs": "", "Fields": [{ "Name": "Threshold", "Docs": "", "Typewords": ["float64"] }, { "Name": "Onegrams", "Docs": "", "Typewords": ["bool"] }, { "Name": "Twograms", "Docs": "", "Typewords": ["bool"] }, { "Name": "Threegrams", "Docs": "", "Typewords": ["bool"] }, { "Name": "MaxPower", "Docs": "", "Typewords": ["float64"] }, { "Name": "TopWords", "Docs": "", "Typewords": ["int32"] }, { "Name": "IgnoreWords", "Docs": "", "Typewords": ["float64"] }, { "Name": "RareWords", "Docs": "", "Typewords": ["int32"] }] },
|
||||
"Route": { "Name": "Route", "Docs": "", "Fields": [{ "Name": "FromDomain", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "ToDomain", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "MinimumAttempts", "Docs": "", "Typewords": ["int32"] }, { "Name": "Transport", "Docs": "", "Typewords": ["string"] }, { "Name": "FromDomainASCII", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "ToDomainASCII", "Docs": "", "Typewords": ["[]", "string"] }] },
|
||||
"AddressAlias": { "Name": "AddressAlias", "Docs": "", "Fields": [{ "Name": "SubscriptionAddress", "Docs": "", "Typewords": ["string"] }, { "Name": "Alias", "Docs": "", "Typewords": ["Alias"] }, { "Name": "MemberAddresses", "Docs": "", "Typewords": ["[]", "string"] }] },
|
||||
"Alias": { "Name": "Alias", "Docs": "", "Fields": [{ "Name": "Addresses", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "PostPublic", "Docs": "", "Typewords": ["bool"] }, { "Name": "ListMembers", "Docs": "", "Typewords": ["bool"] }, { "Name": "AllowMsgFrom", "Docs": "", "Typewords": ["bool"] }, { "Name": "LocalpartStr", "Docs": "", "Typewords": ["string"] }, { "Name": "Domain", "Docs": "", "Typewords": ["Domain"] }, { "Name": "ParsedAddresses", "Docs": "", "Typewords": ["[]", "AliasAddress"] }] },
|
||||
"AliasAddress": { "Name": "AliasAddress", "Docs": "", "Fields": [{ "Name": "Address", "Docs": "", "Typewords": ["Address"] }, { "Name": "AccountName", "Docs": "", "Typewords": ["string"] }, { "Name": "Destination", "Docs": "", "Typewords": ["Destination"] }] },
|
||||
"Address": { "Name": "Address", "Docs": "", "Fields": [{ "Name": "Localpart", "Docs": "", "Typewords": ["Localpart"] }, { "Name": "Domain", "Docs": "", "Typewords": ["Domain"] }] },
|
||||
"Suppression": { "Name": "Suppression", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Created", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "Account", "Docs": "", "Typewords": ["string"] }, { "Name": "BaseAddress", "Docs": "", "Typewords": ["string"] }, { "Name": "OriginalAddress", "Docs": "", "Typewords": ["string"] }, { "Name": "Manual", "Docs": "", "Typewords": ["bool"] }, { "Name": "Reason", "Docs": "", "Typewords": ["string"] }] },
|
||||
"ImportProgress": { "Name": "ImportProgress", "Docs": "", "Fields": [{ "Name": "Token", "Docs": "", "Typewords": ["string"] }] },
|
||||
"Outgoing": { "Name": "Outgoing", "Docs": "", "Fields": [{ "Name": "Version", "Docs": "", "Typewords": ["int32"] }, { "Name": "Event", "Docs": "", "Typewords": ["OutgoingEvent"] }, { "Name": "DSN", "Docs": "", "Typewords": ["bool"] }, { "Name": "Suppressing", "Docs": "", "Typewords": ["bool"] }, { "Name": "QueueMsgID", "Docs": "", "Typewords": ["int64"] }, { "Name": "FromID", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "WebhookQueued", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "SMTPCode", "Docs": "", "Typewords": ["int32"] }, { "Name": "SMTPEnhancedCode", "Docs": "", "Typewords": ["string"] }, { "Name": "Error", "Docs": "", "Typewords": ["string"] }, { "Name": "Extra", "Docs": "", "Typewords": ["{}", "string"] }] },
|
||||
@ -277,6 +281,7 @@ var api;
|
||||
"Structure": { "Name": "Structure", "Docs": "", "Fields": [{ "Name": "ContentType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["string"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Structure"] }] },
|
||||
"IncomingMeta": { "Name": "IncomingMeta", "Docs": "", "Fields": [{ "Name": "MsgID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MailFrom", "Docs": "", "Typewords": ["string"] }, { "Name": "MailFromValidated", "Docs": "", "Typewords": ["bool"] }, { "Name": "MsgFromValidated", "Docs": "", "Typewords": ["bool"] }, { "Name": "RcptTo", "Docs": "", "Typewords": ["string"] }, { "Name": "DKIMVerifiedDomains", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "RemoteIP", "Docs": "", "Typewords": ["string"] }, { "Name": "Received", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Automated", "Docs": "", "Typewords": ["bool"] }] },
|
||||
"CSRFToken": { "Name": "CSRFToken", "Docs": "", "Values": null },
|
||||
"Localpart": { "Name": "Localpart", "Docs": "", "Values": null },
|
||||
"OutgoingEvent": { "Name": "OutgoingEvent", "Docs": "", "Values": [{ "Name": "EventDelivered", "Value": "delivered", "Docs": "" }, { "Name": "EventSuppressed", "Value": "suppressed", "Docs": "" }, { "Name": "EventDelayed", "Value": "delayed", "Docs": "" }, { "Name": "EventFailed", "Value": "failed", "Docs": "" }, { "Name": "EventRelayed", "Value": "relayed", "Docs": "" }, { "Name": "EventExpanded", "Value": "expanded", "Docs": "" }, { "Name": "EventCanceled", "Value": "canceled", "Docs": "" }, { "Name": "EventUnrecognized", "Value": "unrecognized", "Docs": "" }] },
|
||||
};
|
||||
api.parser = {
|
||||
@ -290,6 +295,10 @@ var api;
|
||||
AutomaticJunkFlags: (v) => api.parse("AutomaticJunkFlags", v),
|
||||
JunkFilter: (v) => api.parse("JunkFilter", v),
|
||||
Route: (v) => api.parse("Route", v),
|
||||
AddressAlias: (v) => api.parse("AddressAlias", v),
|
||||
Alias: (v) => api.parse("Alias", v),
|
||||
AliasAddress: (v) => api.parse("AliasAddress", v),
|
||||
Address: (v) => api.parse("Address", v),
|
||||
Suppression: (v) => api.parse("Suppression", v),
|
||||
ImportProgress: (v) => api.parse("ImportProgress", v),
|
||||
Outgoing: (v) => api.parse("Outgoing", v),
|
||||
@ -298,6 +307,7 @@ var api;
|
||||
Structure: (v) => api.parse("Structure", v),
|
||||
IncomingMeta: (v) => api.parse("IncomingMeta", v),
|
||||
CSRFToken: (v) => api.parse("CSRFToken", v),
|
||||
Localpart: (v) => api.parse("Localpart", v),
|
||||
OutgoingEvent: (v) => api.parse("OutgoingEvent", v),
|
||||
};
|
||||
// Account exports web API functions for the account web interface. All its
|
||||
@ -1371,7 +1381,10 @@ const index = async () => {
|
||||
await check(fullNameFieldset, client.AccountSaveFullName(fullName.value));
|
||||
fullName.setAttribute('value', fullName.value);
|
||||
fullNameForm.reset();
|
||||
}), dom.br(), dom.h2('Addresses'), dom.ul(Object.entries(acc.Destinations || {}).length === 0 ? dom.li('(None, login disabled)') : [], Object.entries(acc.Destinations || {}).sort().map(t => dom.li(dom.a(t[0], attr.href('#destinations/' + t[0])), t[0].startsWith('@') ? ' (catchall)' : []))), dom.br(), dom.h2('Change password'), passwordForm = dom.form(passwordFieldset = dom.fieldset(dom.label(style({ display: 'inline-block' }), 'New password', dom.br(), password1 = dom.input(attr.type('password'), attr.autocomplete('new-password'), attr.required(''), function focus() {
|
||||
}), dom.br(), dom.h2('Addresses'), dom.ul(Object.entries(acc.Destinations || {}).length === 0 ? dom.li('(None, login disabled)') : [], Object.entries(acc.Destinations || {}).sort().map(t => dom.li(dom.a(t[0], attr.href('#destinations/' + t[0])), t[0].startsWith('@') ? ' (catchall)' : []))), 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.')), dom.th('Subscription address', attr.title('Address subscribed to the alias/list.')), 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())), (acc.Aliases || []).length === 0 ? dom.tr(dom.td(attr.colspan('5'), 'None')) : [], (acc.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(a.Alias.LocalpartStr, '@', domainName(a.Alias.Domain)), dom.td(a.SubscriptionAddress), dom.td(a.Alias.PostPublic ? 'Anyone' : 'Members only'), dom.td(a.Alias.AllowMsgFrom ? 'Yes' : 'No'), dom.td((a.MemberAddresses || []).length === 0 ? [] :
|
||||
dom.clickbutton('Show members', function click() {
|
||||
popup(dom.h1('Members of alias ', a.Alias.LocalpartStr, '@', domainName(a.Alias.Domain)), dom.ul((a.MemberAddresses || []).map(addr => dom.li(addr))));
|
||||
}))))), dom.br(), dom.h2('Change password'), passwordForm = dom.form(passwordFieldset = dom.fieldset(dom.label(style({ display: 'inline-block' }), 'New password', dom.br(), password1 = dom.input(attr.type('password'), attr.autocomplete('new-password'), attr.required(''), function focus() {
|
||||
passwordHint.style.display = '';
|
||||
})), ' ', dom.label(style({ display: 'inline-block' }), 'New password repeat', dom.br(), password2 = dom.input(attr.type('password'), attr.autocomplete('new-password'), attr.required(''))), ' ', dom.submitbutton('Change password')), passwordHint = dom.div(style({ display: 'none', marginTop: '.5ex' }), dom.clickbutton('Generate random password', function click(e) {
|
||||
e.preventDefault();
|
||||
|
@ -765,6 +765,40 @@ const index = async () => {
|
||||
),
|
||||
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.')),
|
||||
dom.th('Subscription address', attr.title('Address subscribed to the alias/list.')),
|
||||
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(),
|
||||
),
|
||||
),
|
||||
(acc.Aliases || []).length === 0 ? dom.tr(dom.td(attr.colspan('5'), 'None')) : [],
|
||||
(acc.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(a.Alias.LocalpartStr, '@', domainName(a.Alias.Domain)),
|
||||
dom.td(a.SubscriptionAddress),
|
||||
dom.td(a.Alias.PostPublic ? 'Anyone' : 'Members only'),
|
||||
dom.td(a.Alias.AllowMsgFrom ? 'Yes' : 'No'),
|
||||
dom.td(
|
||||
(a.MemberAddresses || []).length === 0 ? [] :
|
||||
dom.clickbutton('Show members', function click() {
|
||||
popup(
|
||||
dom.h1('Members of alias ', a.Alias.LocalpartStr, '@', domainName(a.Alias.Domain)),
|
||||
dom.ul(
|
||||
(a.MemberAddresses || []).map(addr => dom.li(addr)),
|
||||
),
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
dom.br(),
|
||||
|
||||
dom.h2('Change password'),
|
||||
passwordForm=dom.form(
|
||||
passwordFieldset=dom.fieldset(
|
||||
|
@ -26,6 +26,8 @@ import (
|
||||
"github.com/mjl-/bstore"
|
||||
"github.com/mjl-/sherpa"
|
||||
|
||||
"github.com/mjl-/mox/config"
|
||||
"github.com/mjl-/mox/dns"
|
||||
"github.com/mjl-/mox/mlog"
|
||||
"github.com/mjl-/mox/mox-"
|
||||
"github.com/mjl-/mox/queue"
|
||||
@ -227,7 +229,20 @@ func TestAccount(t *testing.T) {
|
||||
|
||||
err = queue.Init() // For DB.
|
||||
tcheck(t, err, "queue init")
|
||||
|
||||
account, _, _, _ := api.Account(ctx)
|
||||
|
||||
// Check we don't see the alias member list.
|
||||
tcompare(t, len(account.Aliases), 1)
|
||||
tcompare(t, account.Aliases[0], config.AddressAlias{
|
||||
SubscriptionAddress: "mjl☺@mox.example",
|
||||
Alias: config.Alias{
|
||||
LocalpartStr: "support",
|
||||
Domain: dns.Domain{ASCII: "mox.example"},
|
||||
AllowMsgFrom: true,
|
||||
},
|
||||
})
|
||||
|
||||
api.DestinationSave(ctx, "mjl☺@mox.example", account.Destinations["mjl☺@mox.example"], account.Destinations["mjl☺@mox.example"]) // todo: save modified value and compare it afterwards
|
||||
|
||||
api.AccountSaveFullName(ctx, account.FullName+" changed") // todo: check if value was changed
|
||||
|
@ -589,6 +589,14 @@
|
||||
"Typewords": [
|
||||
"Domain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Aliases",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"[]",
|
||||
"AddressAlias"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -933,6 +941,138 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "AddressAlias",
|
||||
"Docs": "",
|
||||
"Fields": [
|
||||
{
|
||||
"Name": "SubscriptionAddress",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Alias",
|
||||
"Docs": "Without members.",
|
||||
"Typewords": [
|
||||
"Alias"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "MemberAddresses",
|
||||
"Docs": "Only if allowed to see.",
|
||||
"Typewords": [
|
||||
"[]",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Alias",
|
||||
"Docs": "",
|
||||
"Fields": [
|
||||
{
|
||||
"Name": "Addresses",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"[]",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "PostPublic",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"bool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "ListMembers",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"bool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "AllowMsgFrom",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"bool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "LocalpartStr",
|
||||
"Docs": "In encoded form.",
|
||||
"Typewords": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Domain",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"Domain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "ParsedAddresses",
|
||||
"Docs": "Matches addresses.",
|
||||
"Typewords": [
|
||||
"[]",
|
||||
"AliasAddress"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "AliasAddress",
|
||||
"Docs": "",
|
||||
"Fields": [
|
||||
{
|
||||
"Name": "Address",
|
||||
"Docs": "Parsed address.",
|
||||
"Typewords": [
|
||||
"Address"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "AccountName",
|
||||
"Docs": "Looked up.",
|
||||
"Typewords": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Destination",
|
||||
"Docs": "Belonging to address.",
|
||||
"Typewords": [
|
||||
"Destination"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Address",
|
||||
"Docs": "Address is a parsed email address.",
|
||||
"Fields": [
|
||||
{
|
||||
"Name": "Localpart",
|
||||
"Docs": "",
|
||||
"Typewords": [
|
||||
"Localpart"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Domain",
|
||||
"Docs": "todo: shouldn't we accept an ip address here too? and merge this type into smtp.Path.",
|
||||
"Typewords": [
|
||||
"Domain"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Suppression",
|
||||
"Docs": "Suppression is an address to which messages will not be delivered. Attempts to\ndeliver or queue will result in an immediate permanent failure to deliver.",
|
||||
@ -1365,6 +1505,11 @@
|
||||
"Docs": "",
|
||||
"Values": null
|
||||
},
|
||||
{
|
||||
"Name": "Localpart",
|
||||
"Docs": "Localpart is a decoded local part of an email address, before the \"@\".\nFor quoted strings, values do not hold the double quote or escaping backslashes.\nAn empty string can be a valid localpart.\nLocalparts are in Unicode NFC.",
|
||||
"Values": null
|
||||
},
|
||||
{
|
||||
"Name": "OutgoingEvent",
|
||||
"Docs": "OutgoingEvent is an activity for an outgoing delivery. Either generated by the\nqueue, or through an incoming DSN (delivery status notification) message.",
|
||||
|
@ -23,6 +23,7 @@ export interface Account {
|
||||
NoFirstTimeSenderDelay: boolean
|
||||
Routes?: Route[] | null
|
||||
DNSDomain: Domain // Parsed form of Domain.
|
||||
Aliases?: AddressAlias[] | null
|
||||
}
|
||||
|
||||
export interface OutgoingWebhook {
|
||||
@ -96,6 +97,34 @@ export interface Route {
|
||||
ToDomainASCII?: string[] | null
|
||||
}
|
||||
|
||||
export interface AddressAlias {
|
||||
SubscriptionAddress: string
|
||||
Alias: Alias // Without members.
|
||||
MemberAddresses?: string[] | null // Only if allowed to see.
|
||||
}
|
||||
|
||||
export interface Alias {
|
||||
Addresses?: string[] | null
|
||||
PostPublic: boolean
|
||||
ListMembers: boolean
|
||||
AllowMsgFrom: boolean
|
||||
LocalpartStr: string // In encoded form.
|
||||
Domain: Domain
|
||||
ParsedAddresses?: AliasAddress[] | null // Matches addresses.
|
||||
}
|
||||
|
||||
export interface AliasAddress {
|
||||
Address: Address // Parsed address.
|
||||
AccountName: string // Looked up.
|
||||
Destination: Destination // Belonging to address.
|
||||
}
|
||||
|
||||
// Address is a parsed email address.
|
||||
export interface Address {
|
||||
Localpart: Localpart
|
||||
Domain: Domain // todo: shouldn't we accept an ip address here too? and merge this type into smtp.Path.
|
||||
}
|
||||
|
||||
// Suppression is an address to which messages will not be delivered. Attempts to
|
||||
// deliver or queue will result in an immediate permanent failure to deliver.
|
||||
export interface Suppression {
|
||||
@ -177,6 +206,12 @@ export interface IncomingMeta {
|
||||
|
||||
export type CSRFToken = string
|
||||
|
||||
// Localpart is a decoded local part of an email address, before the "@".
|
||||
// For quoted strings, values do not hold the double quote or escaping backslashes.
|
||||
// An empty string can be a valid localpart.
|
||||
// Localparts are in Unicode NFC.
|
||||
export type Localpart = string
|
||||
|
||||
// OutgoingEvent is an activity for an outgoing delivery. Either generated by the
|
||||
// queue, or through an incoming DSN (delivery status notification) message.
|
||||
export enum OutgoingEvent {
|
||||
@ -203,11 +238,11 @@ export enum OutgoingEvent {
|
||||
EventUnrecognized = "unrecognized",
|
||||
}
|
||||
|
||||
export const structTypes: {[typename: string]: boolean} = {"Account":true,"AutomaticJunkFlags":true,"Destination":true,"Domain":true,"ImportProgress":true,"Incoming":true,"IncomingMeta":true,"IncomingWebhook":true,"JunkFilter":true,"NameAddress":true,"Outgoing":true,"OutgoingWebhook":true,"Route":true,"Ruleset":true,"Structure":true,"SubjectPass":true,"Suppression":true}
|
||||
export const stringsTypes: {[typename: string]: boolean} = {"CSRFToken":true,"OutgoingEvent":true}
|
||||
export const structTypes: {[typename: string]: boolean} = {"Account":true,"Address":true,"AddressAlias":true,"Alias":true,"AliasAddress":true,"AutomaticJunkFlags":true,"Destination":true,"Domain":true,"ImportProgress":true,"Incoming":true,"IncomingMeta":true,"IncomingWebhook":true,"JunkFilter":true,"NameAddress":true,"Outgoing":true,"OutgoingWebhook":true,"Route":true,"Ruleset":true,"Structure":true,"SubjectPass":true,"Suppression":true}
|
||||
export const stringsTypes: {[typename: string]: boolean} = {"CSRFToken":true,"Localpart":true,"OutgoingEvent":true}
|
||||
export const intsTypes: {[typename: string]: boolean} = {}
|
||||
export const types: TypenameMap = {
|
||||
"Account": {"Name":"Account","Docs":"","Fields":[{"Name":"OutgoingWebhook","Docs":"","Typewords":["nullable","OutgoingWebhook"]},{"Name":"IncomingWebhook","Docs":"","Typewords":["nullable","IncomingWebhook"]},{"Name":"FromIDLoginAddresses","Docs":"","Typewords":["[]","string"]},{"Name":"KeepRetiredMessagePeriod","Docs":"","Typewords":["int64"]},{"Name":"KeepRetiredWebhookPeriod","Docs":"","Typewords":["int64"]},{"Name":"Domain","Docs":"","Typewords":["string"]},{"Name":"Description","Docs":"","Typewords":["string"]},{"Name":"FullName","Docs":"","Typewords":["string"]},{"Name":"Destinations","Docs":"","Typewords":["{}","Destination"]},{"Name":"SubjectPass","Docs":"","Typewords":["SubjectPass"]},{"Name":"QuotaMessageSize","Docs":"","Typewords":["int64"]},{"Name":"RejectsMailbox","Docs":"","Typewords":["string"]},{"Name":"KeepRejects","Docs":"","Typewords":["bool"]},{"Name":"AutomaticJunkFlags","Docs":"","Typewords":["AutomaticJunkFlags"]},{"Name":"JunkFilter","Docs":"","Typewords":["nullable","JunkFilter"]},{"Name":"MaxOutgoingMessagesPerDay","Docs":"","Typewords":["int32"]},{"Name":"MaxFirstTimeRecipientsPerDay","Docs":"","Typewords":["int32"]},{"Name":"NoFirstTimeSenderDelay","Docs":"","Typewords":["bool"]},{"Name":"Routes","Docs":"","Typewords":["[]","Route"]},{"Name":"DNSDomain","Docs":"","Typewords":["Domain"]}]},
|
||||
"Account": {"Name":"Account","Docs":"","Fields":[{"Name":"OutgoingWebhook","Docs":"","Typewords":["nullable","OutgoingWebhook"]},{"Name":"IncomingWebhook","Docs":"","Typewords":["nullable","IncomingWebhook"]},{"Name":"FromIDLoginAddresses","Docs":"","Typewords":["[]","string"]},{"Name":"KeepRetiredMessagePeriod","Docs":"","Typewords":["int64"]},{"Name":"KeepRetiredWebhookPeriod","Docs":"","Typewords":["int64"]},{"Name":"Domain","Docs":"","Typewords":["string"]},{"Name":"Description","Docs":"","Typewords":["string"]},{"Name":"FullName","Docs":"","Typewords":["string"]},{"Name":"Destinations","Docs":"","Typewords":["{}","Destination"]},{"Name":"SubjectPass","Docs":"","Typewords":["SubjectPass"]},{"Name":"QuotaMessageSize","Docs":"","Typewords":["int64"]},{"Name":"RejectsMailbox","Docs":"","Typewords":["string"]},{"Name":"KeepRejects","Docs":"","Typewords":["bool"]},{"Name":"AutomaticJunkFlags","Docs":"","Typewords":["AutomaticJunkFlags"]},{"Name":"JunkFilter","Docs":"","Typewords":["nullable","JunkFilter"]},{"Name":"MaxOutgoingMessagesPerDay","Docs":"","Typewords":["int32"]},{"Name":"MaxFirstTimeRecipientsPerDay","Docs":"","Typewords":["int32"]},{"Name":"NoFirstTimeSenderDelay","Docs":"","Typewords":["bool"]},{"Name":"Routes","Docs":"","Typewords":["[]","Route"]},{"Name":"DNSDomain","Docs":"","Typewords":["Domain"]},{"Name":"Aliases","Docs":"","Typewords":["[]","AddressAlias"]}]},
|
||||
"OutgoingWebhook": {"Name":"OutgoingWebhook","Docs":"","Fields":[{"Name":"URL","Docs":"","Typewords":["string"]},{"Name":"Authorization","Docs":"","Typewords":["string"]},{"Name":"Events","Docs":"","Typewords":["[]","string"]}]},
|
||||
"IncomingWebhook": {"Name":"IncomingWebhook","Docs":"","Fields":[{"Name":"URL","Docs":"","Typewords":["string"]},{"Name":"Authorization","Docs":"","Typewords":["string"]}]},
|
||||
"Destination": {"Name":"Destination","Docs":"","Fields":[{"Name":"Mailbox","Docs":"","Typewords":["string"]},{"Name":"Rulesets","Docs":"","Typewords":["[]","Ruleset"]},{"Name":"FullName","Docs":"","Typewords":["string"]}]},
|
||||
@ -217,6 +252,10 @@ export const types: TypenameMap = {
|
||||
"AutomaticJunkFlags": {"Name":"AutomaticJunkFlags","Docs":"","Fields":[{"Name":"Enabled","Docs":"","Typewords":["bool"]},{"Name":"JunkMailboxRegexp","Docs":"","Typewords":["string"]},{"Name":"NeutralMailboxRegexp","Docs":"","Typewords":["string"]},{"Name":"NotJunkMailboxRegexp","Docs":"","Typewords":["string"]}]},
|
||||
"JunkFilter": {"Name":"JunkFilter","Docs":"","Fields":[{"Name":"Threshold","Docs":"","Typewords":["float64"]},{"Name":"Onegrams","Docs":"","Typewords":["bool"]},{"Name":"Twograms","Docs":"","Typewords":["bool"]},{"Name":"Threegrams","Docs":"","Typewords":["bool"]},{"Name":"MaxPower","Docs":"","Typewords":["float64"]},{"Name":"TopWords","Docs":"","Typewords":["int32"]},{"Name":"IgnoreWords","Docs":"","Typewords":["float64"]},{"Name":"RareWords","Docs":"","Typewords":["int32"]}]},
|
||||
"Route": {"Name":"Route","Docs":"","Fields":[{"Name":"FromDomain","Docs":"","Typewords":["[]","string"]},{"Name":"ToDomain","Docs":"","Typewords":["[]","string"]},{"Name":"MinimumAttempts","Docs":"","Typewords":["int32"]},{"Name":"Transport","Docs":"","Typewords":["string"]},{"Name":"FromDomainASCII","Docs":"","Typewords":["[]","string"]},{"Name":"ToDomainASCII","Docs":"","Typewords":["[]","string"]}]},
|
||||
"AddressAlias": {"Name":"AddressAlias","Docs":"","Fields":[{"Name":"SubscriptionAddress","Docs":"","Typewords":["string"]},{"Name":"Alias","Docs":"","Typewords":["Alias"]},{"Name":"MemberAddresses","Docs":"","Typewords":["[]","string"]}]},
|
||||
"Alias": {"Name":"Alias","Docs":"","Fields":[{"Name":"Addresses","Docs":"","Typewords":["[]","string"]},{"Name":"PostPublic","Docs":"","Typewords":["bool"]},{"Name":"ListMembers","Docs":"","Typewords":["bool"]},{"Name":"AllowMsgFrom","Docs":"","Typewords":["bool"]},{"Name":"LocalpartStr","Docs":"","Typewords":["string"]},{"Name":"Domain","Docs":"","Typewords":["Domain"]},{"Name":"ParsedAddresses","Docs":"","Typewords":["[]","AliasAddress"]}]},
|
||||
"AliasAddress": {"Name":"AliasAddress","Docs":"","Fields":[{"Name":"Address","Docs":"","Typewords":["Address"]},{"Name":"AccountName","Docs":"","Typewords":["string"]},{"Name":"Destination","Docs":"","Typewords":["Destination"]}]},
|
||||
"Address": {"Name":"Address","Docs":"","Fields":[{"Name":"Localpart","Docs":"","Typewords":["Localpart"]},{"Name":"Domain","Docs":"","Typewords":["Domain"]}]},
|
||||
"Suppression": {"Name":"Suppression","Docs":"","Fields":[{"Name":"ID","Docs":"","Typewords":["int64"]},{"Name":"Created","Docs":"","Typewords":["timestamp"]},{"Name":"Account","Docs":"","Typewords":["string"]},{"Name":"BaseAddress","Docs":"","Typewords":["string"]},{"Name":"OriginalAddress","Docs":"","Typewords":["string"]},{"Name":"Manual","Docs":"","Typewords":["bool"]},{"Name":"Reason","Docs":"","Typewords":["string"]}]},
|
||||
"ImportProgress": {"Name":"ImportProgress","Docs":"","Fields":[{"Name":"Token","Docs":"","Typewords":["string"]}]},
|
||||
"Outgoing": {"Name":"Outgoing","Docs":"","Fields":[{"Name":"Version","Docs":"","Typewords":["int32"]},{"Name":"Event","Docs":"","Typewords":["OutgoingEvent"]},{"Name":"DSN","Docs":"","Typewords":["bool"]},{"Name":"Suppressing","Docs":"","Typewords":["bool"]},{"Name":"QueueMsgID","Docs":"","Typewords":["int64"]},{"Name":"FromID","Docs":"","Typewords":["string"]},{"Name":"MessageID","Docs":"","Typewords":["string"]},{"Name":"Subject","Docs":"","Typewords":["string"]},{"Name":"WebhookQueued","Docs":"","Typewords":["timestamp"]},{"Name":"SMTPCode","Docs":"","Typewords":["int32"]},{"Name":"SMTPEnhancedCode","Docs":"","Typewords":["string"]},{"Name":"Error","Docs":"","Typewords":["string"]},{"Name":"Extra","Docs":"","Typewords":["{}","string"]}]},
|
||||
@ -225,6 +264,7 @@ export const types: TypenameMap = {
|
||||
"Structure": {"Name":"Structure","Docs":"","Fields":[{"Name":"ContentType","Docs":"","Typewords":["string"]},{"Name":"ContentTypeParams","Docs":"","Typewords":["{}","string"]},{"Name":"ContentID","Docs":"","Typewords":["string"]},{"Name":"DecodedSize","Docs":"","Typewords":["int64"]},{"Name":"Parts","Docs":"","Typewords":["[]","Structure"]}]},
|
||||
"IncomingMeta": {"Name":"IncomingMeta","Docs":"","Fields":[{"Name":"MsgID","Docs":"","Typewords":["int64"]},{"Name":"MailFrom","Docs":"","Typewords":["string"]},{"Name":"MailFromValidated","Docs":"","Typewords":["bool"]},{"Name":"MsgFromValidated","Docs":"","Typewords":["bool"]},{"Name":"RcptTo","Docs":"","Typewords":["string"]},{"Name":"DKIMVerifiedDomains","Docs":"","Typewords":["[]","string"]},{"Name":"RemoteIP","Docs":"","Typewords":["string"]},{"Name":"Received","Docs":"","Typewords":["timestamp"]},{"Name":"MailboxName","Docs":"","Typewords":["string"]},{"Name":"Automated","Docs":"","Typewords":["bool"]}]},
|
||||
"CSRFToken": {"Name":"CSRFToken","Docs":"","Values":null},
|
||||
"Localpart": {"Name":"Localpart","Docs":"","Values":null},
|
||||
"OutgoingEvent": {"Name":"OutgoingEvent","Docs":"","Values":[{"Name":"EventDelivered","Value":"delivered","Docs":""},{"Name":"EventSuppressed","Value":"suppressed","Docs":""},{"Name":"EventDelayed","Value":"delayed","Docs":""},{"Name":"EventFailed","Value":"failed","Docs":""},{"Name":"EventRelayed","Value":"relayed","Docs":""},{"Name":"EventExpanded","Value":"expanded","Docs":""},{"Name":"EventCanceled","Value":"canceled","Docs":""},{"Name":"EventUnrecognized","Value":"unrecognized","Docs":""}]},
|
||||
}
|
||||
|
||||
@ -239,6 +279,10 @@ export const parser = {
|
||||
AutomaticJunkFlags: (v: any) => parse("AutomaticJunkFlags", v) as AutomaticJunkFlags,
|
||||
JunkFilter: (v: any) => parse("JunkFilter", v) as JunkFilter,
|
||||
Route: (v: any) => parse("Route", v) as Route,
|
||||
AddressAlias: (v: any) => parse("AddressAlias", v) as AddressAlias,
|
||||
Alias: (v: any) => parse("Alias", v) as Alias,
|
||||
AliasAddress: (v: any) => parse("AliasAddress", v) as AliasAddress,
|
||||
Address: (v: any) => parse("Address", v) as Address,
|
||||
Suppression: (v: any) => parse("Suppression", v) as Suppression,
|
||||
ImportProgress: (v: any) => parse("ImportProgress", v) as ImportProgress,
|
||||
Outgoing: (v: any) => parse("Outgoing", v) as Outgoing,
|
||||
@ -247,6 +291,7 @@ export const parser = {
|
||||
Structure: (v: any) => parse("Structure", v) as Structure,
|
||||
IncomingMeta: (v: any) => parse("IncomingMeta", v) as IncomingMeta,
|
||||
CSRFToken: (v: any) => parse("CSRFToken", v) as CSRFToken,
|
||||
Localpart: (v: any) => parse("Localpart", v) as Localpart,
|
||||
OutgoingEvent: (v: any) => parse("OutgoingEvent", v) as OutgoingEvent,
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user