From ed67f2a3f556df83de949189272d12362b285261 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 9 Jan 2018 17:17:59 +0100 Subject: [PATCH 1/3] smtp: case-insensitive email address comparison, fixes #20 --- smtp/smtp.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smtp/smtp.go b/smtp/smtp.go index 2db0eac..4bef59a 100644 --- a/smtp/smtp.go +++ b/smtp/smtp.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "strings" "github.com/emersion/go-message/mail" "github.com/emersion/go-smtp" @@ -65,7 +66,7 @@ func (u *user) Send(from string, to []string, r io.Reader) error { fromAddrStr := fromList[0].Address var fromAddr *protonmail.Address for _, addr := range u.u.Addresses { - if addr.Email == fromAddrStr { + if strings.EqualFold(addr.Email, fromAddrStr) { fromAddr = addr break } From dea3ab310627f0b97ce3b293f8e1759e95b88b3a Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 9 Jan 2018 22:58:42 +0100 Subject: [PATCH 2/3] protonmail: export ApiError --- protonmail/protonmail.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/protonmail/protonmail.go b/protonmail/protonmail.go index 2e17e7c..20d12e9 100644 --- a/protonmail/protonmail.go +++ b/protonmail/protonmail.go @@ -24,8 +24,10 @@ type resp struct { func (r *resp) Err() error { if err := r.apiError; err != nil { - err.code = r.Code - return err + return &ApiError{ + Code: r.Code, + Message: err.Message, + } } return nil } @@ -35,12 +37,16 @@ type maybeError interface { } type apiError struct { - code int // populated by resp Message string `json:"Error"` } -func (err apiError) Error() string { - return fmt.Sprintf("[%v] %v", err.code, err.Message) +type ApiError struct { + Code int + Message string +} + +func (err *ApiError) Error() string { + return fmt.Sprintf("[%v] %v", err.Code, err.Message) } // Client is a ProtonMail API client. From ab599a27faa096da8af85a2bf6731b1ed652bf78 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 9 Jan 2018 22:58:55 +0100 Subject: [PATCH 3/3] auth: try to re-authenticate --- auth/auth.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index 315323c..da526bc 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "errors" + "fmt" "io" "os" @@ -97,15 +98,29 @@ func EncryptAndSave(auth *CachedAuth, username string, secretKey *[32]byte) erro return saveAuths(auths) } -func authenticate(c *protonmail.Client, CachedAuth *CachedAuth) (openpgp.EntityList, error) { - auth, err := c.AuthRefresh(&CachedAuth.Auth) - if err != nil { - // TODO: handle expired token, re-authenticate +func authenticate(c *protonmail.Client, cachedAuth *CachedAuth, username string) (openpgp.EntityList, error) { + auth, err := c.AuthRefresh(&cachedAuth.Auth) + if apiErr, ok := err.(*protonmail.ApiError); ok && apiErr.Code == 10013 { + // Invalid refresh token, re-authenticate + authInfo, err := c.AuthInfo(username) + if err != nil { + return nil, fmt.Errorf("cannot re-authenticate: failed to get auth info: %v", err) + } + + if authInfo.TwoFactor == 1 { + return nil, fmt.Errorf("cannot re-authenticate: two factor authentication enabled, please login manually") + } + + auth, err = c.Auth(username, cachedAuth.LoginPassword, "", authInfo) + if err != nil { + return nil, fmt.Errorf("cannot re-authenticate: %v", err) + } + } else if err != nil { return nil, err } - CachedAuth.Auth = *auth + cachedAuth.Auth = *auth - return c.Unlock(auth, CachedAuth.MailboxPassword) + return c.Unlock(auth, cachedAuth.MailboxPassword) } func GeneratePassword() (secretKey *[32]byte, password string, err error) { @@ -168,14 +183,14 @@ func (m *Manager) Auth(username, password string) (*protonmail.Client, openpgp.E c := m.newClient() c.ReAuth = func() error { - if _, err := authenticate(c, &cachedAuth); err != nil { + if _, err := authenticate(c, &cachedAuth, username); err != nil { return err } return EncryptAndSave(&cachedAuth, username, &secretKey) } // authenticate updates cachedAuth with the new refresh token - privateKeys, err := authenticate(c, &cachedAuth) + privateKeys, err := authenticate(c, &cachedAuth, username) if err != nil { return nil, nil, err }