This commit is contained in:
binwiederhier 2024-09-29 13:12:51 -04:00
parent fc3624cd50
commit d38c149263
7 changed files with 55 additions and 19 deletions

View File

@ -2941,11 +2941,17 @@ format is:
ntfy-$topic@ntfy.sh
```
If [access control](config.md#access-control) is enabled, and the target topic does not support anonymous writes, e-mail publishing won't work without providing an authorized access token. That will change the format of the e-mail's recipient address to
If [access control](config.md#access-control) is enabled, and the target topic does not support anonymous writes, e-mail publishing won't work
without providing an authorized access token or using SMTP AUTH PLAIN.
If you use [access tokens](#access-tokens), that will change the format of the e-mail's recipient address to
```
ntfy-$topic+$token@ntfy.sh
```
To use [username/password](https://docs.ntfy.sh/publish/#username-password), you can use SMTP PLAIN auth when authenticating
to the ntfy server.
As of today, e-mail publishing only supports adding a [message title](#message-title) (the e-mail subject). Tags, priority,
delay and other features are not supported (yet). Here's an example that will publish a message with the
title `You've Got Mail` to topic `sometopic` (see [ntfy.sh/sometopic](https://ntfy.sh/sometopic)):

View File

@ -1373,6 +1373,23 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
## Not released yet
### ntfy server v2.12.0 (UNRELEASED)
**Features:**
* Add username/password auth to email publishing ([#1164](https://github.com/binwiederhier/ntfy/pull/1164), thanks to [@bishtawi](https://github.com/bishtawi))
**Bug fixes + maintenance:**
* Add `Date` header to outgoing emails to avoid rejection ([#1141](https://github.com/binwiederhier/ntfy/pull/1141), thanks to [pcouy](https://github.com/pcouy))
**Documentation:**
* Various docs updates ([](https://github.com/binwiederhier/ntfy/pull/1161), thanks to [@OneWeekNotice](https://github.com/OneWeekNotice))
* Typo in config docs ([#1177](https://github.com/binwiederhier/ntfy/pull/1177), thanks to [@hoho4190](https://github.com/hoho4190))
* Typo in CLI docs ([#1172](https://github.com/binwiederhier/ntfy/pull/1172), thanks to [@anirvan](https://github.com/anirvan))
* Correction about MacroDroid ([](https://github.com/binwiederhier/ntfy/pull/1137), thanks to [@ShlomoCode](https://github.com/ShlomoCode))
### ntfy Android app v1.16.1 (UNRELEASED)
**Features:**

View File

@ -1277,6 +1277,7 @@ func TestServer_PublishEmailNoMailer_Fail(t *testing.T) {
func TestServer_PublishAndExpungeTopicAfter16Hours(t *testing.T) {
t.Parallel()
s := newTestServer(t, newTestConfig(t))
defer s.messageCache.Close()
subFn := func(v *visitor, msg *message) error {
return nil
@ -1288,13 +1289,22 @@ func TestServer_PublishAndExpungeTopicAfter16Hours(t *testing.T) {
})
require.Equal(t, 200, response.Code)
waitFor(t, func() bool {
s.mu.Lock()
tp, exists := s.topics["mytopic"]
s.mu.Unlock()
if !exists {
return false
}
// .lastAccess set in t.Publish() -> t.Keepalive() in Goroutine
s.topics["mytopic"].mu.RLock()
defer s.topics["mytopic"].mu.RUnlock()
return s.topics["mytopic"].lastAccess.Unix() >= time.Now().Unix()-2 &&
s.topics["mytopic"].lastAccess.Unix() <= time.Now().Unix()+2
tp.mu.RLock()
defer tp.mu.RUnlock()
return tp.lastAccess.Unix() >= time.Now().Unix()-2 &&
tp.lastAccess.Unix() <= time.Now().Unix()+2
})
// Hack!
time.Sleep(time.Second)
// Topic won't get pruned
s.execManager()
require.NotNil(t, s.topics["mytopic"])

View File

@ -110,7 +110,7 @@ func formatMail(baseURL, senderIP, from, to string, m *message) (string, error)
if trailer != "" {
message += "\n\n" + trailer
}
date := time.Unix(m.Time, 0).Format(time.RFC1123Z)
date := time.Unix(m.Time, 0).UTC().Format(time.RFC1123Z)
subject = mime.BEncoding.Encode("utf-8", subject)
body := `From: "{shortTopicURL}" <{from}>
To: {to}

View File

@ -15,6 +15,7 @@ func TestFormatMail_Basic(t *testing.T) {
})
expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
To: phil@example.com
Date: Fri, 24 Dec 2021 21:43:24 +0000
Subject: A simple message
Content-Type: text/plain; charset="utf-8"
@ -36,6 +37,7 @@ func TestFormatMail_JustEmojis(t *testing.T) {
})
expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
To: phil@example.com
Date: Fri, 24 Dec 2021 21:43:24 +0000
Subject: =?utf-8?b?8J+YgCBBIHNpbXBsZSBtZXNzYWdl?=
Content-Type: text/plain; charset="utf-8"
@ -57,6 +59,7 @@ func TestFormatMail_JustOtherTags(t *testing.T) {
})
expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
To: phil@example.com
Date: Fri, 24 Dec 2021 21:43:24 +0000
Subject: A simple message
Content-Type: text/plain; charset="utf-8"
@ -80,6 +83,7 @@ func TestFormatMail_JustPriority(t *testing.T) {
})
expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
To: phil@example.com
Date: Fri, 24 Dec 2021 21:43:24 +0000
Subject: A simple message
Content-Type: text/plain; charset="utf-8"
@ -103,6 +107,7 @@ func TestFormatMail_UTF8Subject(t *testing.T) {
})
expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
To: phil@example.com
Date: Fri, 24 Dec 2021 21:43:24 +0000
Subject: =?utf-8?b?IDo6IEEgbm90IHNvIHNpbXBsZSB0aXRsZSDDtsOkw7zDnyDCoUhvbGEsIHNl?= =?utf-8?b?w7FvciE=?=
Content-Type: text/plain; charset="utf-8"
@ -126,6 +131,7 @@ func TestFormatMail_WithAllTheThings(t *testing.T) {
})
expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
To: phil@example.com
Date: Fri, 24 Dec 2021 21:43:24 +0000
Subject: =?utf-8?b?4pqg77iPIPCfkoAgT2ggbm8g8J+ZiCBUaGlzIGlzIGEgbWVzc2FnZSBhY3Jv?= =?utf-8?b?c3MgbXVsdGlwbGUgbGluZXM=?=
Content-Type: text/plain; charset="utf-8"

View File

@ -70,19 +70,18 @@ func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
// smtpSession is returned after EHLO.
type smtpSession struct {
backend *smtpBackend
conn *smtp.Conn
topic string
token string
mu sync.Mutex
basic_auth string
backend *smtpBackend
conn *smtp.Conn
topic string
token string // If email address contains token, e.g. topic+token@domain
basicAuth string // If SMTP AUTH PLAIN was used
mu sync.Mutex
}
func (s *smtpSession) AuthPlain(username, password string) error {
logem(s.conn).Field("smtp_username", username).Debug("AUTH PLAIN (with username %s)", username)
basic_auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
s.mu.Lock()
s.basic_auth = basic_auth
s.basicAuth = base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
s.mu.Unlock()
return nil
}
@ -203,8 +202,8 @@ func (s *smtpSession) publishMessage(m *message) error {
}
if s.token != "" {
req.Header.Add("Authorization", "Bearer "+s.token)
} else if s.basic_auth != "" {
req.Header.Add("Authorization", "Basic "+s.basic_auth)
} else if s.basicAuth != "" {
req.Header.Add("Authorization", "Basic "+s.basicAuth)
}
rr := httptest.NewRecorder()
s.backend.handler(rr, req)
@ -222,7 +221,7 @@ func (s *smtpSession) Reset() {
func (s *smtpSession) Logout() error {
s.mu.Lock()
s.basic_auth = ""
s.basicAuth = ""
s.mu.Unlock()
return nil
}

View File

@ -10,8 +10,6 @@ import (
)
func TestTopic_CancelSubscribersExceptUser(t *testing.T) {
t.Parallel()
subFn := func(v *visitor, msg *message) error {
return nil
}