From c629ae26afee9976f7c6530ecff86f561108606e Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Mon, 8 Jul 2024 21:58:10 +0200 Subject: [PATCH] don't prevent the html pages to load a favicon, and provide one by default for issue #186 by morki, thanks for reporting and providing sample favicons. generated by the mentioned generator at favicon.io, with the ubuntu font and a fuchsia-like color. the favicon is served for listeners/domains that have the admin/account/webmail/webapi endpoints enabled, i.e. user-facing. the mta-sts, autoconfig, etc urls don't serve the favicon. admins can create webhandler routes to serve another favicon. these webhandler routes are evaluted before the favicon route (a "service handler"). --- http/favicon.ico | Bin 0 -> 823 bytes http/web.go | 68 +++++++++++++++++++++++++++++----------- webaccount/account.html | 1 - webadmin/admin.html | 1 - webmail/msg.html | 1 - webmail/text.html | 1 - webmail/webmail.html | 1 - 7 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 http/favicon.ico diff --git a/http/favicon.ico b/http/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d64d0bfbd0079bd42d25fe4993ea12b5eeba46e3 GIT binary patch literal 823 zcmV-71IYXU0096203aX$0096X03iba02TlM0EtjeM-2)Z3IG5A4M|8uQUCw|AOHXW zAP5Ek0047(dh`GQ0_aIZK~z|U#h1HJR6!KRe=~P^7(`c1T%eF>G{(fngcxHiC@hVQ zJ%4~$m`FiT5;O)kHa5~*P}&(AO|(!d?F{L>`Y`DO5qCQmi??C9h_QT!U6Ny%bh%MyLQ)yQ?;gBBT;g;*p6-%U)6u zC6!AIns0%wcK4Z+P}zp*6)0~QDYbX~T z0b0Yockt}95kkhTC4kf#RySZ~6{c2TW5-h-YxRO_4L{c4>mMlnrO^pOK(QB29}OY9 z`s5zydky20-n_jT#&5vQbKbibUtstH%&)t1N5F%tFmlbEH#^XG5gtzXfOWkBF`{dk zd4yqo3vR!08?7!=D==U2=2Z=kCSbPYy}Qz73?Yrfx(L9Le!{N&EW>vn`1un=mQn*B z=RLRXBS{1-z2Jzz#%=)C%MEb6#q7IK_FVI=L7j*Yu!tiA**5?yzH^|-azSkRZ9<{6 zNK3$SuIUsX1hE^e-h_U96P=uyfI{uzo5UZ|k78o%uWq1aWx8wBA}W0k_!u+@W(aL5 zlhiQ;0)=o4CNe|u6Sx`)1YVk&(6m(9F7^gYyqm&Y0h_H)($q+L1T1xu1S^pK11epY zxYuzpO7?m;wAAjYZ>um*}Hdx7C{_;$rRDf2JgOgTAz(SJAO%K-L5YbO8z002ovPDHLkV1h-a BY0>}y literal 0 HcmV?d00001 diff --git a/http/web.go b/http/web.go index d660e088fa..c9d6d9b40e 100644 --- a/http/web.go +++ b/http/web.go @@ -19,6 +19,7 @@ import ( "strings" "time" + _ "embed" _ "net/http/pprof" "golang.org/x/exp/maps" @@ -74,6 +75,29 @@ var ( ) ) +// We serve a favicon when webaccount/webmail/webadmin/webapi for account-related +// domains. They are configured as "service handler", which have a lower priority +// than web handler. Admins can configure a custom /favicon.ico route to override +// the builtin favicon. In the future, we may want to make it easier to customize +// the favicon, possibly per client settings domain. +// +//go:embed favicon.ico +var faviconIco string +var faviconModTime = time.Now() + +func init() { + p, err := os.Executable() + if err == nil { + if st, err := os.Stat(p); err == nil { + faviconModTime = st.ModTime() + } + } +} + +func faviconHandle(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, "favicon.ico", faviconModTime, strings.NewReader(faviconIco)) +} + type responseWriterFlusher interface { http.ResponseWriter http.Flusher @@ -361,6 +385,7 @@ type pathHandler struct { type serve struct { Kinds []string // Type of handler and protocol (e.g. acme-tls-alpn-01, account-http, admin-https). TLSConfig *tls.Config + Favicon bool // SystemHandlers are for MTA-STS, autoconfig, ACME validation. They can't be // overridden by WebHandlers. WebHandlers are evaluated next, and the internal @@ -555,21 +580,26 @@ func portServes(l config.Listener) map[int]*serve { return mox.Conf.IsClientSettingsDomain(host.Domain) } - var ensureServe func(https bool, port int, kind string) *serve - ensureServe = func(https bool, port int, kind string) *serve { + var ensureServe func(https bool, port int, kind string, favicon bool) *serve + ensureServe = func(https bool, port int, kind string, favicon bool) *serve { s := portServe[port] if s == nil { - s = &serve{nil, nil, nil, false, nil} + s = &serve{nil, nil, false, nil, false, nil} portServe[port] = s } s.Kinds = append(s.Kinds, kind) + if favicon && !s.Favicon { + s.ServiceHandle("favicon", accountHostMatch, "/favicon.ico", mox.SafeHeaders(http.HandlerFunc(faviconHandle))) + s.Favicon = true + } + if https && l.TLS.ACME != "" { s.TLSConfig = l.TLS.ACMEConfig } else if https { s.TLSConfig = l.TLS.Config if l.TLS.ACME != "" { tlsport := config.Port(mox.Conf.Static.ACME[l.TLS.ACME].Port, 443) - ensureServe(true, tlsport, "acme-tls-alpn-01") + ensureServe(true, tlsport, "acme-tls-alpn-01", false) } } return s @@ -577,7 +607,7 @@ func portServes(l config.Listener) map[int]*serve { if l.TLS != nil && l.TLS.ACME != "" && (l.SMTP.Enabled && !l.SMTP.NoSTARTTLS || l.Submissions.Enabled || l.IMAPS.Enabled) { port := config.Port(mox.Conf.Static.ACME[l.TLS.ACME].Port, 443) - ensureServe(true, port, "acme-tls-alpn-01") + ensureServe(true, port, "acme-tls-alpn-01", false) } if l.AccountHTTP.Enabled { @@ -586,7 +616,7 @@ func portServes(l config.Listener) map[int]*serve { if l.AccountHTTP.Path != "" { path = l.AccountHTTP.Path } - srv := ensureServe(false, port, "account-http at "+path) + srv := ensureServe(false, port, "account-http at "+path, true) handler := mox.SafeHeaders(http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webaccount.Handler(path, l.AccountHTTP.Forwarded)))) srv.ServiceHandle("account", accountHostMatch, path, handler) redirectToTrailingSlash(srv, accountHostMatch, "account", path) @@ -597,7 +627,7 @@ func portServes(l config.Listener) map[int]*serve { if l.AccountHTTPS.Path != "" { path = l.AccountHTTPS.Path } - srv := ensureServe(true, port, "account-https at "+path) + srv := ensureServe(true, port, "account-https at "+path, true) handler := mox.SafeHeaders(http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webaccount.Handler(path, l.AccountHTTPS.Forwarded)))) srv.ServiceHandle("account", accountHostMatch, path, handler) redirectToTrailingSlash(srv, accountHostMatch, "account", path) @@ -609,7 +639,7 @@ func portServes(l config.Listener) map[int]*serve { if l.AdminHTTP.Path != "" { path = l.AdminHTTP.Path } - srv := ensureServe(false, port, "admin-http at "+path) + srv := ensureServe(false, port, "admin-http at "+path, true) handler := mox.SafeHeaders(http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webadmin.Handler(path, l.AdminHTTP.Forwarded)))) srv.ServiceHandle("admin", listenerHostMatch, path, handler) redirectToTrailingSlash(srv, listenerHostMatch, "admin", path) @@ -620,7 +650,7 @@ func portServes(l config.Listener) map[int]*serve { if l.AdminHTTPS.Path != "" { path = l.AdminHTTPS.Path } - srv := ensureServe(true, port, "admin-https at "+path) + srv := ensureServe(true, port, "admin-https at "+path, true) handler := mox.SafeHeaders(http.StripPrefix(path[:len(path)-1], http.HandlerFunc(webadmin.Handler(path, l.AdminHTTPS.Forwarded)))) srv.ServiceHandle("admin", listenerHostMatch, path, handler) redirectToTrailingSlash(srv, listenerHostMatch, "admin", path) @@ -637,7 +667,7 @@ func portServes(l config.Listener) map[int]*serve { if l.WebAPIHTTP.Path != "" { path = l.WebAPIHTTP.Path } - srv := ensureServe(false, port, "webapi-http at "+path) + srv := ensureServe(false, port, "webapi-http at "+path, true) handler := mox.SafeHeaders(http.StripPrefix(path[:len(path)-1], webapisrv.NewServer(maxMsgSize, path, l.WebAPIHTTP.Forwarded))) srv.ServiceHandle("webapi", accountHostMatch, path, handler) redirectToTrailingSlash(srv, accountHostMatch, "webapi", path) @@ -648,7 +678,7 @@ func portServes(l config.Listener) map[int]*serve { if l.WebAPIHTTPS.Path != "" { path = l.WebAPIHTTPS.Path } - srv := ensureServe(true, port, "webapi-https at "+path) + srv := ensureServe(true, port, "webapi-https at "+path, true) handler := mox.SafeHeaders(http.StripPrefix(path[:len(path)-1], webapisrv.NewServer(maxMsgSize, path, l.WebAPIHTTPS.Forwarded))) srv.ServiceHandle("webapi", accountHostMatch, path, handler) redirectToTrailingSlash(srv, accountHostMatch, "webapi", path) @@ -660,7 +690,7 @@ func portServes(l config.Listener) map[int]*serve { if l.WebmailHTTP.Path != "" { path = l.WebmailHTTP.Path } - srv := ensureServe(false, port, "webmail-http at "+path) + srv := ensureServe(false, port, "webmail-http at "+path, true) var accountPath string if l.AccountHTTP.Enabled { accountPath = "/" @@ -678,7 +708,7 @@ func portServes(l config.Listener) map[int]*serve { if l.WebmailHTTPS.Path != "" { path = l.WebmailHTTPS.Path } - srv := ensureServe(true, port, "webmail-https at "+path) + srv := ensureServe(true, port, "webmail-https at "+path, true) var accountPath string if l.AccountHTTPS.Enabled { accountPath = "/" @@ -693,7 +723,7 @@ func portServes(l config.Listener) map[int]*serve { if l.MetricsHTTP.Enabled { port := config.Port(l.MetricsHTTP.Port, 8010) - srv := ensureServe(false, port, "metrics-http") + srv := ensureServe(false, port, "metrics-http", false) srv.SystemHandle("metrics", nil, "/metrics", mox.SafeHeaders(promhttp.Handler())) srv.SystemHandle("metrics", nil, "/", mox.SafeHeaders(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { @@ -709,7 +739,7 @@ func portServes(l config.Listener) map[int]*serve { } if l.AutoconfigHTTPS.Enabled { port := config.Port(l.AutoconfigHTTPS.Port, 443) - srv := ensureServe(!l.AutoconfigHTTPS.NonTLS, port, "autoconfig-https") + srv := ensureServe(!l.AutoconfigHTTPS.NonTLS, port, "autoconfig-https", false) autoconfigMatch := func(ipdom dns.IPDomain) bool { dom := ipdom.Domain if dom.IsZero() { @@ -736,7 +766,7 @@ func portServes(l config.Listener) map[int]*serve { } if l.MTASTSHTTPS.Enabled { port := config.Port(l.MTASTSHTTPS.Port, 443) - srv := ensureServe(!l.MTASTSHTTPS.NonTLS, port, "mtasts-https") + srv := ensureServe(!l.MTASTSHTTPS.NonTLS, port, "mtasts-https", false) mtastsMatch := func(ipdom dns.IPDomain) bool { // todo: may want to check this against the configured domains, could in theory be just a webserver. dom := ipdom.Domain @@ -753,18 +783,18 @@ func portServes(l config.Listener) map[int]*serve { if _, ok := portServe[port]; ok { pkglog.Fatal("cannot serve pprof on same endpoint as other http services") } - srv := &serve{[]string{"pprof-http"}, nil, nil, false, nil} + srv := &serve{[]string{"pprof-http"}, nil, false, nil, false, nil} portServe[port] = srv srv.SystemHandle("pprof", nil, "/", http.DefaultServeMux) } if l.WebserverHTTP.Enabled { port := config.Port(l.WebserverHTTP.Port, 80) - srv := ensureServe(false, port, "webserver-http") + srv := ensureServe(false, port, "webserver-http", false) srv.Webserver = true } if l.WebserverHTTPS.Enabled { port := config.Port(l.WebserverHTTPS.Port, 443) - srv := ensureServe(true, port, "webserver-https") + srv := ensureServe(true, port, "webserver-https", false) srv.Webserver = true } diff --git a/webaccount/account.html b/webaccount/account.html index 61520902b9..4cd6fee052 100644 --- a/webaccount/account.html +++ b/webaccount/account.html @@ -4,7 +4,6 @@ Mox Account -