nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: emersion/go-imap
Pull request #184 (2018-05-25 12:06:24)

Fix login state checking condition
The current condition:

```
c.State() != imap.NotAuthenticatedState
```

give an confused message already login since when client state is `imap.LogoutState`. The client is already login only if it's in `imap.AuthenticatedState` or `imap.SelectedState`.


Patch:
diff --git a/client/cmd_noauth.go b/client/cmd_noauth.go
index ddbc8f0..06256c3 100644
--- a/client/cmd_noauth.go
+++ b/client/cmd_noauth.go
@@ -115,7 +115,7 @@ func (c *Client) Authenticate(auth sasl.Client) error {
 // Login identifies the client to the server and carries the plaintext password
 // authenticating this user.
 func (c *Client) Login(username, password string) error {
-	if c.State() != imap.NotAuthenticatedState {
+	if state := c.State(); state == imap.AuthenticatedState || state == imap.SelectedState {
 		return ErrAlreadyLoggedIn
 	}
 


Test patch:
diff --git a/client/cmd_noauth_test.go b/client/cmd_noauth_test.go
index 6cb9b0b..5687c72 100644
--- a/client/cmd_noauth_test.go
+++ b/client/cmd_noauth_test.go
@@ -158,3 +158,50 @@ func TestClient_Login_Error(t *testing.T) {
 		t.Errorf("c.State() = %v, want %v", state, imap.NotAuthenticatedState)
 	}
 }
+
+func TestClient_Login_State_Allowed(t *testing.T) {
+	c, s := newTestClient(t)
+	defer s.Close()
+
+	done := make(chan error, 1)
+	go func() {
+		done <- c.Login("username", "password")
+	}()
+
+	tag, cmd := s.ScanCmd()
+	if cmd != "LOGIN username password" {
+		t.Fatalf("client sent command %v, want LOGIN username password", cmd)
+	}
+	s.WriteString(tag + " OK LOGIN completed\r\n")
+
+	if err := <-done; err != nil {
+		t.Fatalf("c.Login() = %v", err)
+	}
+
+	if state := c.State(); state != imap.AuthenticatedState {
+		t.Errorf("c.State() = %v, want %v", state, imap.AuthenticatedState)
+	}
+
+	go func() {
+		done <- c.Login("username", "password")
+	}()
+	if err := <-done; err != ErrAlreadyLoggedIn {
+		t.Fatalf("c.Login() = %v, want %v", err, ErrAlreadyLoggedIn)
+	}
+
+	go func() {
+		done <- c.Logout()
+	}()
+
+	s.ScanCmd()
+	s.WriteString("* BYE Client asked to close the connection.\r\n")
+	s.WriteString(tag + " OK LOGOUT completed\r\n")
+
+	if err := <-done; err != nil {
+		t.Fatalf("c.Logout() = %v", err)
+	}
+
+	if err := c.Login("username", "password"); err == ErrAlreadyLoggedIn {
+		t.Errorf("Client is logout, login must not give %v", ErrAlreadyLoggedIn)
+	}
+}
Repository: emersion/go-imap
Pull request #417 (2021-02-12 17:01:20)

utf7: reset ascii boolean after decode while destination is short.
Hello,  
Thanks for this useful library.  
In Japanese environment, I found a bug in decoding UTF-7.

In `decoder.Transform`, there are two cases to return `transform.ErrShortDst`.  
The second case occurs after decode Base64. This is the point.  

When whole decoded string cannot be passed to `dst`, next call of `decoder.Transform` will begin with decoding Base64(repeat last decode and try to copy them to `dst` again).  
So, `decoder.ascii` should be set `true` before return `transform.ErrShortDst`. Otherwise, misdetection of Null shift makes error.


Patch:
diff --git a/utf7/decoder.go b/utf7/decoder.go
index 843fb8e..cfcba8c 100644
--- a/utf7/decoder.go
+++ b/utf7/decoder.go
@@ -77,9 +77,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
 		}
 
 		if nDst+len(b) > len(dst) {
-			if atEOF {
-				d.ascii = true
-			}
+			d.ascii = true
 			err = transform.ErrShortDst
 			return
 		}


Test patch:
diff --git a/utf7/decoder_test.go b/utf7/decoder_test.go
index 483f704..5ff9fc2 100644
--- a/utf7/decoder_test.go
+++ b/utf7/decoder_test.go
@@ -1,6 +1,7 @@
 package utf7_test
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/emersion/go-imap/utf7"
@@ -76,6 +77,10 @@ var decode = []struct {
 	{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &2D3eCg- &2D3eCw- &2D3eDg-",
 		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \U0001f60a \U0001f60b \U0001f60e", true},
 
+	// Long input in Base64 between short ASCII
+	{"00000000000000000000 &MEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEIwQjBCMEI- 00000000000000000000",
+		"00000000000000000000 " + strings.Repeat("\U00003042", 37) + " 00000000000000000000", true},
+
 	// ASCII in Base64
 	{"&AGE-", "", false},            // "a"
 	{"&ACY-", "", false},            // "&"
Repository: emersion/go-imap
Pull request #453 (2021-12-22 06:57:15)

Add IDLE to capabilities list
IDLE was changed into a built-in extension, but the default capabilities list was never updated to include it. The go-imap/client library (and probably any other sane IMAP client) resorts to polling if the IDLE cap isn't present. This PR simply adds IDLE to the list.

emersion: `IDLE` is only supported if the server implements it.
emersion: Hm, scratch that, we should always support it indeed.

Can you update the tests accordingly?

Patch:
diff --git a/server/conn.go b/server/conn.go
index 8929852..37a0356 100644
--- a/server/conn.go
+++ b/server/conn.go
@@ -163,7 +163,7 @@ func (c *conn) Close() error {
 }
 
 func (c *conn) Capabilities() []string {
-	caps := []string{"IMAP4rev1", "LITERAL+", "SASL-IR", "CHILDREN", "UNSELECT", "MOVE"}
+	caps := []string{"IMAP4rev1", "LITERAL+", "SASL-IR", "CHILDREN", "UNSELECT", "MOVE", "IDLE"}
 
 	appendLimitSet := false
 	if c.ctx.State == imap.AuthenticatedState {


Test patch:
diff --git a/server/server_test.go b/server/server_test.go
index e0054bc..32bfe85 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 // Extnesions that are always advertised by go-imap server.
-const builtinExtensions = "LITERAL+ SASL-IR CHILDREN UNSELECT MOVE APPENDLIMIT"
+const builtinExtensions = "LITERAL+ SASL-IR CHILDREN UNSELECT MOVE IDLE APPENDLIMIT"
 
 func testServer(t *testing.T) (s *server.Server, conn net.Conn) {
 	bkd := memory.New()
Repository: emersion/go-smtp
Pull request #57 (2019-07-26 07:36:41)

do not abort mailfrom if FROM is <> 
The change was rebased with changes from #55

A FROM Adress <> is valid and used for NDR Reports / MailerDaemon mails.

codecov[bot]: # [Codecov](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=h1) Report
> Merging [#57](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=desc) into [master](https://codecov.io/gh/emersion/go-smtp/commit/a85189bd8e0e0e608c9e6e0314fdef5f611a151b?src=pr&el=desc) will **decrease** coverage by `0.35%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/emersion/go-smtp/pull/57/graphs/tree.svg?width=650&token=vj7HO8ABwC&height=150&src=pr)](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #57      +/-   ##
==========================================
- Coverage   62.77%   62.41%   -0.36%     
==========================================
  Files           7        7              
  Lines         736      737       +1     
==========================================
- Hits          462      460       -2     
- Misses        201      203       +2     
- Partials       73       74       +1
```


| [Impacted Files](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [conn.go](https://codecov.io/gh/emersion/go-smtp/pull/57/diff?src=pr&el=tree#diff-Y29ubi5nbw==) | `59.75% <100%> (-0.81%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=footer). Last update [a85189b...81787ca](https://codecov.io/gh/emersion/go-smtp/pull/57?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/conn.go b/conn.go
index a4eac51..565459d 100644
--- a/conn.go
+++ b/conn.go
@@ -263,11 +263,12 @@ func (c *Conn) handleMail(arg string) {
 			return
 		}
 	}
-	from := strings.Trim(fromArgs[0], "<> ")
+	from := fromArgs[0]
 	if from == "" {
 		c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:<address>")
 		return
 	}
+	from = strings.Trim(from, "<>")
 
 	// This is where the Conn may put BODY=8BITMIME, but we already
 	// read the DATA as bytes, so it does not effect our processing.


Test patch:
diff --git a/server_test.go b/server_test.go
index b263356..9cd6e95 100644
--- a/server_test.go
+++ b/server_test.go
@@ -224,7 +224,7 @@ func TestServerEmptyFrom2(t *testing.T) {
 
 	io.WriteString(c, "MAIL FROM:<>\r\n")
 	scanner.Scan()
-	if strings.HasPrefix(scanner.Text(), "250 ") {
+	if !strings.HasPrefix(scanner.Text(), "250 ") {
 		t.Fatal("Invalid MAIL response:", scanner.Text())
 	}
Repository: emersion/go-smtp
Pull request #232 (2023-08-28 23:37:30)

Fix broken encodeXtext()
With this method call:
```go
email := "e=mc2@example.com"
c.Mail(email, &MailOptions{Auth: &email})
```
this should be sent to the server:
```
MAIL FROM:<e=mc2@example.com> AUTH=e+3Dmc2@example.com
```

However, following thing is sent:
```
MAIL FROM:<e=mc2@example.com> AUTH=e+65+3D=+3Dm+6Dc+632+32@+40e+65x+78a+61m+6Dp+70l+6Ce+65.+2Ec+63o+6Fm+6D
```
This PR will fix it.

emersion: Overall LGTM, the old code was definitely broken.

Patch:
diff --git a/conn.go b/conn.go
index d060bbe..1254175 100644
--- a/conn.go
+++ b/conn.go
@@ -426,16 +426,14 @@ func encodeXtext(raw string) string {
 	out.Grow(len(raw))
 
 	for _, ch := range raw {
-		if ch == '+' || ch == '=' {
+		switch {
+		case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
+			// printable non-space US-ASCII except '+' and '='
+			out.WriteRune(ch)
+		default:
 			out.WriteRune('+')
 			out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
 		}
-		if ch > '!' && ch < '~' { // printable non-space US-ASCII
-			out.WriteRune(ch)
-		}
-		// Non-ASCII.
-		out.WriteRune('+')
-		out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
 	}
 	return out.String()
 }


Test patch:
diff --git a/client_test.go b/client_test.go
index 17eac3c..ba8dfe3 100644
--- a/client_test.go
+++ b/client_test.go
@@ -931,3 +931,29 @@ Goodbye.`
 		t.Fatalf("QUIT failed: %s", err)
 	}
 }
+
+func TestClientXtext(t *testing.T) {
+	server := "220 hello world\r\n" +
+		"200 some more"
+	var wrote bytes.Buffer
+	var fake faker
+	fake.ReadWriter = struct {
+		io.Reader
+		io.Writer
+	}{
+		strings.NewReader(server),
+		&wrote,
+	}
+	c, err := NewClient(fake, "fake.host")
+	if err != nil {
+		t.Fatalf("NewClient: %v", err)
+	}
+	c.didHello = true
+	c.ext = map[string]string{"AUTH": "PLAIN"}
+	email := "e=mc2@example.com"
+	c.Mail(email, &MailOptions{Auth: &email})
+	c.Close()
+	if got, want := wrote.String(), "MAIL FROM:<e=mc2@example.com> AUTH=e+3Dmc2@example.com\r\n"; got != want {
+		t.Errorf("wrote %q; want %q", got, want)
+	}
+}
Repository: emersion/go-smtp
Pull request #244 (2023-12-25 14:27:38)

Remove DotLF to EOFState case
Hi, this should be enough for closing #243. However, since this is a small code change, I did not test anything.

emersion: I've written a small test for this here: https://github.com/emersion/go-smtp/tree/smtp-smuggling-test

Could you cherry-pick that commit and make sure that it passes?
blmayer: Yes.
blmayer: The test failed due to a timeout. I think `scanner.Scan()` doesn't return, as it is expecting the correct end. I added a smaller timeout to that test, and removed the check for an incorrect end, since the server doesn't error out: it is correctly expecting more data.

See if you agree with this.
emersion: Ah right, of course. I've pushed another variant of the test which shouldn't cause a timeout.
blmayer: Cool, much better, I had to adjust the check's text.
emersion: For reference, excerpt from the RFC:

> The custom of accepting lines ending only in <LF>, as a concession to
> non-conforming behavior on the part of some UNIX systems, has proven
> to cause more interoperability problems than it solves, and SMTP
> server systems MUST NOT do this, even in the name of improved
> robustness.  In particular, the sequence "<LF>.<LF>" (bare line
> feeds, without carriage returns) MUST NOT be treated as equivalent to
> <CRLF>.<CRLF> as the end of mail data indication.

Patch:
diff --git a/data.go b/data.go
index b4621e4..d2d3d8f 100644
--- a/data.go
+++ b/data.go
@@ -112,11 +112,6 @@ func (r *dataReader) Read(b []byte) (n int, err error) {
 				r.state = stateDotCR
 				continue
 			}
-			if c == '\n' {
-				r.state = stateEOF
-				continue
-			}
-
 			r.state = stateData
 		case stateDotCR:
 			if c == '\n' {


Test patch:
diff --git a/server_test.go b/server_test.go
index daba4ce..759385b 100644
--- a/server_test.go
+++ b/server_test.go
@@ -733,6 +733,37 @@ func TestServer_tooLongMessage(t *testing.T) {
 	}
 }
 
+// See https://www.postfix.org/smtp-smuggling.html
+func TestServer_smtpSmuggling(t *testing.T) {
+	be, s, c, scanner := testServerAuthenticated(t)
+	defer s.Close()
+
+	io.WriteString(c, "MAIL FROM:<root@nsa.gov>\r\n")
+	scanner.Scan()
+	io.WriteString(c, "RCPT TO:<root@gchq.gov.uk>\r\n")
+	scanner.Scan()
+	io.WriteString(c, "DATA\r\n")
+	scanner.Scan()
+
+	io.WriteString(c, "This is a message with an SMTP smuggling dot:\r\n")
+	io.WriteString(c, ".\n")
+	io.WriteString(c, "Final dot comes after.\r\n")
+	io.WriteString(c, ".\r\n")
+	scanner.Scan()
+	if !strings.HasPrefix(scanner.Text(), "250 ") {
+		t.Fatal("Invalid DATA response, expected an error but got:", scanner.Text())
+	}
+
+	if len(be.messages) != 1 {
+		t.Fatal("Invalid number of sent messages:", len(be.messages))
+	}
+
+	msg := be.messages[0]
+	if string(msg.Data) != "This is a message with an SMTP smuggling dot:\r\n\nFinal dot comes after.\r\n" {
+		t.Fatalf("Invalid mail data: %q", string(msg.Data))
+	}
+}
+
 func TestServer_tooLongLine(t *testing.T) {
 	_, s, c, scanner := testServerAuthenticated(t)
 	defer s.Close()
Repository: emersion/go-smtp
Pull request #255 (2024-03-23 05:13:24)

Server: The value of BODY parameter is case-insensitive
Currently,
```code
MAIL FROM:<alice@wonderland.book> BODY=8bitMIME
```
is responded by `500 5.5.4 Unknown BODY value`.

This PR fixes it to make the parameter value case-insensitive.

___
In this occasion, it also makes status code for invalid parameter value `501` as RFC describes.



Patch:
diff --git a/conn.go b/conn.go
index 3651023..9ccb141 100644
--- a/conn.go
+++ b/conn.go
@@ -355,16 +355,18 @@ func (c *Conn) handleMail(arg string) {
 			}
 			opts.RequireTLS = true
 		case "BODY":
-			switch value {
-			case "BINARYMIME":
+			value = strings.ToUpper(value)
+			switch BodyType(value) {
+			case BodyBinaryMIME:
 				if !c.server.EnableBINARYMIME {
 					c.writeResponse(504, EnhancedCode{5, 5, 4}, "BINARYMIME is not implemented")
 					return
 				}
 				c.binarymime = true
-			case "7BIT", "8BITMIME":
+			case Body7Bit, Body8BitMIME:
+				// This space is intentionally left blank
 			default:
-				c.writeResponse(500, EnhancedCode{5, 5, 4}, "Unknown BODY value")
+				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown BODY value")
 				return
 			}
 			opts.Body = BodyType(value)


Test patch:
diff --git a/server_test.go b/server_test.go
index f814407..4e0fc2f 100644
--- a/server_test.go
+++ b/server_test.go
@@ -468,7 +468,7 @@ func TestServer8BITMIME(t *testing.T) {
 	defer s.Close()
 	defer c.Close()
 
-	io.WriteString(c, "MAIL FROM:<alice@wonderland.book> BODY=8BITMIME\r\n")
+	io.WriteString(c, "MAIL FROM:<alice@wonderland.book> BODY=8bitMIME\r\n")
 	scanner.Scan()
 	if !strings.HasPrefix(scanner.Text(), "250 ") {
 		t.Fatal("Invalid MAIL response:", scanner.Text())