From db55b9ce393c72308ddfafef6e352202a46c93cf Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 14 Aug 2023 13:05:31 +0200 Subject: [PATCH] Introduce RcptOptions Some SMTP extensions add new RCPT parameters. Add a struct to be able to support these. --- backend.go | 2 +- backendutil/transform.go | 4 ++-- backendutil/transform_test.go | 2 +- client.go | 7 +++++-- client_test.go | 10 +++++----- cmd/smtp-debug-server/main.go | 2 +- conn.go | 3 ++- example_test.go | 4 ++-- server_test.go | 2 +- smtp.go | 6 ++++-- 10 files changed, 24 insertions(+), 18 deletions(-) diff --git a/backend.go b/backend.go index 2f28880..1f3fdae 100644 --- a/backend.go +++ b/backend.go @@ -43,7 +43,7 @@ type Session interface { // Set return path for currently processed message. Mail(from string, opts *MailOptions) error // Add recipient for currently processed message. - Rcpt(to string) error + Rcpt(to string, opts *RcptOptions) error // Set currently processed message contents and send it. // // r must be consumed before Data returns. diff --git a/backendutil/transform.go b/backendutil/transform.go index 2a2fe9f..b3ce429 100755 --- a/backendutil/transform.go +++ b/backendutil/transform.go @@ -48,7 +48,7 @@ func (s *transformSession) Mail(from string, opts *smtp.MailOptions) error { return s.Session.Mail(from, opts) } -func (s *transformSession) Rcpt(to string) error { +func (s *transformSession) Rcpt(to string, opts *smtp.RcptOptions) error { if s.be.TransformRcpt != nil { var err error to, err = s.be.TransformRcpt(to) @@ -56,7 +56,7 @@ func (s *transformSession) Rcpt(to string) error { return err } } - return s.Session.Rcpt(to) + return s.Session.Rcpt(to, opts) } func (s *transformSession) Data(r io.Reader) error { diff --git a/backendutil/transform_test.go b/backendutil/transform_test.go index 50961cf..0c742c1 100755 --- a/backendutil/transform_test.go +++ b/backendutil/transform_test.go @@ -65,7 +65,7 @@ func (s *session) Mail(from string, opts *smtp.MailOptions) error { return nil } -func (s *session) Rcpt(to string) error { +func (s *session) Rcpt(to string, opts *smtp.RcptOptions) error { s.msg.To = append(s.msg.To, to) return nil } diff --git a/client.go b/client.go index 7a99a10..bcb22ec 100644 --- a/client.go +++ b/client.go @@ -414,8 +414,11 @@ func (c *Client) Mail(from string, opts *MailOptions) error { // A call to Rcpt must be preceded by a call to Mail and may be followed by // a Data call or another Rcpt call. // +// If opts is not nil, RCPT arguments provided in the structure will be added +// to the command. Handling of unsupported options depends on the extension. +// // If server returns an error, it will be of type *SMTPError. -func (c *Client) Rcpt(to string) error { +func (c *Client) Rcpt(to string, opts *RcptOptions) error { if err := validateLine(to); err != nil { return err } @@ -531,7 +534,7 @@ func (c *Client) SendMail(from string, to []string, r io.Reader) error { return err } for _, addr := range to { - if err = c.Rcpt(addr); err != nil { + if err = c.Rcpt(addr, nil); err != nil { return err } } diff --git a/client_test.go b/client_test.go index 4fbe2cd..17eac3c 100644 --- a/client_test.go +++ b/client_test.go @@ -120,7 +120,7 @@ func TestBasic(t *testing.T) { t.Fatalf("AUTH failed: %s", err) } - if err := c.Rcpt("golang-nuts@googlegroups.com>\r\nDATA\r\nInjected message body\r\n.\r\nQUIT\r\n"); err == nil { + if err := c.Rcpt("golang-nuts@googlegroups.com>\r\nDATA\r\nInjected message body\r\n.\r\nQUIT\r\n", nil); err == nil { t.Fatalf("RCPT should have failed due to a message injection attempt") } if err := c.Mail("user@gmail.com>\r\nDATA\r\nAnother injected message body\r\n.\r\nQUIT\r\n", nil); err == nil { @@ -129,7 +129,7 @@ func TestBasic(t *testing.T) { if err := c.Mail("user@gmail.com", nil); err != nil { t.Fatalf("MAIL failed: %s", err) } - if err := c.Rcpt("golang-nuts@googlegroups.com"); err != nil { + if err := c.Rcpt("golang-nuts@googlegroups.com", nil); err != nil { t.Fatalf("RCPT failed: %s", err) } msg := `From: user@gmail.com @@ -797,7 +797,7 @@ func TestLMTP(t *testing.T) { if err := c.Mail("user@gmail.com", nil); err != nil { t.Fatalf("MAIL failed: %s", err) } - if err := c.Rcpt("golang-nuts@googlegroups.com"); err != nil { + if err := c.Rcpt("golang-nuts@googlegroups.com", nil); err != nil { t.Fatalf("RCPT failed: %s", err) } msg := `From: user@gmail.com @@ -882,10 +882,10 @@ func TestLMTPData(t *testing.T) { if err := c.Mail("user@gmail.com", nil); err != nil { t.Fatalf("MAIL failed: %s", err) } - if err := c.Rcpt("golang-nuts@googlegroups.com"); err != nil { + if err := c.Rcpt("golang-nuts@googlegroups.com", nil); err != nil { t.Fatalf("RCPT failed: %s", err) } - if err := c.Rcpt("golang-not-nuts@googlegroups.com"); err != nil { + if err := c.Rcpt("golang-not-nuts@googlegroups.com", nil); err != nil { t.Fatalf("RCPT failed: %s", err) } msg := `From: user@gmail.com diff --git a/cmd/smtp-debug-server/main.go b/cmd/smtp-debug-server/main.go index 271a7ef..d54f4fc 100644 --- a/cmd/smtp-debug-server/main.go +++ b/cmd/smtp-debug-server/main.go @@ -31,7 +31,7 @@ func (s *session) Mail(from string, opts *smtp.MailOptions) error { return nil } -func (s *session) Rcpt(to string) error { +func (s *session) Rcpt(to string, opts *smtp.RcptOptions) error { return nil } diff --git a/conn.go b/conn.go index e412cb7..d060bbe 100644 --- a/conn.go +++ b/conn.go @@ -473,7 +473,8 @@ func (c *Conn) handleRcpt(arg string) { return } - if err := c.Session().Rcpt(recipient); err != nil { + opts := &RcptOptions{} + if err := c.Session().Rcpt(recipient, opts); err != nil { if smtpErr, ok := err.(*SMTPError); ok { c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) return diff --git a/example_test.go b/example_test.go index d747688..b84a6e9 100644 --- a/example_test.go +++ b/example_test.go @@ -28,7 +28,7 @@ func ExampleDial() { if err := c.Mail("sender@example.org", nil); err != nil { log.Fatal(err) } - if err := c.Rcpt("recipient@example.net"); err != nil { + if err := c.Rcpt("recipient@example.net", nil); err != nil { log.Fatal(err) } @@ -113,7 +113,7 @@ func (s *Session) Mail(from string, opts *smtp.MailOptions) error { return nil } -func (s *Session) Rcpt(to string) error { +func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error { log.Println("Rcpt to:", to) return nil } diff --git a/server_test.go b/server_test.go index 7e30044..61c1286 100644 --- a/server_test.go +++ b/server_test.go @@ -94,7 +94,7 @@ func (s *session) Mail(from string, opts *smtp.MailOptions) error { return nil } -func (s *session) Rcpt(to string) error { +func (s *session) Rcpt(to string, opts *smtp.RcptOptions) error { s.msg.To = append(s.msg.To, to) return nil } diff --git a/smtp.go b/smtp.go index 50c68fb..cfb5cd4 100644 --- a/smtp.go +++ b/smtp.go @@ -24,8 +24,7 @@ const ( BodyBinaryMIME BodyType = "BINARYMIME" ) -// MailOptions contains custom arguments that were -// passed as an argument to the MAIL command. +// MailOptions contains parameters for the MAIL command. type MailOptions struct { // Value of BODY= argument, 7BIT, 8BITMIME or BINARYMIME. Body BodyType @@ -52,3 +51,6 @@ type MailOptions struct { // Defined in RFC 4954. Auth *string } + +// RcptOptions contains parameters for the RCPT command. +type RcptOptions struct{}