From 8a1d81c29a0ae461c4a7fe8dd6b1d147810785b0 Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Fri, 19 Apr 2024 17:44:31 +0200 Subject: [PATCH] webmail: show link to webaccount interface in top right only if account web interface is enabled on the same listener and same http/https scheme. --- http/web.go | 18 ++++++++++++++++-- webmail/api.json | 7 +++++++ webmail/api.ts | 3 ++- webmail/msg.js | 2 +- webmail/text.js | 2 +- webmail/view.go | 5 +++-- webmail/view_test.go | 2 +- webmail/webmail.go | 8 ++++---- webmail/webmail.js | 8 +++++--- webmail/webmail.ts | 6 +++++- webmail/webmail_test.go | 2 +- 11 files changed, 46 insertions(+), 17 deletions(-) diff --git a/http/web.go b/http/web.go index eb880b8..ced1f3a 100644 --- a/http/web.go +++ b/http/web.go @@ -609,7 +609,14 @@ func Listen() { path = l.WebmailHTTP.Path } srv := ensureServe(false, port, "webmail-http at "+path) - handler := http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webmail.Handler(maxMsgSize, path, l.WebmailHTTP.Forwarded))) + var accountPath string + if l.AccountHTTP.Enabled { + accountPath = "/" + if l.AccountHTTP.Path != "" { + accountPath = l.AccountHTTP.Path + } + } + handler := http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webmail.Handler(maxMsgSize, path, l.WebmailHTTP.Forwarded, accountPath))) srv.Handle("webmail", nil, path, handler) redirectToTrailingSlash(srv, "webmail", path) } @@ -620,7 +627,14 @@ func Listen() { path = l.WebmailHTTPS.Path } srv := ensureServe(true, port, "webmail-https at "+path) - handler := http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webmail.Handler(maxMsgSize, path, l.WebmailHTTPS.Forwarded))) + var accountPath string + if l.AccountHTTPS.Enabled { + accountPath = "/" + if l.AccountHTTPS.Path != "" { + accountPath = l.AccountHTTPS.Path + } + } + handler := http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webmail.Handler(maxMsgSize, path, l.WebmailHTTPS.Forwarded, accountPath))) srv.Handle("webmail", nil, path, handler) redirectToTrailingSlash(srv, "webmail", path) } diff --git a/webmail/api.json b/webmail/api.json index 29eac2b..a5952d0 100644 --- a/webmail/api.json +++ b/webmail/api.json @@ -1492,6 +1492,13 @@ "Settings" ] }, + { + "Name": "AccountPath", + "Docs": "If nonempty, the path on same host to webaccount interface.", + "Typewords": [ + "string" + ] + }, { "Name": "Version", "Docs": "", diff --git a/webmail/api.ts b/webmail/api.ts index 92bb65b..bd8d655 100644 --- a/webmail/api.ts +++ b/webmail/api.ts @@ -210,6 +210,7 @@ export interface EventStart { Mailboxes?: Mailbox[] | null RejectsMailbox: string Settings: Settings + AccountPath: string // If nonempty, the path on same host to webaccount interface. Version: string } @@ -556,7 +557,7 @@ export const types: TypenameMap = { "Mailbox": {"Name":"Mailbox","Docs":"","Fields":[{"Name":"ID","Docs":"","Typewords":["int64"]},{"Name":"Name","Docs":"","Typewords":["string"]},{"Name":"UIDValidity","Docs":"","Typewords":["uint32"]},{"Name":"UIDNext","Docs":"","Typewords":["UID"]},{"Name":"Archive","Docs":"","Typewords":["bool"]},{"Name":"Draft","Docs":"","Typewords":["bool"]},{"Name":"Junk","Docs":"","Typewords":["bool"]},{"Name":"Sent","Docs":"","Typewords":["bool"]},{"Name":"Trash","Docs":"","Typewords":["bool"]},{"Name":"Keywords","Docs":"","Typewords":["[]","string"]},{"Name":"HaveCounts","Docs":"","Typewords":["bool"]},{"Name":"Total","Docs":"","Typewords":["int64"]},{"Name":"Deleted","Docs":"","Typewords":["int64"]},{"Name":"Unread","Docs":"","Typewords":["int64"]},{"Name":"Unseen","Docs":"","Typewords":["int64"]},{"Name":"Size","Docs":"","Typewords":["int64"]}]}, "RecipientSecurity": {"Name":"RecipientSecurity","Docs":"","Fields":[{"Name":"STARTTLS","Docs":"","Typewords":["SecurityResult"]},{"Name":"MTASTS","Docs":"","Typewords":["SecurityResult"]},{"Name":"DNSSEC","Docs":"","Typewords":["SecurityResult"]},{"Name":"DANE","Docs":"","Typewords":["SecurityResult"]},{"Name":"RequireTLS","Docs":"","Typewords":["SecurityResult"]}]}, "Settings": {"Name":"Settings","Docs":"","Fields":[{"Name":"ID","Docs":"","Typewords":["uint8"]},{"Name":"Signature","Docs":"","Typewords":["string"]},{"Name":"Quoting","Docs":"","Typewords":["Quoting"]},{"Name":"ShowAddressSecurity","Docs":"","Typewords":["bool"]}]}, - "EventStart": {"Name":"EventStart","Docs":"","Fields":[{"Name":"SSEID","Docs":"","Typewords":["int64"]},{"Name":"LoginAddress","Docs":"","Typewords":["MessageAddress"]},{"Name":"Addresses","Docs":"","Typewords":["[]","MessageAddress"]},{"Name":"DomainAddressConfigs","Docs":"","Typewords":["{}","DomainAddressConfig"]},{"Name":"MailboxName","Docs":"","Typewords":["string"]},{"Name":"Mailboxes","Docs":"","Typewords":["[]","Mailbox"]},{"Name":"RejectsMailbox","Docs":"","Typewords":["string"]},{"Name":"Settings","Docs":"","Typewords":["Settings"]},{"Name":"Version","Docs":"","Typewords":["string"]}]}, + "EventStart": {"Name":"EventStart","Docs":"","Fields":[{"Name":"SSEID","Docs":"","Typewords":["int64"]},{"Name":"LoginAddress","Docs":"","Typewords":["MessageAddress"]},{"Name":"Addresses","Docs":"","Typewords":["[]","MessageAddress"]},{"Name":"DomainAddressConfigs","Docs":"","Typewords":["{}","DomainAddressConfig"]},{"Name":"MailboxName","Docs":"","Typewords":["string"]},{"Name":"Mailboxes","Docs":"","Typewords":["[]","Mailbox"]},{"Name":"RejectsMailbox","Docs":"","Typewords":["string"]},{"Name":"Settings","Docs":"","Typewords":["Settings"]},{"Name":"AccountPath","Docs":"","Typewords":["string"]},{"Name":"Version","Docs":"","Typewords":["string"]}]}, "DomainAddressConfig": {"Name":"DomainAddressConfig","Docs":"","Fields":[{"Name":"LocalpartCatchallSeparator","Docs":"","Typewords":["string"]},{"Name":"LocalpartCaseSensitive","Docs":"","Typewords":["bool"]}]}, "EventViewErr": {"Name":"EventViewErr","Docs":"","Fields":[{"Name":"ViewID","Docs":"","Typewords":["int64"]},{"Name":"RequestID","Docs":"","Typewords":["int64"]},{"Name":"Err","Docs":"","Typewords":["string"]}]}, "EventViewReset": {"Name":"EventViewReset","Docs":"","Fields":[{"Name":"ViewID","Docs":"","Typewords":["int64"]},{"Name":"RequestID","Docs":"","Typewords":["int64"]}]}, diff --git a/webmail/msg.js b/webmail/msg.js index 2d18f38..0519685 100644 --- a/webmail/msg.js +++ b/webmail/msg.js @@ -302,7 +302,7 @@ var api; "Mailbox": { "Name": "Mailbox", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "UIDValidity", "Docs": "", "Typewords": ["uint32"] }, { "Name": "UIDNext", "Docs": "", "Typewords": ["UID"] }, { "Name": "Archive", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Sent", "Docs": "", "Typewords": ["bool"] }, { "Name": "Trash", "Docs": "", "Typewords": ["bool"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HaveCounts", "Docs": "", "Typewords": ["bool"] }, { "Name": "Total", "Docs": "", "Typewords": ["int64"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unread", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unseen", "Docs": "", "Typewords": ["int64"] }, { "Name": "Size", "Docs": "", "Typewords": ["int64"] }] }, "RecipientSecurity": { "Name": "RecipientSecurity", "Docs": "", "Fields": [{ "Name": "STARTTLS", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "MTASTS", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "DNSSEC", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "DANE", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "RequireTLS", "Docs": "", "Typewords": ["SecurityResult"] }] }, "Settings": { "Name": "Settings", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["uint8"] }, { "Name": "Signature", "Docs": "", "Typewords": ["string"] }, { "Name": "Quoting", "Docs": "", "Typewords": ["Quoting"] }, { "Name": "ShowAddressSecurity", "Docs": "", "Typewords": ["bool"] }] }, - "EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Settings", "Docs": "", "Typewords": ["Settings"] }, { "Name": "Version", "Docs": "", "Typewords": ["string"] }] }, + "EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Settings", "Docs": "", "Typewords": ["Settings"] }, { "Name": "AccountPath", "Docs": "", "Typewords": ["string"] }, { "Name": "Version", "Docs": "", "Typewords": ["string"] }] }, "DomainAddressConfig": { "Name": "DomainAddressConfig", "Docs": "", "Fields": [{ "Name": "LocalpartCatchallSeparator", "Docs": "", "Typewords": ["string"] }, { "Name": "LocalpartCaseSensitive", "Docs": "", "Typewords": ["bool"] }] }, "EventViewErr": { "Name": "EventViewErr", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Err", "Docs": "", "Typewords": ["string"] }] }, "EventViewReset": { "Name": "EventViewReset", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }] }, diff --git a/webmail/text.js b/webmail/text.js index c112029..30add86 100644 --- a/webmail/text.js +++ b/webmail/text.js @@ -302,7 +302,7 @@ var api; "Mailbox": { "Name": "Mailbox", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "UIDValidity", "Docs": "", "Typewords": ["uint32"] }, { "Name": "UIDNext", "Docs": "", "Typewords": ["UID"] }, { "Name": "Archive", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Sent", "Docs": "", "Typewords": ["bool"] }, { "Name": "Trash", "Docs": "", "Typewords": ["bool"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HaveCounts", "Docs": "", "Typewords": ["bool"] }, { "Name": "Total", "Docs": "", "Typewords": ["int64"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unread", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unseen", "Docs": "", "Typewords": ["int64"] }, { "Name": "Size", "Docs": "", "Typewords": ["int64"] }] }, "RecipientSecurity": { "Name": "RecipientSecurity", "Docs": "", "Fields": [{ "Name": "STARTTLS", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "MTASTS", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "DNSSEC", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "DANE", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "RequireTLS", "Docs": "", "Typewords": ["SecurityResult"] }] }, "Settings": { "Name": "Settings", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["uint8"] }, { "Name": "Signature", "Docs": "", "Typewords": ["string"] }, { "Name": "Quoting", "Docs": "", "Typewords": ["Quoting"] }, { "Name": "ShowAddressSecurity", "Docs": "", "Typewords": ["bool"] }] }, - "EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Settings", "Docs": "", "Typewords": ["Settings"] }, { "Name": "Version", "Docs": "", "Typewords": ["string"] }] }, + "EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Settings", "Docs": "", "Typewords": ["Settings"] }, { "Name": "AccountPath", "Docs": "", "Typewords": ["string"] }, { "Name": "Version", "Docs": "", "Typewords": ["string"] }] }, "DomainAddressConfig": { "Name": "DomainAddressConfig", "Docs": "", "Fields": [{ "Name": "LocalpartCatchallSeparator", "Docs": "", "Typewords": ["string"] }, { "Name": "LocalpartCaseSensitive", "Docs": "", "Typewords": ["bool"] }] }, "EventViewErr": { "Name": "EventViewErr", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Err", "Docs": "", "Typewords": ["string"] }] }, "EventViewReset": { "Name": "EventViewReset", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }] }, diff --git a/webmail/view.go b/webmail/view.go index e229da7..ac2afbe 100644 --- a/webmail/view.go +++ b/webmail/view.go @@ -216,6 +216,7 @@ type EventStart struct { Mailboxes []store.Mailbox RejectsMailbox string Settings store.Settings + AccountPath string // If nonempty, the path on same host to webaccount interface. Version string } @@ -488,7 +489,7 @@ type ioErr struct { // serveEvents serves an SSE connection. Authentication is done through a query // string parameter "token", a one-time-use token returned by the Token API call. -func serveEvents(ctx context.Context, log mlog.Log, w http.ResponseWriter, r *http.Request) { +func serveEvents(ctx context.Context, log mlog.Log, accountPath string, w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "405 - method not allowed - use get", http.StatusMethodNotAllowed) return @@ -733,7 +734,7 @@ func serveEvents(ctx context.Context, log mlog.Log, w http.ResponseWriter, r *ht } // Write first event, allowing client to fill its UI with mailboxes. - start := EventStart{sse.ID, loginAddress, addresses, domainAddressConfigs, mailbox.Name, mbl, accConf.RejectsMailbox, settings, moxvar.Version} + start := EventStart{sse.ID, loginAddress, addresses, domainAddressConfigs, mailbox.Name, mbl, accConf.RejectsMailbox, settings, accountPath, moxvar.Version} writer.xsendEvent(ctx, log, "start", start) // The goroutine doing the querying will send messages on these channels, which diff --git a/webmail/view_test.go b/webmail/view_test.go index 743e5d6..5b8c6cd 100644 --- a/webmail/view_test.go +++ b/webmail/view_test.go @@ -104,7 +104,7 @@ func TestView(t *testing.T) { // We start an actual HTTP server to easily get a body we can do blocking reads on. // With a httptest.ResponseRecorder, it's a bit more work to parse SSE events as // they come in. - server := httptest.NewServer(http.HandlerFunc(Handler(1024*1024, "/webmail/", false))) + server := httptest.NewServer(http.HandlerFunc(Handler(1024*1024, "/webmail/", false, ""))) defer server.Close() serverURL, err := url.Parse(server.URL) diff --git a/webmail/webmail.go b/webmail/webmail.go index 6465995..caf7518 100644 --- a/webmail/webmail.go +++ b/webmail/webmail.go @@ -170,18 +170,18 @@ func serveContentFallback(log mlog.Log, w http.ResponseWriter, r *http.Request, // Handler returns a handler for the webmail endpoints, customized for the max // message size coming from the listener and cookiePath. -func Handler(maxMessageSize int64, cookiePath string, isForwarded bool) func(w http.ResponseWriter, r *http.Request) { +func Handler(maxMessageSize int64, cookiePath string, isForwarded bool, accountPath string) func(w http.ResponseWriter, r *http.Request) { sh, err := makeSherpaHandler(maxMessageSize, cookiePath, isForwarded) return func(w http.ResponseWriter, r *http.Request) { if err != nil { http.Error(w, "500 - internal server error - cannot handle requests", http.StatusInternalServerError) return } - handle(sh, isForwarded, w, r) + handle(sh, isForwarded, accountPath, w, r) } } -func handle(apiHandler http.Handler, isForwarded bool, w http.ResponseWriter, r *http.Request) { +func handle(apiHandler http.Handler, isForwarded bool, accountPath string, w http.ResponseWriter, r *http.Request) { ctx := r.Context() log := pkglog.WithContext(ctx).With(slog.String("userauth", "")) @@ -189,7 +189,7 @@ func handle(apiHandler http.Handler, isForwarded bool, w http.ResponseWriter, r // messages, and all events afterwards. Authenticated through a token in the query // string, which it got from a Token API call. if r.URL.Path == "/events" { - serveEvents(ctx, log, w, r) + serveEvents(ctx, log, accountPath, w, r) return } diff --git a/webmail/webmail.js b/webmail/webmail.js index d69fcf9..c4c740b 100644 --- a/webmail/webmail.js +++ b/webmail/webmail.js @@ -302,7 +302,7 @@ var api; "Mailbox": { "Name": "Mailbox", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "UIDValidity", "Docs": "", "Typewords": ["uint32"] }, { "Name": "UIDNext", "Docs": "", "Typewords": ["UID"] }, { "Name": "Archive", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Sent", "Docs": "", "Typewords": ["bool"] }, { "Name": "Trash", "Docs": "", "Typewords": ["bool"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HaveCounts", "Docs": "", "Typewords": ["bool"] }, { "Name": "Total", "Docs": "", "Typewords": ["int64"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unread", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unseen", "Docs": "", "Typewords": ["int64"] }, { "Name": "Size", "Docs": "", "Typewords": ["int64"] }] }, "RecipientSecurity": { "Name": "RecipientSecurity", "Docs": "", "Fields": [{ "Name": "STARTTLS", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "MTASTS", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "DNSSEC", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "DANE", "Docs": "", "Typewords": ["SecurityResult"] }, { "Name": "RequireTLS", "Docs": "", "Typewords": ["SecurityResult"] }] }, "Settings": { "Name": "Settings", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["uint8"] }, { "Name": "Signature", "Docs": "", "Typewords": ["string"] }, { "Name": "Quoting", "Docs": "", "Typewords": ["Quoting"] }, { "Name": "ShowAddressSecurity", "Docs": "", "Typewords": ["bool"] }] }, - "EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Settings", "Docs": "", "Typewords": ["Settings"] }, { "Name": "Version", "Docs": "", "Typewords": ["string"] }] }, + "EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }, { "Name": "RejectsMailbox", "Docs": "", "Typewords": ["string"] }, { "Name": "Settings", "Docs": "", "Typewords": ["Settings"] }, { "Name": "AccountPath", "Docs": "", "Typewords": ["string"] }, { "Name": "Version", "Docs": "", "Typewords": ["string"] }] }, "DomainAddressConfig": { "Name": "DomainAddressConfig", "Docs": "", "Fields": [{ "Name": "LocalpartCatchallSeparator", "Docs": "", "Typewords": ["string"] }, { "Name": "LocalpartCaseSensitive", "Docs": "", "Typewords": ["bool"] }] }, "EventViewErr": { "Name": "EventViewErr", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Err", "Docs": "", "Typewords": ["string"] }] }, "EventViewReset": { "Name": "EventViewReset", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }] }, @@ -5606,6 +5606,7 @@ const parseComposeMailto = (mailto) => { const init = async () => { let connectionElem; // SSE connection status/error. Empty when connected. let layoutElem; // Select dropdown for layout. + let accountElem; let loginAddressElem; let msglistscrollElem; let queryactivityElem; // We show ... when a query is active and data is forthcoming. @@ -6005,7 +6006,7 @@ const init = async () => { else { selectLayout(layoutElem.value); } - }), ' ', dom.clickbutton('Tooltip', attr.title('Show tooltips, based on the title attributes (underdotted text) for the focused element and all user interface elements below it. Use the keyboard shortcut "ctrl ?" instead of clicking on the tooltip button, which changes focus to the tooltip button.'), clickCmd(cmdTooltip, shortcuts)), ' ', dom.clickbutton('Help', attr.title('Show popup with basic usage information and a keyboard shortcuts.'), clickCmd(cmdHelp, shortcuts)), ' ', dom.clickbutton('Settings', attr.title('Change settings for composing messages.'), clickCmd(cmdSettings, shortcuts)), ' ', loginAddressElem = dom.span(), ' ', dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e) { + }), ' ', dom.clickbutton('Tooltip', attr.title('Show tooltips, based on the title attributes (underdotted text) for the focused element and all user interface elements below it. Use the keyboard shortcut "ctrl ?" instead of clicking on the tooltip button, which changes focus to the tooltip button.'), clickCmd(cmdTooltip, shortcuts)), ' ', dom.clickbutton('Help', attr.title('Show popup with basic usage information and a keyboard shortcuts.'), clickCmd(cmdHelp, shortcuts)), ' ', dom.clickbutton('Settings', attr.title('Change settings for composing messages.'), clickCmd(cmdSettings, shortcuts)), ' ', accountElem = dom.span(), ' ', loginAddressElem = dom.span(), ' ', dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e) { await withStatus('Logging out', client.Logout(), e.target); localStorageRemove('webmailcsrftoken'); if (eventSource) { @@ -6406,8 +6407,9 @@ const init = async () => { connecting = false; sseID = start.SSEID; loginAddress = start.LoginAddress; - dom._kids(loginAddressElem, formatEmail(loginAddress)); + dom._kids(accountElem, start.AccountPath ? dom.a(attr.href(start.AccountPath), 'Account') : []); const loginAddr = formatEmail(loginAddress); + dom._kids(loginAddressElem, loginAddr); accountAddresses = start.Addresses || []; accountAddresses.sort((a, b) => { if (formatEmail(a) === loginAddr) { diff --git a/webmail/webmail.ts b/webmail/webmail.ts index 1d243fb..24d6595 100644 --- a/webmail/webmail.ts +++ b/webmail/webmail.ts @@ -5681,6 +5681,7 @@ type listMailboxes = () => api.Mailbox[] const init = async () => { let connectionElem: HTMLElement // SSE connection status/error. Empty when connected. let layoutElem: HTMLSelectElement // Select dropdown for layout. + let accountElem: HTMLElement let loginAddressElem: HTMLElement let msglistscrollElem: HTMLElement @@ -6257,6 +6258,8 @@ const init = async () => { ' ', dom.clickbutton('Settings', attr.title('Change settings for composing messages.'), clickCmd(cmdSettings, shortcuts)), ' ', + accountElem=dom.span(), + ' ', loginAddressElem=dom.span(), ' ', dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e: MouseEvent) { @@ -6751,8 +6754,9 @@ const init = async () => { connecting = false sseID = start.SSEID loginAddress = start.LoginAddress - dom._kids(loginAddressElem, formatEmail(loginAddress)) + dom._kids(accountElem, start.AccountPath ? dom.a(attr.href(start.AccountPath), 'Account') : []) const loginAddr = formatEmail(loginAddress) + dom._kids(loginAddressElem, loginAddr) accountAddresses = start.Addresses || [] accountAddresses.sort((a, b) => { if (formatEmail(a) === loginAddr) { diff --git a/webmail/webmail_test.go b/webmail/webmail_test.go index f70bce3..cfcb223 100644 --- a/webmail/webmail_test.go +++ b/webmail/webmail_test.go @@ -377,7 +377,7 @@ func TestWebmail(t *testing.T) { } rr := httptest.NewRecorder() rr.Body = &bytes.Buffer{} - handle(apiHandler, false, rr, req) + handle(apiHandler, false, "", rr, req) if rr.Code != expStatusCode { t.Fatalf("got status %d, expected %d (%s)", rr.Code, expStatusCode, readBody(rr.Body)) }