Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aerc for openSUSE:Factory checked in at 2024-07-16 22:02:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aerc (Old) and /work/SRC/openSUSE:Factory/.aerc.new.17339 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aerc" Tue Jul 16 22:02:48 2024 rev:12 rq:1187625 version:0.18.1 Changes: -------- --- /work/SRC/openSUSE:Factory/aerc/aerc.changes 2024-07-15 19:50:40.094755991 +0200 +++ /work/SRC/openSUSE:Factory/.aerc.new.17339/aerc.changes 2024-07-16 22:02:57.543040940 +0200 @@ -1,0 +2,10 @@ +Mon Jul 15 22:56:47 UTC 2024 - Hannes Braun <apple.han...@gmail.com> - 0.18.1 + +- Update to upstream version 0.18.1 + * Fixed startup error if "log-file" directory does not exist. + * Aerc is now less pedantic about invalid headers for the maildir and notmuch + backends. + * Error when trying to configure smtp-domain with STARTTLS enabled. + * smtp-domain is now properly taken into account for TLS connections. + +------------------------------------------------------------------- Old: ---- aerc-0.18.0.tar.gz New: ---- aerc-0.18.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aerc.spec ++++++ --- /var/tmp/diff_new_pack.LtFbsm/_old 2024-07-16 22:02:58.175063990 +0200 +++ /var/tmp/diff_new_pack.LtFbsm/_new 2024-07-16 22:02:58.179064136 +0200 @@ -18,7 +18,7 @@ Name: aerc -Version: 0.18.0 +Version: 0.18.1 Release: 0 Summary: An email client for terminals License: GPL-3.0-or-later ++++++ aerc-0.18.0.tar.gz -> aerc-0.18.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/CHANGELOG.md new/aerc-0.18.1/CHANGELOG.md --- old/aerc-0.18.0/CHANGELOG.md 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/CHANGELOG.md 2024-07-15 22:50:02.000000000 +0200 @@ -3,6 +3,16 @@ All notable changes to aerc will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.18.1](https://git.sr.ht/~rjarry/aerc/refs/0.18.1) - 2024-07-15 + +### Fixed + +- Startup error if `log-file` directory does not exist. +- Aerc is now less pedantic about invalid headers for the maildir and notmuch + backends. +- Error when trying to configure `smtp-domain` with STARTTLS enabled. +- `smtp-domain` is now properly taken into account for TLS connections. + ## [0.18.0](https://git.sr.ht/~rjarry/aerc/refs/0.18.0) - 2024-07-02 ### Added diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/GNUmakefile new/aerc-0.18.1/GNUmakefile --- old/aerc-0.18.0/GNUmakefile 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/GNUmakefile 2024-07-15 22:50:02.000000000 +0200 @@ -1,6 +1,6 @@ # variables that can be changed by users # -VERSION ?= $(shell git describe --long --abbrev=12 --tags --dirty 2>/dev/null || echo 0.18.0) +VERSION ?= $(shell git describe --long --abbrev=12 --tags --dirty 2>/dev/null || echo 0.18.1) DATE ?= $(shell date +%Y-%m-%d) PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/config/general.go new/aerc-0.18.1/config/general.go --- old/aerc-0.18.0/config/general.go 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/config/general.go 2024-07-15 22:50:02.000000000 +0200 @@ -3,6 +3,7 @@ import ( "fmt" "os" + "path/filepath" "git.sr.ht/~rjarry/aerc/lib/log" "git.sr.ht/~rjarry/aerc/lib/xdg" @@ -39,6 +40,10 @@ } else if General.LogFile != "" { var err error path := xdg.ExpandHome(General.LogFile) + err = os.MkdirAll(filepath.Dir(path), 0o700) + if err != nil { + return fmt.Errorf("log-file: %w", err) + } logFile, err = os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/go.mod new/aerc-0.18.1/go.mod --- old/aerc-0.18.0/go.mod 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/go.mod 2024-07-15 22:50:02.000000000 +0200 @@ -17,7 +17,7 @@ github.com/emersion/go-msgauth v0.6.8 github.com/emersion/go-pgpmail v0.2.1 github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 - github.com/emersion/go-smtp v0.21.0 + github.com/emersion/go-smtp v0.21.3 github.com/fsnotify/fsevents v0.1.1 github.com/fsnotify/fsnotify v1.7.0 github.com/gatherstars-com/jwz v1.4.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/go.sum new/aerc-0.18.1/go.sum --- old/aerc-0.18.0/go.sum 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/go.sum 2024-07-15 22:50:02.000000000 +0200 @@ -47,8 +47,8 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY= github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-smtp v0.21.0 h1:ZDZmX9aFUuPlD1lpoT0nC/nozZuIkSCyQIyxdijjCy0= -github.com/emersion/go-smtp v0.21.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY= +github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/lib/rfc822/message.go new/aerc-0.18.1/lib/rfc822/message.go --- old/aerc-0.18.0/lib/rfc822/message.go 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/lib/rfc822/message.go 2024-07-15 22:50:02.000000000 +0200 @@ -131,40 +131,17 @@ return &body, nil } -var DateParseError = errors.New("date parsing failed") - -func parseEnvelope(h *mail.Header) (*models.Envelope, error) { - from, err := parseAddressList(h, "from") - if err != nil { - return nil, fmt.Errorf("could not read from address: %w", err) - } - to, err := parseAddressList(h, "to") - if err != nil { - return nil, fmt.Errorf("could not read to address: %w", err) - } - cc, err := parseAddressList(h, "cc") - if err != nil { - return nil, fmt.Errorf("could not read cc address: %w", err) - } - bcc, err := parseAddressList(h, "bcc") - if err != nil { - return nil, fmt.Errorf("could not read bcc address: %w", err) - } - replyTo, err := parseAddressList(h, "reply-to") - if err != nil { - return nil, fmt.Errorf("could not read reply-to address: %w", err) - } +func parseEnvelope(h *mail.Header) *models.Envelope { subj, err := h.Subject() if err != nil { - return nil, fmt.Errorf("could not read subject: %w", err) + log.Errorf("could not decode subject: %v", err) + subj = h.Get("Subject") } msgID, err := h.MessageID() if err != nil { + log.Errorf("invalid Message-ID header: %v", err) // proper parsing failed, so fall back to whatever is there - msgID, err = h.Text("message-id") - if err != nil { - return nil, err - } + msgID = strings.Trim(h.Get("message-id"), "<>") } var irt string irtList := parse.MsgIDList(h, "in-reply-to") @@ -173,21 +150,24 @@ } date, err := parseDate(h) if err != nil { - // still return a valid struct plus a sentinel date parsing error - // if only the date parsing failed - err = fmt.Errorf("%w: %v", DateParseError, err) //nolint:errorlint // can only use %w once + // if only the date parsing failed we still get the rest of the + // envelop structure in a valid state. + // Date parsing errors are fairly common and it's better to be + // slightly off than to not be able to read the mails at all + // hence we continue here + log.Errorf("invalid Date header: %v", err) } return &models.Envelope{ Date: date, Subject: subj, MessageId: msgID, - From: from, - ReplyTo: replyTo, - To: to, - Cc: cc, - Bcc: bcc, + From: parseAddressList(h, "from"), + ReplyTo: parseAddressList(h, "reply-to"), + To: parseAddressList(h, "to"), + Cc: parseAddressList(h, "cc"), + Bcc: parseAddressList(h, "bcc"), InReplyTo: irt, - }, err + } } // If the date is formatted like ...... -0500 (EST), parser takes the EST part @@ -227,11 +207,11 @@ } bestDate = t } - text, err := h.Text("date") + text := h.Get("date") // sometimes, no error occurs but the date is empty. // In this case, guess time from received header field - if err != nil || text == "" { + if text == "" { t, err := parseReceivedHeader(h) if err == nil { return t, nil @@ -277,13 +257,22 @@ return time.Parse(time.RFC1123Z, dateRe.FindString(guess)) } -func parseAddressList(h *mail.Header, key string) ([]*mail.Address, error) { +func parseAddressList(h *mail.Header, key string) []*mail.Address { addrs, err := h.AddressList(key) if len(addrs) == 0 { // Only consider the error if the returned address list is empty // Sometimes, we get a list of addresses and unknown charset // errors which are not fatal. - return nil, err + if val := h.Get(key); val != "" { + if err != nil { + log.Errorf("%s: %s: %v", key, val, err) + } + // Header value is not empty but parsing completely + // failed. Return something so that the message can at + // least be displayed. + return []*mail.Address{{Name: val}} + } + return nil } for _, addr := range addrs { // Handle invalid headers with quoted *AND* encoded names @@ -293,7 +282,7 @@ } } // If we got at least one address, ignore any returned error. - return addrs, nil + return addrs } // RawMessage is an interface that describes a raw message @@ -324,15 +313,7 @@ return nil, fmt.Errorf("could not get structure: %w", err) } h := &mail.Header{Header: msg.Header} - env, err := parseEnvelope(h) - if err != nil && !errors.Is(err, DateParseError) { - return nil, fmt.Errorf("could not parse envelope: %w", err) - // if only the date parsing failed we still get the rest of the - // envelop structure in a valid state. - // Date parsing errors are fairly common and it's better to be - // slightly off than to not be able to read the mails at all - // hence we continue here - } + env := parseEnvelope(h) recDate, _ := parseReceivedHeader(h) if recDate.IsZero() { // better than nothing, if incorrect @@ -374,15 +355,7 @@ return nil, fmt.Errorf("could not read message: %w", err) } h := &mail.Header{Header: msg.Header} - env, err := parseEnvelope(h) - if err != nil && !errors.Is(err, DateParseError) { - return nil, fmt.Errorf("could not parse envelope: %w", err) - // if only the date parsing failed we still get the rest of the - // envelop structure in a valid state. - // Date parsing errors are fairly common and it's better to be - // slightly off than to not be able to read the mails at all - // hence we continue here - } + env := parseEnvelope(h) recDate, _ := parseReceivedHeader(h) if recDate.IsZero() { // better than nothing, if incorrect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/lib/rfc822/message_test.go new/aerc-0.18.1/lib/rfc822/message_test.go --- old/aerc-0.18.0/lib/rfc822/message_test.go 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/lib/rfc822/message_test.go 2024-07-15 22:50:02.000000000 +0200 @@ -114,10 +114,11 @@ func TestParseAddressList(t *testing.T) { header := mail.HeaderFromMap(map[string][]string{ - "From": {`"=?utf-8?B?U21pZXRhbnNraSwgV29qY2llY2ggVGFkZXVzeiBpbiBUZWFtcw==?=" <nore...@email.teams.microsoft.com>`}, - "To": {`=?UTF-8?q?Oc=C3=A9ane_de_Seazon?= <he...@seazon.fr>`}, - "Cc": {`=?utf-8?b?0KjQsNCz0L7QsiDQk9C10L7RgNCz0LjQuSB2aWEgZGlzY3Vzcw==?= <ovs-disc...@openvswitch.org>`}, - "Bcc": {`"Foo, Baz Bar" <~foo/b...@bar.org>`}, + "From": {`"=?utf-8?B?U21pZXRhbnNraSwgV29qY2llY2ggVGFkZXVzeiBpbiBUZWFtcw==?=" <nore...@email.teams.microsoft.com>`}, + "To": {`=?UTF-8?q?Oc=C3=A9ane_de_Seazon?= <he...@seazon.fr>`}, + "Cc": {`=?utf-8?b?0KjQsNCz0L7QsiDQk9C10L7RgNCz0LjQuSB2aWEgZGlzY3Vzcw==?= <ovs-disc...@openvswitch.org>`}, + "Bcc": {`"Foo, Baz Bar" <~foo/b...@bar.org>`}, + "Reply-To": {`Someone`}, }) type vector struct { kind string @@ -151,12 +152,17 @@ name: "Smietanski, Wojciech Tadeusz in Teams", email: "nore...@email.teams.microsoft.com", }, + { + kind: "no email", + header: "Reply-To", + name: "Someone", + email: "", + }, } for _, vec := range vectors { t.Run(vec.kind, func(t *testing.T) { - addrs, err := parseAddressList(&header, vec.header) - assert.Nil(t, err) + addrs := parseAddressList(&header, vec.header) assert.Len(t, addrs, 1) assert.Equal(t, vec.name, addrs[0].Name) assert.Equal(t, vec.email, addrs[0].Address) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aerc-0.18.0/lib/send/smtp.go new/aerc-0.18.1/lib/send/smtp.go --- old/aerc-0.18.0/lib/send/smtp.go 2024-07-02 22:30:51.000000000 +0200 +++ new/aerc-0.18.1/lib/send/smtp.go 2024-07-15 22:50:02.000000000 +0200 @@ -39,7 +39,7 @@ return conn, nil } -func connectSmtps(host string) (*smtp.Client, error) { +func connectSmtps(host string, domain string) (*smtp.Client, error) { serverName := host if !strings.ContainsRune(host, ':') { host += ":465" // Default to smtps port @@ -52,6 +52,13 @@ if err != nil { return nil, errors.Wrap(err, "smtp.DialTLS") } + if domain != "" { + err := conn.Hello(domain) + if err != nil { + conn.Close() + return nil, errors.Wrap(err, "Hello") + } + } return conn, nil } @@ -85,7 +92,7 @@ case "smtp+insecure": conn, err = connectSmtp(false, uri.Host, domain) case "smtps": - conn, err = connectSmtps(uri.Host) + conn, err = connectSmtps(uri.Host, domain) default: return nil, fmt.Errorf("not a smtp protocol %s", protocol) } ++++++ vendor.tar.zst ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/emersion/go-smtp/.build.yml new/vendor/github.com/emersion/go-smtp/.build.yml --- old/vendor/github.com/emersion/go-smtp/.build.yml 2024-07-02 22:58:05.000000000 +0200 +++ new/vendor/github.com/emersion/go-smtp/.build.yml 2024-07-16 00:59:03.000000000 +0200 @@ -1,4 +1,4 @@ -image: alpine/edge +image: alpine/latest packages: - go sources: @@ -8,10 +8,13 @@ tasks: - build: | cd go-smtp - go build -v ./... + go build -race -v ./... - test: | cd go-smtp - go test -coverprofile=coverage.txt -covermode=atomic ./... + go test -race -coverprofile=coverage.txt -covermode=atomic ./... - coverage: | cd go-smtp go tool cover -html=coverage.txt -o ~/coverage.html + - gofmt: | + cd go-smtp + test -z $(gofmt -l .) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/emersion/go-smtp/client.go new/vendor/github.com/emersion/go-smtp/client.go --- old/vendor/github.com/emersion/go-smtp/client.go 2024-07-02 22:58:05.000000000 +0200 +++ new/vendor/github.com/emersion/go-smtp/client.go 2024-07-16 00:59:03.000000000 +0200 @@ -29,6 +29,8 @@ lmtp bool ext map[string]string // supported extensions localName string // the name to use in HELO/EHLO/LHLO + didGreet bool // whether we've received greeting from server + greetError error // the error from the greeting didHello bool // whether we've said HELO/EHLO/LHLO helloError error // the error from the hello rcpts []string // recipients accumulated for the current session @@ -44,9 +46,7 @@ // 30 seconds was chosen as it's the same duration as http.DefaultTransport's // timeout. -const defaultTimeout = 30 * time.Second - -var defaultDialer = net.Dialer{Timeout: defaultTimeout} +var defaultDialer = net.Dialer{Timeout: 30 * time.Second} // Dial returns a new Client connected to an SMTP server at addr. The addr must // include a port, as in "mail.example.com:smtp". @@ -181,30 +181,42 @@ } func (c *Client) greet() error { + if c.didGreet { + return c.greetError + } + // Initial greeting timeout. RFC 5321 recommends 5 minutes. c.conn.SetDeadline(time.Now().Add(c.CommandTimeout)) defer c.conn.SetDeadline(time.Time{}) - _, _, err := c.text.ReadResponse(220) + c.didGreet = true + _, _, err := c.readResponse(220) if err != nil { + c.greetError = err c.text.Close() - if protoErr, ok := err.(*textproto.Error); ok { - return toSMTPErr(protoErr) - } - return err } - return nil + return c.greetError } // hello runs a hello exchange if needed. func (c *Client) hello() error { - if !c.didHello { - c.didHello = true - if err := c.greet(); err != nil { - c.helloError = err - } else if err := c.ehlo(); err != nil { + if c.didHello { + return c.helloError + } + + if err := c.greet(); err != nil { + return err + } + + c.didHello = true + if err := c.ehlo(); err != nil { + var smtpError *SMTPError + if errors.As(err, &smtpError) && (smtpError.Code == 500 || smtpError.Code == 502) { + // The server doesn't support EHLO, fallback to HELO c.helloError = c.helo() + } else { + c.helloError = err } } return c.helloError @@ -228,6 +240,14 @@ return c.hello() } +func (c *Client) readResponse(expectCode int) (int, string, error) { + code, msg, err := c.text.ReadResponse(expectCode) + if protoErr, ok := err.(*textproto.Error); ok { + err = toSMTPErr(protoErr) + } + return code, msg, err +} + // cmd is a convenience function that sends a command and returns the response // textproto.Error returned by c.text.ReadResponse is converted into SMTPError. func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) { @@ -240,15 +260,8 @@ } c.text.StartResponse(id) defer c.text.EndResponse(id) - code, msg, err := c.text.ReadResponse(expectCode) - if err != nil { - if protoErr, ok := err.(*textproto.Error); ok { - smtpErr := toSMTPErr(protoErr) - return code, smtpErr.Message, smtpErr - } - return code, msg, err - } - return code, msg, nil + + return c.readResponse(expectCode) } // helo sends the HELO greeting to the server. It should be used only when the @@ -314,7 +327,8 @@ testHookStartTLS(config) } c.setConn(tls.Client(c.conn, config)) - return c.ehlo() + c.didHello = false + return nil } // TLSConnectionState returns the client's TLS connection state. @@ -358,8 +372,13 @@ if err != nil { return err } - resp64 := make([]byte, encoding.EncodedLen(len(resp))) - encoding.Encode(resp64, resp) + var resp64 []byte + if len(resp) > 0 { + resp64 = make([]byte, encoding.EncodedLen(len(resp))) + encoding.Encode(resp64, resp) + } else if resp != nil { + resp64 = []byte{'='} + } code, msg64, err := c.cmd(0, strings.TrimSpace(fmt.Sprintf("AUTH %s %s", mech, resp64))) for err == nil { var msg []byte @@ -541,10 +560,10 @@ if d.c.lmtp { for expectedResponses > 0 { rcpt := d.c.rcpts[len(d.c.rcpts)-expectedResponses] - if _, _, err := d.c.text.ReadResponse(250); err != nil { - if protoErr, ok := err.(*textproto.Error); ok { + if _, _, err := d.c.readResponse(250); err != nil { + if smtpErr, ok := err.(*SMTPError); ok { if d.statusCb != nil { - d.statusCb(rcpt, toSMTPErr(protoErr)) + d.statusCb(rcpt, smtpErr) } } else { return err @@ -555,11 +574,8 @@ expectedResponses-- } } else { - _, _, err := d.c.text.ReadResponse(250) + _, _, err := d.c.readResponse(250) if err != nil { - if protoErr, ok := err.(*textproto.Error); ok { - return toSMTPErr(protoErr) - } return err } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/emersion/go-smtp/conn.go new/vendor/github.com/emersion/go-smtp/conn.go --- old/vendor/github.com/emersion/go-smtp/conn.go 2024-07-02 22:58:05.000000000 +0200 +++ new/vendor/github.com/emersion/go-smtp/conn.go 2024-07-16 00:59:03.000000000 +0200 @@ -232,12 +232,7 @@ sess, err := c.server.Backend.NewSession(c) if err != nil { c.helo = "" - - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(451, EnhancedCode{4, 0, 0}, err.Error()) + c.writeError(451, EnhancedCode{4, 0, 0}, err) return } @@ -421,11 +416,7 @@ } if err := c.Session().Mail(from, opts); err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(451, EnhancedCode{4, 0, 0}, err.Error()) + c.writeError(451, EnhancedCode{4, 0, 0}, err) return } @@ -725,11 +716,7 @@ } if err := c.Session().Rcpt(recipient, opts); err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(451, EnhancedCode{4, 0, 0}, err.Error()) + c.writeError(451, EnhancedCode{4, 0, 0}, err) return } c.recipients = append(c.recipients, recipient) @@ -787,7 +774,7 @@ var ir []byte if len(parts) > 1 { var err error - ir, err = base64.StdEncoding.DecodeString(parts[1]) + ir, err = decodeSASLResponse(parts[1]) if err != nil { c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") return @@ -796,11 +783,7 @@ sasl, err := c.auth(mechanism) if err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - } else { - c.writeResponse(454, EnhancedCode{4, 7, 0}, err.Error()) - } + c.writeError(454, EnhancedCode{4, 7, 0}, err) return } @@ -808,11 +791,7 @@ for { challenge, done, err := sasl.Next(response) if err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(454, EnhancedCode{4, 7, 0}, err.Error()) + c.writeError(454, EnhancedCode{4, 7, 0}, err) return } @@ -837,7 +816,7 @@ return } - response, err = base64.StdEncoding.DecodeString(encoded) + response, err = decodeSASLResponse(encoded) if err != nil { c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") return @@ -848,6 +827,13 @@ c.didAuth = true } +func decodeSASLResponse(s string) ([]byte, error) { + if s == "=" { + return []byte{}, nil + } + return base64.StdEncoding.DecodeString(s) +} + func (c *Conn) authMechanisms() []string { if authSession, ok := c.Session().(AuthSession); ok { return authSession.AuthMechanisms() @@ -930,7 +916,7 @@ } r := newDataReader(c) - code, enhancedCode, msg := toSMTPStatus(c.Session().Data(r)) + code, enhancedCode, msg := dataErrorToStatus(c.Session().Data(r)) r.limited = false io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed c.writeResponse(code, enhancedCode, msg) @@ -1027,7 +1013,7 @@ // the whole chunk. io.Copy(ioutil.Discard, chunk) - c.writeResponse(toSMTPStatus(err)) + c.writeResponse(dataErrorToStatus(err)) if err == errPanic { c.Close() @@ -1050,11 +1036,11 @@ if c.server.LMTP { c.bdatStatus.fillRemaining(err) for i, rcpt := range c.recipients { - code, enchCode, msg := toSMTPStatus(<-c.bdatStatus.status[i]) + code, enchCode, msg := dataErrorToStatus(<-c.bdatStatus.status[i]) c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg) } } else { - c.writeResponse(toSMTPStatus(err)) + c.writeResponse(dataErrorToStatus(err)) } if err == errPanic { @@ -1189,7 +1175,7 @@ } for i, rcpt := range c.recipients { - code, enchCode, msg := toSMTPStatus(<-status.status[i]) + code, enchCode, msg := dataErrorToStatus(<-status.status[i]) c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg) } @@ -1200,7 +1186,7 @@ } } -func toSMTPStatus(err error) (code int, enchCode EnhancedCode, msg string) { +func dataErrorToStatus(err error) (code int, enchCode EnhancedCode, msg string) { if err != nil { if smtperr, ok := err.(*SMTPError); ok { return smtperr.Code, smtperr.EnhancedCode, smtperr.Message @@ -1253,6 +1239,14 @@ } } +func (c *Conn) writeError(code int, enhCode EnhancedCode, err error) { + if smtpErr, ok := err.(*SMTPError); ok { + c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) + } else { + c.writeResponse(code, enhCode, err.Error()) + } +} + // Reads a line of input func (c *Conn) readLine() (string, error) { if c.server.ReadTimeout != 0 { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/emersion/go-smtp/server.go new/vendor/github.com/emersion/go-smtp/server.go --- old/vendor/github.com/emersion/go-smtp/server.go 2024-07-02 22:58:05.000000000 +0200 +++ new/vendor/github.com/emersion/go-smtp/server.go 2024-07-16 00:59:03.000000000 +0200 @@ -12,9 +12,7 @@ "time" ) -var ( - ErrServerClosed = errors.New("smtp: server already closed") -) +var ErrServerClosed = errors.New("smtp: server already closed") // Logger interface is used by Server to report unexpected internal errors. type Logger interface { @@ -122,7 +120,7 @@ err := s.handleConn(newConn(c, s)) if err != nil { - s.ErrorLog.Printf("handler error: %s", err) + s.ErrorLog.Printf("error handling %v: %s", c.RemoteAddr(), err) } }() } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2024-07-02 22:58:05.000000000 +0200 +++ new/vendor/modules.txt 2024-07-16 00:59:03.000000000 +0200 @@ -99,7 +99,7 @@ # github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 ## explicit; go 1.12 github.com/emersion/go-sasl -# github.com/emersion/go-smtp v0.21.0 +# github.com/emersion/go-smtp v0.21.3 ## explicit; go 1.13 github.com/emersion/go-smtp # github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594