diff --git a/internal/session/handle_append.go b/internal/session/handle_append.go index 726dfb4a..04169321 100644 --- a/internal/session/handle_append.go +++ b/internal/session/handle_append.go @@ -3,12 +3,12 @@ package session import ( "context" "errors" - "github.com/ProtonMail/gluon/imap/command" "github.com/ProtonMail/gluon/internal/response" "github.com/ProtonMail/gluon/internal/state" "github.com/ProtonMail/gluon/profiling" "github.com/ProtonMail/gluon/reporter" + "github.com/ProtonMail/gluon/rfc5322" ) func (s *Session) handleAppend(ctx context.Context, tag string, cmd *command.Append, ch chan response.Response) error { @@ -25,6 +25,10 @@ func (s *Session) handleAppend(ctx context.Context, tag string, cmd *command.App return response.Bad(tag).WithError(err) } + if err := rfc5322.ValidateMessageHeaderFields(cmd.Literal); err != nil { + return response.Bad(tag).WithError(err) + } + if err := s.state.AppendOnlyMailbox(ctx, nameUTF8, func(mailbox state.AppendOnlyMailbox, isSameMBox bool) error { messageUID, err := mailbox.Append(ctx, cmd.Literal, flags, cmd.DateTime) if err != nil { diff --git a/rfc5322/validation.go b/rfc5322/validation.go new file mode 100644 index 00000000..2d3aefdd --- /dev/null +++ b/rfc5322/validation.go @@ -0,0 +1,73 @@ +package rfc5322 + +import ( + "errors" + "fmt" + "github.com/ProtonMail/gluon/rfc822" +) + +var ErrInvalidMessage = errors.New("invalid rfc5322 message") + +// ValidateMessageHeaderFields checks the headers of message to verify that: +// * From and Date are present. +// * If From has multiple addresses, a Sender field must be present. +// * If Both From and Sender are present and they contain one address, they must not be equal. +func ValidateMessageHeaderFields(literal []byte) error { + headerBytes, _ := rfc822.Split(literal) + + header, err := rfc822.NewHeader(headerBytes) + if err != nil { + return err + } + + // Check for date. + { + value := header.Get("Date") + if len(value) == 0 { + return fmt.Errorf("%w: Required header field 'Date' not found or empty", ErrInvalidMessage) + } + } + + // Check for from. + { + value := header.Get("From") + if len(value) == 0 { + return fmt.Errorf("%w: Required header field 'From' not found or empty", ErrInvalidMessage) + } + + // Check if From is a multi address. If so, a sender filed must be present and non-empty. + addresses, err := ParseAddressList(value) + if err != nil { + return fmt.Errorf("%w: failed to parse From header: %v", ErrInvalidMessage, err) + } + + if len(addresses) > 1 { + senderValue := header.Get("Sender") + if len(senderValue) == 0 { + return fmt.Errorf("%w: Required header field 'Sender' not found or empty", ErrInvalidMessage) + } + _, err := ParseAddress(senderValue) + if err != nil { + return fmt.Errorf("%w: failed to parse Sender header: %v", ErrInvalidMessage, err) + } + } else { + senderValue, ok := header.GetChecked("Sender") + if ok { + if len(senderValue) == 0 { + return fmt.Errorf("%w: Required header field 'Sender' should not be empty", ErrInvalidMessage) + } + + senderAddr, err := ParseAddress(senderValue) + if err != nil { + return fmt.Errorf("%w: failed to parse Sender header: %v", ErrInvalidMessage, err) + } + + if len(senderAddr) == 1 && senderAddr[0].Address == addresses[0].Address { + return fmt.Errorf("%w: `Sender` should not be present if equal to `From`", ErrInvalidMessage) + } + } + } + } + + return nil +} diff --git a/rfc5322/validation_test.go b/rfc5322/validation_test.go new file mode 100644 index 00000000..d385c036 --- /dev/null +++ b/rfc5322/validation_test.go @@ -0,0 +1,54 @@ +package rfc5322 + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestValidateMessageHeaderFields_RequiredFieldsPass(t *testing.T) { + const literal = `From: Foo@bar.com +Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) +` + + require.NoError(t, ValidateMessageHeaderFields([]byte(literal))) +} + +func TestValidateMessageHeaderFields_ErrOnMissingFrom(t *testing.T) { + const literal = `Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) +` + + require.Error(t, ValidateMessageHeaderFields([]byte(literal))) +} + +func TestValidateMessageHeaderFields_ErrOnMissingDate(t *testing.T) { + const literal = `From: Foo@bar.com +` + + require.Error(t, ValidateMessageHeaderFields([]byte(literal))) +} + +func TestValidateMessageHeaderFields_ErrOnSingleFromAndSenderEqual(t *testing.T) { + const literal = `From: Foo@bar.com +Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) +Sender: Foo@bar.com +` + + require.Error(t, ValidateMessageHeaderFields([]byte(literal))) +} + +func TestValidateMessageHeaderFields_AllowSingleFromWithDifferentSender(t *testing.T) { + const literal = `From: Foo@bar.com +Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) +Sender: Bar@bar.com +` + + require.NoError(t, ValidateMessageHeaderFields([]byte(literal))) +} + +func TestValidateMessageHeaderFields_ErrOnMultipleFromAndNoSender(t *testing.T) { + const literal = `From: Foo@bar.com, Bar@bar.com +Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) +` + + require.Error(t, ValidateMessageHeaderFields([]byte(literal))) +} diff --git a/tests/append_test.go b/tests/append_test.go index 141779b3..2585910c 100644 --- a/tests/append_test.go +++ b/tests/append_test.go @@ -377,7 +377,7 @@ func TestAppendCanHandleOutOfOrderUIDUpdates(t *testing.T) { appendFN := func(clientIndex int) { for i := 0; i < MessageCount; i++ { - c[clientIndex+1].doAppend("INBOX", "To: f3@pm.me\r\n", "\\Seen").expect("OK") + c[clientIndex+1].doAppend("INBOX", buildRFC5322TestLiteral("To: f3@pm.me\r\n"), "\\Seen").expect("OK") } } @@ -414,7 +414,7 @@ func TestGODT2007AppendInternalIDPresentOnDeletedMessage(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t), func(client *client.Client, s *testSession) { // Create message and mark deleted. mboxID := s.mailboxCreated("user", []string{mailboxName}) - messageID := s.messageCreated("user", mboxID, []byte("To: foo@bar.com\r\n"), time.Now()) + messageID := s.messageCreated("user", mboxID, []byte(buildRFC5322TestLiteral("To: foo@bar.com\r\n")), time.Now()) s.flush("user") _, err := client.Select(mailboxName, false) @@ -424,7 +424,7 @@ func TestGODT2007AppendInternalIDPresentOnDeletedMessage(t *testing.T) { // Check if the header is correctly set. result := newFetchCommand(t, client).withItems("UID", "BODY[HEADER]").fetch("1") result.forSeqNum(1, func(builder *validatorBuilder) { - builder.ignoreFlags().wantSectionAndSkipGLUONHeaderOrPanic("BODY[HEADER]", "To: foo@bar.com\r\n") + builder.ignoreFlags().wantSectionAndSkipGLUONHeaderOrPanic("BODY[HEADER]", buildRFC5322TestLiteral("To: foo@bar.com\r\n")) builder.wantUID(1) }) result.checkAndRequireMessageCount(1) diff --git a/tests/case_test.go b/tests/case_test.go index 343d24bd..3c3bd979 100644 --- a/tests/case_test.go +++ b/tests/case_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "testing" ) @@ -27,12 +28,16 @@ func TestMailboxCase(t *testing.T) { c.C(`tag status iNbOx/other (messages)`).OK(`tag`) // We can append INBOX in any case. - c.C(`tag append INBOX () {11}`).Continue().C(`To: 1@pm.me`).OK(`tag`) - c.C(`tag append inbox () {11}`).Continue().C(`To: 1@pm.me`).OK(`tag`) - c.C(`tag append iNbOx () {11}`).Continue().C(`To: 1@pm.me`).OK(`tag`) - c.C(`tag append INBOX/other () {11}`).Continue().C(`To: 1@pm.me`).OK(`tag`) - c.C(`tag append inbox/other () {11}`).Continue().C(`To: 1@pm.me`).OK(`tag`) - c.C(`tag append iNbOx/other () {11}`).Continue().C(`To: 1@pm.me`).OK(`tag`) + + literal := buildRFC5322TestLiteral(`To: 1@pm.me`) + literalLen := len(literal) + + c.C(fmt.Sprintf(`tag append INBOX () {%v}`, literalLen)).Continue().C(literal).OK(`tag`) + c.C(fmt.Sprintf(`tag append inbox () {%v}`, literalLen)).Continue().C(literal).OK(`tag`) + c.C(fmt.Sprintf(`tag append iNbOx () {%v}`, literalLen)).Continue().C(literal).OK(`tag`) + c.C(fmt.Sprintf(`tag append INBOX/other () {%v}`, literalLen)).Continue().C(literal).OK(`tag`) + c.C(fmt.Sprintf(`tag append inbox/other () {%v}`, literalLen)).Continue().C(literal).OK(`tag`) + c.C(fmt.Sprintf(`tag append iNbOx/other () {%v}`, literalLen)).Continue().C(literal).OK(`tag`) // We can list inbox in any case. c.C(`tag LIST "" "INBOX"`).Sx(`INBOX`).OK(`tag`) diff --git a/tests/close_test.go b/tests/close_test.go index ea5fb1d2..faca053a 100644 --- a/tests/close_test.go +++ b/tests/close_test.go @@ -18,11 +18,11 @@ func TestClose(t *testing.T) { c[1].C("b001 CREATE saved-messages") c[1].S("b001 OK CREATE") - c[1].doAppend(`saved-messages`, `To: 1@pm.me`, `\Seen`).expect("OK") - c[1].doAppend(`saved-messages`, `To: 2@pm.me`).expect("OK") - c[1].doAppend(`saved-messages`, `To: 3@pm.me`, `\Seen`).expect("OK") - c[1].doAppend(`saved-messages`, `To: 4@pm.me`).expect("OK") - c[1].doAppend(`saved-messages`, `To: 5@pm.me`, `\Seen`).expect("OK") + c[1].doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c[1].doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c[1].doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 3@pm.me`), `\Seen`).expect("OK") + c[1].doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 4@pm.me`)).expect("OK") + c[1].doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 5@pm.me`), `\Seen`).expect("OK") c[1].C(`A002 SELECT saved-messages`) c[1].Se(`A002 OK [READ-WRITE] SELECT`) @@ -92,11 +92,11 @@ func TestCloseWithClient(t *testing.T) { ) require.NoError(t, client.Create(messageBoxName)) - require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) - require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader("To: 2@pm.me"))) - require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 3@pm.me"))) - require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader("To: 4@pm.me"))) - require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 5@pm.me"))) + require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) + require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 2@pm.me")))) + require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 3@pm.me")))) + require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 4@pm.me")))) + require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 5@pm.me")))) { mailboxStatus, err := client.Select(messageBoxName, false) diff --git a/tests/delete_test.go b/tests/delete_test.go index 54ffeb49..cdc12588 100644 --- a/tests/delete_test.go +++ b/tests/delete_test.go @@ -56,8 +56,8 @@ func TestDeleteMailboxHasChildren(t *testing.T) { c.C(`A002 CREATE "foo"`).OK(`A002`) c.C(`A003 CREATE "foo/bar"`).OK(`A003`) - c.doAppend(`foo`, `To: 1@pm.me`).expect("OK") - c.doAppend(`foo/bar`, `To: 2@pm.me`).expect("OK") + c.doAppend(`foo`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c.doAppend(`foo/bar`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") c.C(`A004 SELECT "INBOX"`).OK(`A004`) c.C(`A005 LIST "" *`) diff --git a/tests/deleted_test.go b/tests/deleted_test.go index da47f9cd..87d4f25e 100644 --- a/tests/deleted_test.go +++ b/tests/deleted_test.go @@ -17,8 +17,8 @@ func TestDeleted(t *testing.T) { c.S("b001 OK CREATE") // Create a message in mbox1. - c.doAppend(`mbox1`, `To: 1@pm.me`, `\Seen`).expect("OK") - c.doAppend(`mbox1`, `To: 2@pm.me`, `\Seen`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 2@pm.me`), `\Seen`).expect("OK") c.C(`A002 SELECT mbox1`) c.Se(`A002 OK [READ-WRITE] SELECT`) @@ -85,8 +85,8 @@ func TestUIDDeleted(t *testing.T) { c.S("b001 OK CREATE") // Create a message in mbox1 - c.doAppend(`mbox1`, `To: 1@pm.me`, `\Seen`).expect("OK") - c.doAppend(`mbox1`, `To: 2@pm.me`, `\Seen`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 2@pm.me`), `\Seen`).expect("OK") c.C(`A002 SELECT mbox1`) c.Se(`A002 OK [READ-WRITE] SELECT`) diff --git a/tests/deletion_pool_test.go b/tests/deletion_pool_test.go index 587ead45..6902878e 100644 --- a/tests/deletion_pool_test.go +++ b/tests/deletion_pool_test.go @@ -8,9 +8,9 @@ import ( func TestDeletionPool(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3, 4}, func(c map[int]*testConnection, s *testSession) { - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 2@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 3@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") for _, i := range []int{1, 2, 4} { c[i].C("A006 SELECT INBOX") @@ -64,9 +64,9 @@ func TestDeletionPool(t *testing.T) { func TestExpungeIssued(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 2@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 3@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") // 2 snapshots of INBOX for i := 1; i <= 2; i++ { @@ -115,8 +115,8 @@ func TestExpungeIssued(t *testing.T) { func TestExpungeUpdate(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 2@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") // 2 snapshots of INBOX for i := 1; i <= 2; i++ { @@ -155,7 +155,7 @@ func TestExpungeUpdate(t *testing.T) { c[2].Sx(`B002 OK \[EXPUNGEISSUED\] command completed in`) // session 1 adds a message to the box and flags it as answered - c[1].doAppend(`INBOX`, `To: 3@pm.me`).expect(`OK .*`) + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") c[1].C(`A005 STORE 2 +FLAGS (\Answered)`) c[1].S(`* 2 FETCH (FLAGS (\Answered \Recent))`) c[1].Sx("A005 OK command completed in") @@ -196,10 +196,10 @@ func TestExpungeUpdate(t *testing.T) { func TestStatusOnUnnotifiedSnapshot(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, s *testSession) { // INBOX with 4 messages - c[1].doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") - c[1].doAppend(`INBOX`, `To: 2@pm.me`, `\Seen`).expect("OK") - c[1].doAppend(`INBOX`, `To: 3@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 4@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`), `\Seen`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 4@pm.me`)).expect("OK") // 2 sessions with INBOX selected (-> 2 snapshots), session 3 is in Authenticated state (no mailbox selected). for i := 1; i <= 2; i++ { @@ -236,7 +236,7 @@ func TestDeletionFlagPropagation(t *testing.T) { defer done() // Create a message. - c[1].doAppend(origin, `To: 1@pm.me`).expect(`OK`) + c[1].doAppend(origin, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect(`OK`) destination, done := c[1].doCreateTempDir() defer done() @@ -267,7 +267,7 @@ func TestDeletionFlagPropagation(t *testing.T) { func TestDeletionFlagPropagationIDLE(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { // Create a message. - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect(`OK`) + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect(`OK`) // Create a destination mailbox. c[1].C("A002 CREATE destination").OK("A002") @@ -301,7 +301,7 @@ func TestDeletionFlagPropagationIDLE(t *testing.T) { func TestDeletionFlagPropagationMulti(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, s *testSession) { // Create a message. - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect(`OK`) + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect(`OK`) // Create two destination mailboxes. c[1].C("A002 CREATE mbox1").OK("A002") @@ -358,6 +358,7 @@ func TestNoopReceivesPendingDeletionUpdates(t *testing.T) { messageID1 := s.messageCreatedFromFile("user", mailboxID, "testdata/multipart-mixed.eml") messageID2 := s.messageCreatedFromFile("user", mailboxID, "testdata/afternoon-meeting.eml") + s.flush("user") // Create a snapshot by selecting in the mailbox. c.C(`A001 select mbox`).OK(`A001`) @@ -365,6 +366,7 @@ func TestNoopReceivesPendingDeletionUpdates(t *testing.T) { s.messageRemoved("user", messageID1, mailboxID) s.messageRemoved("user", messageID2, mailboxID) + s.flush("user") // Noop should process their deletion. c.C(`A002 noop`) c.S(`* 1 EXPUNGE`, `* 1 EXPUNGE`) diff --git a/tests/draft_test.go b/tests/draft_test.go index 0c806728..a56659c2 100644 --- a/tests/draft_test.go +++ b/tests/draft_test.go @@ -52,7 +52,7 @@ func TestDraftSavedAgain(t *testing.T) { c.C("A002 SELECT Drafts").OK("A002") - _ = s.messageCreated("user", mailboxID, []byte("To: 3@3.pm"), time.Now()) + _ = s.messageCreated("user", mailboxID, []byte(buildRFC5322TestLiteral("To: 3@3.pm")), time.Now()) s.flush("user") c.C("A002 NOOP") @@ -64,9 +64,9 @@ func TestDraftSavedAgain(t *testing.T) { raw := c.read() fetch := regexp.MustCompile( - regexp.QuoteMeta("* 1 FETCH (BODY[] {63}\r\nX-Pm-Gluon-Id: ") + + regexp.QuoteMeta("* 1 FETCH (BODY[] {134}\r\nX-Pm-Gluon-Id: ") + ".*" + - regexp.QuoteMeta("\r\nTo: 3@3.pm)\r\n"), + regexp.QuoteMeta("\r\n"+buildRFC5322TestLiteral("To: 3@3.pm")+")\r\n"), ) require.Regexp(t, fetch, string(raw)) @@ -79,7 +79,7 @@ func TestDraftSavedAgain(t *testing.T) { c.doAppend( "Drafts", - string(raw[24:len(raw)-3]), + string(raw[25:len(raw)-3]), ).expect("OK") c.C("A004 FETCH 1 (BODY.PEEK[])") @@ -88,11 +88,11 @@ func TestDraftSavedAgain(t *testing.T) { c.OK("A004") } - newBody2 := string(raw[24:]) + "\r\nThis is body\r\n" + newBody2 := string(raw[25:]) + "\r\nThis is body\r\n" fetchUpdated2 := regexp.MustCompile( - regexp.QuoteMeta("* 1 FETCH (BODY[] {82}\r\nX-Pm-Gluon-Id: ") + + regexp.QuoteMeta("* 1 FETCH (BODY[] {153}\r\nX-Pm-Gluon-Id: ") + ".*" + - regexp.QuoteMeta("\r\nTo: 3@3.pm)\r\n\r\nThis is body\r\n)"), + regexp.QuoteMeta("\r\n"+buildRFC5322TestLiteral("To: 3@3.pm)")+"\r\n\r\nThis is body\r\n)"), ) // Expunge+Append different message { @@ -115,9 +115,9 @@ func TestDraftSavedAgain(t *testing.T) { fetchUpdated3 := regexp.MustCompile( regexp.QuoteMeta("* ") + "\\d" + - regexp.QuoteMeta(" FETCH (BODY[] {89}\r\nX-Pm-Gluon-Id: ") + + regexp.QuoteMeta(" FETCH (BODY[] {160}\r\nX-Pm-Gluon-Id: ") + ".*" + - regexp.QuoteMeta("\r\nTo: 3@3.pm)\r\n\r\nThis is body\r\nhello\r\n)"), + regexp.QuoteMeta("\r\n"+buildRFC5322TestLiteral("To: 3@3.pm)")+"\r\n\r\nThis is body\r\nhello\r\n)"), ) { diff --git a/tests/examine_test.go b/tests/examine_test.go index 3b437ee3..23509690 100644 --- a/tests/examine_test.go +++ b/tests/examine_test.go @@ -18,7 +18,7 @@ func TestExamineWithLiteral(t *testing.T) { c.C("A002 CREATE Archive") c.S("A002 OK CREATE") - c.doAppend(`Archive`, `To: 3@pm.me`, `\Seen`).expect("OK") + c.doAppend(`Archive`, buildRFC5322TestLiteral(`To: 3@pm.me`), `\Seen`).expect("OK") c.C("a007 examine {7}") c.S("+ Ready") @@ -36,9 +36,9 @@ func TestExamineWithLiteral(t *testing.T) { func TestExamineClient(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withUIDValidityGenerator(imap.NewFixedUIDValidityGenerator(1))), func(client *client.Client, _ *testSession) { require.NoError(t, client.Create("Archive")) - require.NoError(t, client.Append("INBOX", []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) - require.NoError(t, client.Append("INBOX", []string{}, time.Now(), strings.NewReader("To: 2@pm.me"))) - require.NoError(t, client.Append("Archive", []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 3@pm.me"))) + require.NoError(t, client.Append("INBOX", []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) + require.NoError(t, client.Append("INBOX", []string{}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 2@pm.me")))) + require.NoError(t, client.Append("Archive", []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 3@pm.me")))) // IMAP client does not have an explicit Examine call, but this a call to Select(..., readonly=true) gets // converted into an EXAMINE command. diff --git a/tests/expunge_test.go b/tests/expunge_test.go index 3e9595d8..ec511d22 100644 --- a/tests/expunge_test.go +++ b/tests/expunge_test.go @@ -17,7 +17,7 @@ func TestExpungeUnmarked(t *testing.T) { require.NoError(t, client.Create("mbox")) _, err := client.Select("mbox", false) require.NoError(t, err) - require.NoError(t, client.Append("mbox", []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) + require.NoError(t, client.Append("mbox", []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) }) } @@ -27,7 +27,7 @@ func TestExpungeSingle(t *testing.T) { _, err := client.Select("mbox", false) require.NoError(t, err) - require.NoError(t, client.Append("mbox", []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) + require.NoError(t, client.Append("mbox", []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) messages := storeWithRetrievalClient(t, client, createSeqSet("1"), goimap.AddFlags, []interface{}{goimap.DeletedFlag}) require.Equal(t, 1, len(messages)) @@ -41,9 +41,9 @@ func TestExpungeSingle(t *testing.T) { func TestRemoveSameMessageTwice(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, s *testSession) { // There are three messages in the inbox. - c[1].doAppend(`inbox`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`inbox`, `To: 2@pm.me`).expect("OK") - c[1].doAppend(`inbox`, `To: 3@pm.me`).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") // There are three clients selected in the inbox. for i := range c { @@ -74,7 +74,7 @@ func TestExpungeInterval(t *testing.T) { _, err := client.Select("mbox", false) require.NoError(t, err) for i := 1; i <= 4; i++ { - require.NoError(t, client.Append("mbox", []string{goimap.SeenFlag}, time.Now(), strings.NewReader(fmt.Sprintf(`To: %d@pm.me`, i)))) + require.NoError(t, client.Append("mbox", []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral(fmt.Sprintf(`To: %d@pm.me`, i))))) } messages := storeWithRetrievalClient(t, client, createSeqSet("1,3"), goimap.AddFlags, []interface{}{goimap.DeletedFlag}) require.Equal(t, 2, len(messages)) @@ -124,11 +124,11 @@ func TestExpungeWithAppendBeforeMailboxSelect(t *testing.T) { const mailboxName = "saved-messages" require.NoError(t, client.Create(mailboxName)) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader("To: 2@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 3@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader("To: 4@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 5@pm.me"))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 2@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 3@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 4@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 5@pm.me")))) _, err := client.Select(mailboxName, false) require.NoError(t, err) @@ -147,11 +147,11 @@ func TestExpungeWithAppendAfterMailBoxSelect(t *testing.T) { _, err := client.Select(mailboxName, false) require.NoError(t, err) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader("To: 2@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 3@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader("To: 4@pm.me"))) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 5@pm.me"))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 2@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 3@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 4@pm.me")))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 5@pm.me")))) beforeOrAfterExpungeCheck(t, client, mailboxName) }) @@ -165,7 +165,7 @@ func TestExpungeUID(t *testing.T) { _, err := client.Select(mailboxName, false) require.NoError(t, err) - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) uidClient := uidplus.NewClient(client) { @@ -193,7 +193,7 @@ func TestExpungeUID(t *testing.T) { // Append new messages for i := 1; i <= 4; i++ { - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(fmt.Sprintf(`To: %d@pm.me`, i)))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral(fmt.Sprintf(`To: %d@pm.me`, i))))) } { @@ -228,7 +228,7 @@ func TestExpungeResponseSequence(t *testing.T) { require.NoError(t, err) for i := 1; i <= 11; i++ { - require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(fmt.Sprintf(`To: %d@pm.me`, i)))) + require.NoError(t, client.Append(mailboxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral(fmt.Sprintf(`To: %d@pm.me`, i))))) } { diff --git a/tests/fetch_test.go b/tests/fetch_test.go index 58e2bc0c..b8d76cf2 100644 --- a/tests/fetch_test.go +++ b/tests/fetch_test.go @@ -255,18 +255,24 @@ func TestFetchSequence(t *testing.T) { fetchResult.forSeqNum(1, func(builder *validatorBuilder) { builder.wantUID(1) builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("1@pm.me") }) }). forSeqNum(2, func(builder *validatorBuilder) { builder.wantUID(2) builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("2@pm.me") }) }). forSeqNum(4, func(builder *validatorBuilder) { builder.wantUID(5) builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("5@pm.me") }) }).checkAndRequireMessageCount(3) @@ -293,18 +299,24 @@ func TestFetchUID(t *testing.T) { fetchResult.forSeqNum(2, func(builder *validatorBuilder) { builder.wantUID(2) builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("2@pm.me") }) }). forSeqNum(3, func(builder *validatorBuilder) { builder.wantUID(3) builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("3@pm.me") }) }). forSeqNum(4, func(builder *validatorBuilder) { builder.wantUID(5) builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("5@pm.me") }) }).checkAndRequireMessageCount(3) @@ -559,11 +571,11 @@ func afternoonMeetingMessageDataSizeWithExtraHeader() uint32 { func fillAndSelectMailboxWithMultipleEntries(t *testing.T, client *client.Client) { const messageBoxName = "INBOX" - require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 1@pm.me"))) - require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader("To: 2@pm.me"))) - require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 3@pm.me"))) - require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader("To: 4@pm.me"))) - require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader("To: 5@pm.me"))) + require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 1@pm.me")))) + require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 2@pm.me")))) + require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 3@pm.me")))) + require.NoError(t, client.Append(messageBoxName, nil, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 4@pm.me")))) + require.NoError(t, client.Append(messageBoxName, []string{goimap.SeenFlag}, time.Now(), strings.NewReader(buildRFC5322TestLiteral("To: 5@pm.me")))) _, err := client.Select(messageBoxName, false) require.NoError(t, err) } diff --git a/tests/helper_test.go b/tests/helper_test.go index 722ef865..334941be 100644 --- a/tests/helper_test.go +++ b/tests/helper_test.go @@ -31,6 +31,20 @@ func (h *testHelper) expect(res ...string) { h.s.Sxe(res...) } +const DefaultTestDateStr = "23 Feb 2020 02:52:25 -0800 (PST)" + +func buildRFC5322TestLiteral(literal string) string { + if !strings.Contains(literal, "Date: ") { + literal = "Date: " + DefaultTestDateStr + "\r\n" + literal + } + + if !strings.Contains(literal, "From: ") { + literal = "From: NoAddress@void.universe" + "\r\n" + literal + } + + return literal +} + func (s *testConnection) doAppend(mailboxName, literal string, flags ...string) *testHelper { tag := uuid.NewString() @@ -652,6 +666,21 @@ func (v *envelopeValidatorBuilder) wantDateTime(dateTime string) *envelopeValida return v } +func (v *envelopeValidatorBuilder) skipDateTime() { + v.validator.validateDateTime = func(_ testing.TB, _ time.Time) { + } +} + +func (v *envelopeValidatorBuilder) skipFromAndSender() { + v.validator.validateFrom = func(_ testing.TB, _ []*goimap.Address) { + + } + + v.validator.validateSender = func(_ testing.TB, _ []*goimap.Address) { + + } +} + func (v *envelopeValidatorBuilder) wantSubject(subject string) *envelopeValidatorBuilder { v.validator.validateSubject = func(t testing.TB, s string) { require.Equal(t, s, subject) diff --git a/tests/id_test.go b/tests/id_test.go index 09633c9b..eeab557f 100644 --- a/tests/id_test.go +++ b/tests/id_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "testing" "github.com/ProtonMail/gluon/events" @@ -30,9 +31,11 @@ func TestIdContextLookup(t *testing.T) { // NOTE: We are only recording this with APPEND since it was the easiest command to verify the data has been // record properly in the context, as APPEND will always require a communication with the remote connector. - c.C("A004 APPEND INBOX (\\Seen) {26}") + literal := buildRFC5322TestLiteral("To: 00010203-0405-4607-880") + literalLen := len(literal) + c.C(fmt.Sprintf("A004 APPEND INBOX (\\Seen) {%v}", literalLen)) c.S("+ Ready") - c.C("To: 00010203-0405-4607-880").OK("A004") + c.C(literal).OK("A004") s.flush("user") diff --git a/tests/idle_test.go b/tests/idle_test.go index 6d2656f6..eac3693d 100644 --- a/tests/idle_test.go +++ b/tests/idle_test.go @@ -19,8 +19,10 @@ func TestIDLEExistsUpdates(t *testing.T) { // Second client appends to INBOX to generate EXISTS updates. // The client is not selected and thus doesn't itself receive responses. - c[2].doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") - c[2].doAppend(`INBOX`, `To: 2@pm.me`, `\Seen`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`), `\Seen`).expect("OK") + + s.flush("user") // First client receives the EXISTS and RECENT updates while idling. c[1].S(`* 2 EXISTS`, `* 2 RECENT`) @@ -30,7 +32,7 @@ func TestIDLEExistsUpdates(t *testing.T) { c[1].OK(`A007`) // Further stuff doesn't trigger any issues. - c[2].doAppend(`INBOX`, `To: 3@pm.me`, `\Seen`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 3@pm.me`), `\Seen`).expect("OK") }) } @@ -63,8 +65,8 @@ func TestIDLERecentReceivedOnSelectedClient(t *testing.T) { c[1].C("A001 select INBOX").OK("A001") // Generate some pending updates. - c[2].doAppend("INBOX", "To: foo.foo").s.OK("") - c[2].doAppend("INBOX", "To: bar.bar").s.OK("") + c[2].doAppend("INBOX", buildRFC5322TestLiteral("To: foo.foo")).s.OK("") + c[2].doAppend("INBOX", buildRFC5322TestLiteral("To: bar.bar")).s.OK("") // Begin IDLE. c[1].C("A002 IDLE").S("+ Ready") @@ -73,14 +75,14 @@ func TestIDLERecentReceivedOnSelectedClient(t *testing.T) { c[1].S(`* 2 EXISTS`, `* 2 RECENT`) // And bulked after 500 ms - c[2].doAppend("INBOX", "To: biz.biz").s.OK("") - c[2].doAppend("INBOX", "To: gardy.loo").s.OK("") + c[2].doAppend("INBOX", buildRFC5322TestLiteral("To: biz.biz")).s.OK("") + c[2].doAppend("INBOX", buildRFC5322TestLiteral("To: gardy.loo")).s.OK("") // Testing splitting the updates in at least two bulks. time.Sleep(510 * time.Millisecond) - c[2].doAppend("INBOX", "To: wolo.lo").s.OK("") - c[2].doAppend("INBOX", "To: huga.chaga").s.OK("") + c[2].doAppend("INBOX", buildRFC5322TestLiteral("To: wolo.lo")).s.OK("") + c[2].doAppend("INBOX", buildRFC5322TestLiteral("To: huga.chaga")).s.OK("") c[2].C("C2 LOGOUT").OK("C2") // First bulk update should have 4 exists and recent. diff --git a/tests/imap_limits_test.go b/tests/imap_limits_test.go index 12d20554..1b7734c5 100644 --- a/tests/imap_limits_test.go +++ b/tests/imap_limits_test.go @@ -29,20 +29,20 @@ func TestMaxMailboxLimitRespected(t *testing.T) { func TestMaxMessageLimitRespected_Append(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withIMAPLimits(testIMAPLimits()), withUIDValidityGenerator(imap.NewIncrementalUIDValidityGenerator())), func(client *client.Client, session *testSession) { - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Foo@bar.com", time.Now())) - require.Error(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) }) } func TestMaxUIDLimitRespected_Append(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withIMAPLimits(testIMAPLimits()), withUIDValidityGenerator(imap.NewIncrementalUIDValidityGenerator())), func(client *client.Client, session *testSession) { - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Foo@bar.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) _, err := client.Select("INBOX", false) require.NoError(t, err) require.NoError(t, client.Store(createSeqSet("1"), goimap.AddFlags, []interface{}{goimap.DeletedFlag}, nil)) require.NoError(t, client.Expunge(nil)) // Append should fail now as we triggered max UID validity error. - require.Error(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) }) } @@ -50,8 +50,8 @@ func TestMaxMessageLimitRespected_Copy(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withIMAPLimits(testIMAPLimits()), withUIDValidityGenerator(imap.NewIncrementalUIDValidityGenerator())), func(client *client.Client, session *testSession) { session.setUpdatesAllowedToFail("user", true) require.NoError(t, client.Create("mbox1")) - require.NoError(t, doAppendWithClient(client, "mbox1", "To: Foo@bar.com", time.Now())) - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "mbox1", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) _, err := client.Select("INBOX", false) require.NoError(t, err) require.Error(t, client.Copy(createSeqSet("1"), "mbox1")) @@ -62,8 +62,8 @@ func TestMaxUIDLimitRespected_Copy(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withIMAPLimits(testIMAPLimits()), withUIDValidityGenerator(imap.NewIncrementalUIDValidityGenerator())), func(client *client.Client, session *testSession) { session.setUpdatesAllowedToFail("user", true) require.NoError(t, client.Create("mbox1")) - require.NoError(t, doAppendWithClient(client, "mbox1", "To: Foo@bar.com", time.Now())) - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "mbox1", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) // Delete existing message in mbox1 to trigget UID validity check _, err := client.Select("mbox1", false) @@ -82,8 +82,8 @@ func TestMaxMessageLimitRespected_Move(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withIMAPLimits(testIMAPLimits()), withUIDValidityGenerator(imap.NewIncrementalUIDValidityGenerator())), func(client *client.Client, session *testSession) { session.setUpdatesAllowedToFail("user", true) require.NoError(t, client.Create("mbox1")) - require.NoError(t, doAppendWithClient(client, "mbox1", "To: Foo@bar.com", time.Now())) - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "mbox1", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) _, err := client.Select("INBOX", false) require.NoError(t, err) require.Error(t, client.Move(createSeqSet("1"), "mbox1")) @@ -94,8 +94,8 @@ func TestMaxUIDLimitRespected_Move(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withIMAPLimits(testIMAPLimits()), withUIDValidityGenerator(imap.NewIncrementalUIDValidityGenerator())), func(client *client.Client, session *testSession) { session.setUpdatesAllowedToFail("user", true) require.NoError(t, client.Create("mbox1")) - require.NoError(t, doAppendWithClient(client, "mbox1", "To: Foo@bar.com", time.Now())) - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "mbox1", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) // Delete existing message in mbox1 to trigget UID validity check _, err := client.Select("mbox1", false) diff --git a/tests/login_test.go b/tests/login_test.go index df324513..d8e7f48e 100644 --- a/tests/login_test.go +++ b/tests/login_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "testing" "time" @@ -61,8 +62,12 @@ func TestLoginAlias(t *testing.T) { c[2].C("tag2 login alias2 pass").OK("tag2") // Create a message with each alias. - c[1].C("tag3 append inbox {11}\r\nTo: 1@pm.me").OK("tag3") - c[2].C("tag4 append inbox {11}\r\nTo: 2@pm.me").OK("tag4") + literal1 := buildRFC5322TestLiteral("To: 1@pm.me") + literal2 := buildRFC5322TestLiteral("To: 2@pm.me") + literalLen := len(literal2) + + c[1].C(fmt.Sprintf("tag3 append inbox {%v}\r\n%v", literalLen, literal1)).OK("tag3") + c[1].C(fmt.Sprintf("tag4 append inbox {%v}\r\n%v", literalLen, literal1)).OK("tag4") // Both messages should be visible to both clients. c[1].C("tag5 status inbox (messages)").Sx("MESSAGES 2").OK("tag5") diff --git a/tests/move_test.go b/tests/move_test.go index 7ca2f2dc..8c77f022 100644 --- a/tests/move_test.go +++ b/tests/move_test.go @@ -108,10 +108,10 @@ func TestMoveBackAndForth(t *testing.T) { func TestMoveCopyDuplicates(t *testing.T) { runOneToOneTestWithAuth(t, defaultServerOptions(t), func(c *testConnection, s *testSession) { // 4 messages in inbox. - c.doAppend("inbox", "To: 1@pm.me").expect("OK") - c.doAppend("inbox", "To: 2@pm.me").expect("OK") - c.doAppend("inbox", "To: 3@pm.me").expect("OK") - c.doAppend("inbox", "To: 4@pm.me").expect("OK") + c.doAppend("inbox", buildRFC5322TestLiteral("To: 1@pm.me")).expect("OK") + c.doAppend("inbox", buildRFC5322TestLiteral("To: 2@pm.me")).expect("OK") + c.doAppend("inbox", buildRFC5322TestLiteral("To: 3@pm.me")).expect("OK") + c.doAppend("inbox", buildRFC5322TestLiteral("To: 4@pm.me")).expect("OK") // Create other mailbox. c.C("tag create other").OK("tag") @@ -264,9 +264,9 @@ func (simulateLabelConnectorBuilder) New(usernames []string, password []byte, pe func TestMoveLabelBehavior(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&simulateLabelConnectorBuilder{})), func(client *client.Client, _ *testSession) { - require.NoError(t, doAppendWithClient(client, "inbox", "To: Foo@foo.com", time.Now())) - require.NoError(t, doAppendWithClient(client, "inbox", "To: Bar@foo.com", time.Now())) - require.NoError(t, doAppendWithClient(client, "inbox", "To: Z@foo.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "inbox", buildRFC5322TestLiteral("To: Foo@foo.com"), time.Now())) + require.NoError(t, doAppendWithClient(client, "inbox", buildRFC5322TestLiteral("To: Bar@foo.com"), time.Now())) + require.NoError(t, doAppendWithClient(client, "inbox", buildRFC5322TestLiteral("To: Z@foo.com"), time.Now())) require.NoError(t, client.Create("mylabel")) @@ -289,18 +289,24 @@ func TestMoveLabelBehavior(t *testing.T) { forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("Foo@foo.com") }) }). forSeqNum(2, func(builder *validatorBuilder) { builder.ignoreFlags() builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("Bar@foo.com") }) }). forSeqNum(3, func(builder *validatorBuilder) { builder.ignoreFlags() builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("Z@foo.com") }) }). @@ -323,6 +329,8 @@ func TestMoveLabelBehavior(t *testing.T) { newFetchCommand(t, client).withItems("ENVELOPE").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() builder.wantEnvelope(func(builder *envelopeValidatorBuilder) { + builder.skipDateTime() + builder.skipFromAndSender() builder.wantTo("Foo@foo.com") }) }).checkAndRequireMessageCount(1) diff --git a/tests/multi_test.go b/tests/multi_test.go index 1ae1e28d..6190e647 100644 --- a/tests/multi_test.go +++ b/tests/multi_test.go @@ -21,7 +21,7 @@ func TestExistsUpdates(t *testing.T) { c[1].Se("A006 OK [READ-WRITE] SELECT") // Second client appends to INBOX to generate EXISTS update. - c[2].doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") // First client receives the EXISTS update. Apply RECENT update is also received. c[1].C("b001 noop") @@ -41,7 +41,7 @@ func TestExistsUpdatesInSeparateMailboxes(t *testing.T) { c[1].Se("A006 OK [READ-WRITE] SELECT") // Second client appends to INBOX to generate EXISTS update. - c[2].doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") // First client does not receive the EXISTS update from INBOX. c[1].C("b001 noop") @@ -51,7 +51,7 @@ func TestExistsUpdatesInSeparateMailboxes(t *testing.T) { func TestFetchUpdates(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { - c[1].doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") // First client selects in INBOX to receive FETCH update. c[1].C("A006 select INBOX") @@ -79,9 +79,9 @@ func TestFetchUpdates(t *testing.T) { func TestExpungeUpdates(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, _ *testSession) { // Generate three messages, the first two unseen, the third seen. - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") // Both clients select in inbox. c[1].C("A006 select INBOX") @@ -134,11 +134,11 @@ func TestExpungeUpdates(t *testing.T) { func TestSequenceNumbersPerSession(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { // Generate five messages. - c[1].doAppend(`inbox`, `To: 1@pm.me`).expect("OK") - c[1].doAppend(`inbox`, `To: 2@pm.me`).expect("OK") - c[1].doAppend(`inbox`, `To: 3@pm.me`).expect("OK") - c[1].doAppend(`inbox`, `To: 4@pm.me`).expect("OK") - c[1].doAppend(`inbox`, `To: 5@pm.me`).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 4@pm.me`)).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 5@pm.me`)).expect("OK") // Both clients select in inbox. c[1].C("tag select inbox").OK("tag") @@ -197,7 +197,7 @@ func TestSequenceNumbersPerSession(t *testing.T) { func TestAddFlagsToExpungedMessage(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { - c[1].doAppend(`inbox`, `To: 1@pm.me`).expect("OK") + c[1].doAppend(`inbox`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Both clients select in inbox. c[1].C("tag select inbox").OK("tag") diff --git a/tests/recent_test.go b/tests/recent_test.go index a951258d..e82a8d53 100644 --- a/tests/recent_test.go +++ b/tests/recent_test.go @@ -7,7 +7,7 @@ import ( func TestRecentSelect(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, _ *testSession) { // Client 1 appends a new message to INBOX. - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Client 2 is the first to be notified of the new message; it appears as recent to client 2. c[2].C("A006 select INBOX") @@ -22,7 +22,7 @@ func TestRecentSelect(t *testing.T) { func TestRecentStatus(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, _ *testSession) { // Client 1 appends a new message to INBOX. - c[1].doAppend("INBOX", `To: 1@pm.me`).expect("OK") + c[1].doAppend("INBOX", buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Client 2 is the first to be notified of the new message; it appears as recent to client 2. // Calling STATUS must not change the value of RECENT. @@ -38,7 +38,7 @@ func TestRecentStatus(t *testing.T) { func TestRecentFetch(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, _ *testSession) { // Client 1 appends a new message to INBOX. - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Client 2 is the first to be notified of the new message; it appears as recent to client 2. c[2].C("A006 select INBOX") @@ -65,7 +65,7 @@ func TestRecentFetch(t *testing.T) { func TestRecentAppend(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2, 3}, func(c map[int]*testConnection, _ *testSession) { // Client 1 appends a new message to INBOX. - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Client 2 is the first to be notified of the new message; it appears as recent to client 2. c[2].C("A006 select INBOX") @@ -73,7 +73,7 @@ func TestRecentAppend(t *testing.T) { // Client 2 then appends a second message to the mailbox while selected. // As it was the first client to perform the operation, it sees the message as recent. - c[2].doAppend(`INBOX`, `To: 2@pm.me`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") c[2].C("A006 fetch 2 (UID FLAGS)") c[2].Se(`* 2 FETCH (UID 2 FLAGS (\Recent))`).OK("A006") }) @@ -88,7 +88,7 @@ func TestRecentStore(t *testing.T) { }() // Create a message in mbox. - c[1].doAppend(mbox, `To: 1@pm.me`).expect(`OK`) + c[1].doAppend(mbox, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect(`OK`) // Select in the mailbox. c[1].Cf(`A002 SELECT %v`, mbox).OK(`A002`) @@ -120,7 +120,7 @@ func TestRecentExists(t *testing.T) { c[2].C("A006 select INBOX").OK("A006") // Client 3 appends a new message to INBOX. - c[3].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c[3].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Client 1 is notified of the new message. No recent is sent as client 2 still has the mailbox selected. c[1].C("A007 NOOP") @@ -140,8 +140,8 @@ func TestRecentIDLEExists(t *testing.T) { c[1].S("+ Ready") // Client 2 appends two new messages to INBOX. - c[2].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[2].doAppend(`INBOX`, `To: 2@pm.me`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") // Client 1 receives EXISTS and RECENT updates. c[1].S(`* 2 EXISTS`, `* 2 RECENT`) @@ -159,8 +159,8 @@ func TestRecentIDLEExpunge(t *testing.T) { c[1].S("+ Ready") // Client 2 appends two new messages to INBOX. - c[2].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") - c[2].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c[2].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // Client 2 moves those two messages to the other folder. c[2].C("A006 select INBOX").OK("A006") diff --git a/tests/recovery_mailbox_test.go b/tests/recovery_mailbox_test.go index 78ab2726..ac6a2230 100644 --- a/tests/recovery_mailbox_test.go +++ b/tests/recovery_mailbox_test.go @@ -25,7 +25,7 @@ func TestRecoveryMBoxNotVisibleWhenEmpty(t *testing.T) { func TestRecoveryMBoxVisibleWhenNotEmpty(t *testing.T) { runOneToOneTestWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&failAppendLabelConnectorBuilder{})), func(c *testConnection, s *testSession) { - c.doAppend("INBOX", "INBOX", "To: Test@test.com").expect("NO") + c.doAppend("INBOX", buildRFC5322TestLiteral("To: Test@test.com")).expect("NO") c.C(`A103 LIST "" *`) c.S(`* LIST (\Unmarked) "/" "INBOX"`, fmt.Sprintf(`* LIST (\Marked \Noinferiors) "/" "%v"`, ids.GluonRecoveryMailboxName), @@ -73,7 +73,7 @@ func TestRecoveryMBoxCanNotBeMovedOrCopiedInto(t *testing.T) { func TestRecoveryMBoxCanBeMovedOutOf(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&disableRemoveFromMailboxBuilder{})), func(client *client.Client, s *testSession) { // Insert first message, fails. - require.Error(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) status, err := client.Status(ids.GluonRecoveryMailboxName, []goimap.StatusItem{goimap.StatusMessages}) require.NoError(t, err) require.Equal(t, uint32(1), status.Messages) @@ -82,7 +82,7 @@ func TestRecoveryMBoxCanBeMovedOutOf(t *testing.T) { require.NoError(t, err) newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSection("BODY[]", "To: Test@test.com") + builder.wantSection("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } @@ -103,7 +103,7 @@ func TestRecoveryMBoxCanBeMovedOutOf(t *testing.T) { // Check that message has the new internal ID header. newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", "To: Test@test.com") + builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } }) @@ -112,7 +112,7 @@ func TestRecoveryMBoxCanBeMovedOutOf(t *testing.T) { func TestRecoveryMBoxCanBeCopiedOutOf(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&disableRemoveFromMailboxBuilder{})), func(client *client.Client, s *testSession) { // Insert first message, fails. - require.Error(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) status, err := client.Status(ids.GluonRecoveryMailboxName, []goimap.StatusItem{goimap.StatusMessages}) require.NoError(t, err) require.Equal(t, uint32(1), status.Messages) @@ -121,7 +121,7 @@ func TestRecoveryMBoxCanBeCopiedOutOf(t *testing.T) { require.NoError(t, err) newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSection("BODY[]", "To: Test@test.com") + builder.wantSection("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } @@ -142,7 +142,7 @@ func TestRecoveryMBoxCanBeCopiedOutOf(t *testing.T) { // Check that message has the new internal ID header. newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", "To: Test@test.com") + builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } }) @@ -151,7 +151,7 @@ func TestRecoveryMBoxCanBeCopiedOutOf(t *testing.T) { func TestRecoveryMBoxCanBeExpunged(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&disableRemoveFromMailboxBuilder{})), func(client *client.Client, s *testSession) { // Insert first message, fails. - require.Error(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) // Execute expunge status, err := client.Select(ids.GluonRecoveryMailboxName, false) require.NoError(t, err) @@ -175,7 +175,7 @@ func TestFailedAppendEndsInRecovery(t *testing.T) { status, err := client.Select("INBOX", false) require.NoError(t, err) require.Equal(t, uint32(0), status.Messages) - require.Error(t, doAppendWithClient(client, "INBOX", "To: Foo@bar.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) { status, err := client.Status(ids.GluonRecoveryMailboxName, []goimap.StatusItem{goimap.StatusMessages}) @@ -194,7 +194,7 @@ func TestFailedAppendEndsInRecovery(t *testing.T) { // Check that no custom headers are appended to the message. newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSection("BODY[]", "To: Foo@bar.com") + builder.wantSection("BODY[]", buildRFC5322TestLiteral("To: Foo@bar.com")) }).checkAndRequireMessageCount(1) } }) @@ -211,9 +211,9 @@ func TestFailedAppendAreDedupedInRecoveryMailbox(t *testing.T) { status, err := client.Select("INBOX", false) require.NoError(t, err) require.Equal(t, uint32(0), status.Messages) - require.Error(t, doAppendWithClient(client, "INBOX", "To: Foo@bar.com", time.Now())) - require.Error(t, doAppendWithClient(client, "INBOX", "To: Foo@bar.com", time.Now())) - require.Error(t, doAppendWithClient(client, "INBOX", "To: Bar@bar.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Foo@bar.com"), time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Bar@bar.com"), time.Now())) { status, err := client.Status(ids.GluonRecoveryMailboxName, []goimap.StatusItem{goimap.StatusMessages}) @@ -232,7 +232,7 @@ func TestFailedAppendAreDedupedInRecoveryMailbox(t *testing.T) { // Check that no custom headers are appended to the message. newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSection("BODY[]", "To: Foo@bar.com") + builder.wantSection("BODY[]", buildRFC5322TestLiteral("To: Foo@bar.com")) }).checkAndRequireMessageCount(1) } }) @@ -300,25 +300,25 @@ func TestRecoveryMailboxDoesNotStoreMessageWhichExceedLimit(t *testing.T) { func TestRecoveryMBoxCanBeCopiedOutOfDedup(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&recoveryDedupConnectorConnectorBuilder{})), func(client *client.Client, s *testSession) { // Insert first message, fails. - require.Error(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) { _, err := client.Select(ids.GluonRecoveryMailboxName, false) require.NoError(t, err) newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSection("BODY[]", "To: Test@test.com") + builder.wantSection("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } // Insert same message, succeeds. - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) { _, err := client.Select("INBOX", false) require.NoError(t, err) newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", "To: Test@test.com") + builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } @@ -352,25 +352,25 @@ func TestRecoveryMBoxCanBeCopiedOutOfDedup(t *testing.T) { func TestRecoveryMBoxCanBeMovedOutOfDedup(t *testing.T) { runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withConnectorBuilder(&recoveryDedupConnectorConnectorBuilder{})), func(client *client.Client, s *testSession) { // Insert first message, fails. - require.Error(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.Error(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) { _, err := client.Select(ids.GluonRecoveryMailboxName, false) require.NoError(t, err) newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSection("BODY[]", "To: Test@test.com") + builder.wantSection("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } // Insert same message, succeeds. - require.NoError(t, doAppendWithClient(client, "INBOX", "To: Test@test.com", time.Now())) + require.NoError(t, doAppendWithClient(client, "INBOX", buildRFC5322TestLiteral("To: Test@test.com"), time.Now())) { _, err := client.Select("INBOX", false) require.NoError(t, err) newFetchCommand(t, client).withItems("BODY[]").fetch("1").forSeqNum(1, func(builder *validatorBuilder) { builder.ignoreFlags() - builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", "To: Test@test.com") + builder.wantSectionAndSkipGLUONHeaderOrPanic("BODY[]", buildRFC5322TestLiteral("To: Test@test.com")) }).checkAndRequireMessageCount(1) } diff --git a/tests/search_test.go b/tests/search_test.go index 0ba6405b..474f854d 100644 --- a/tests/search_test.go +++ b/tests/search_test.go @@ -19,7 +19,7 @@ func TestSearchCharSetUTF8(t *testing.T) { b := enc("ééé", "UTF-8") // Append a message with that as the body. - c.doAppend("inbox", "To: 1@pm.me\r\n\r\nééé").expect("OK") + c.doAppend("inbox", buildRFC5322TestLiteral("To: 1@pm.me\r\n\r\nééé")).expect("OK") // Search for it with UTF-8 encoding. c.Cf(`TAG SEARCH CHARSET UTF-8 BODY {%v}`, len(b)).Continue().Cb(b).S("* SEARCH 1").OK("TAG") @@ -37,7 +37,7 @@ func TestSearchCharSetISO88591(t *testing.T) { require.False(t, utf8.Valid(b)) // Append a message with that as the body. - c.doAppend("inbox", "To: 1@pm.me\r\n\r\nééé").expect("OK") + c.doAppend("inbox", buildRFC5322TestLiteral("To: 1@pm.me\r\n\r\nééé")).expect("OK") // Search for it with ISO-8859-1 encoding (literal). c.Cf(`TAG SEARCH CHARSET ISO-8859-1 BODY {%v}`, len(b)).Continue().Cb(b).S("* SEARCH 1").OK("TAG") @@ -171,6 +171,7 @@ func TestSearchFlagged(t *testing.T) { // They should show up in search. c.C("A002 search flagged") c.S("* SEARCH 10 20 30 40 50") + c.OK("A002") }) } @@ -277,7 +278,7 @@ func TestSearchOld(t *testing.T) { c.OK("A003") // Create a new message; it will be recent and thus not old. - c.doAppend(mbox, `To: 1@pm.me`).expect("OK") + c.doAppend(mbox, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // It will be returned in the search result. c.C("A004 search old") @@ -385,7 +386,7 @@ func TestSearchRecent(t *testing.T) { c.OK("A003") // Create a new message; it will be recent. - c.doAppend(mbox, `To: 1@pm.me`).expect("OK") + c.doAppend(mbox, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // It will be returned in the search result. c.C("A004 search recent") @@ -424,7 +425,7 @@ func TestSearchSentSinceAndSentBefore(t *testing.T) { // mail.ParseDate the date was being converted to 17 Feb 2003 22:29:37 +000, causing the search to pass // rather than fail. runOneToOneTestWithAuth(t, defaultServerOptions(t), func(c *testConnection, _ *testSession) { - c.doAppend("INBOX", "Date: 18 Feb 2003 00:29:37 +0200\n\nTo: foo@foo.com\r\n") + c.doAppend("INBOX", buildRFC5322TestLiteral("Date: 18 Feb 2003 00:29:37 +0200\n\nTo: foo@foo.com\r\n")) c.C(`A002 SELECT INBOX`) c.Se(`A002 OK [READ-WRITE] SELECT`) diff --git a/tests/select_test.go b/tests/select_test.go index d2475987..0b1ed07f 100644 --- a/tests/select_test.go +++ b/tests/select_test.go @@ -11,9 +11,9 @@ func TestSelect(t *testing.T) { c.C("A002 CREATE Archive") c.OK("A002") - c.doAppend(`INBOX`, `To: 1@pm.me`, `\Seen`).expect("OK") - c.doAppend(`INBOX`, `To: 2@pm.me`).expect("OK") - c.doAppend(`Archive`, `To: 3@pm.me`, `\Seen`).expect("OK") + c.doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c.doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c.doAppend(`Archive`, buildRFC5322TestLiteral(`To: 3@pm.me`), `\Seen`).expect("OK") c.C("A006 select INBOX") c.S(`* FLAGS (\Deleted \Flagged \Seen)`, diff --git a/tests/sequence_range_test.go b/tests/sequence_range_test.go index d4d2a9c2..66cca882 100644 --- a/tests/sequence_range_test.go +++ b/tests/sequence_range_test.go @@ -18,11 +18,11 @@ func TestSequenceRange(t *testing.T) { c.C(`A005 FETCH * (FLAGS)`).BAD(`A005`) c.C(`A006 FETCH 1:* (FLAGS)`).BAD(`A006`) - c.doAppend(`mbox1`, `To: 1@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 2@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 3@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 4@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 5@pm.me`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 4@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 5@pm.me`)).expect("OK") // test various set of ranges with STORE, FETCH, MOVE & COPY c.C(`A007 FETCH 1 (FLAGS)`) @@ -51,8 +51,8 @@ func TestSequenceRange(t *testing.T) { c.OK(`A013`) // test ranges given in reverse order - c.doAppend(`mbox1`, `To: 6@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 7@pm.me`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 6@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 7@pm.me`)).expect("OK") c.C(`A014 STORE 4:2 -FLAGS (flag)`) c.Sx(`\* 2 FETCH `) // 2 was the only message in 4:2 to have flag set c.OK(`A014`) @@ -84,11 +84,11 @@ func TestUIDSequenceRange(t *testing.T) { c.C(`A006 UID FETCH 1:* (FLAGS)`) c.OK(`A006`) - c.doAppend(`mbox1`, `To: 1@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 2@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 3@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 4@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 5@pm.me`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 4@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 5@pm.me`)).expect("OK") //// test various set of ranges with STORE, FETCH, MOVE & COPY c.C(`A007 UID FETCH 1 (FLAGS)`) @@ -120,8 +120,8 @@ func TestUIDSequenceRange(t *testing.T) { c.OK(`A013`) // test ranges given in reverse order - c.doAppend(`mbox1`, `To: 6@pm.me`).expect("OK") - c.doAppend(`mbox1`, `To: 7@pm.me`).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 6@pm.me`)).expect("OK") + c.doAppend(`mbox1`, buildRFC5322TestLiteral(`To: 7@pm.me`)).expect("OK") c.C(`A014 UID STORE 4:2 -FLAGS (flag)`) for i := 0; i < 2; i++ { c.Sx(`\* \d FETCH`) diff --git a/tests/status_test.go b/tests/status_test.go index c6942bb6..54acc7ea 100644 --- a/tests/status_test.go +++ b/tests/status_test.go @@ -9,9 +9,9 @@ func TestStatus(t *testing.T) { c.C("B001 CREATE blurdybloop") c.S("B001 OK CREATE") - c.doAppend(`blurdybloop`, `To: 1@pm.me`, `\Seen`).expect("OK") - c.doAppend(`blurdybloop`, `To: 2@pm.me`).expect("OK") - c.doAppend(`blurdybloop`, `To: 3@pm.me`).expect("OK") + c.doAppend(`blurdybloop`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c.doAppend(`blurdybloop`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c.doAppend(`blurdybloop`, buildRFC5322TestLiteral(`To: 3@pm.me`)).expect("OK") c.C("A042 STATUS blurdybloop (MESSAGES UNSEEN)") c.S(`* STATUS "blurdybloop" (MESSAGES 3 UNSEEN 2)`) @@ -23,7 +23,7 @@ func TestStatusWithUtf8MailboxNames(t *testing.T) { runOneToOneTestWithAuth(t, defaultServerOptions(t, withDelimiter(".")), func(c *testConnection, s *testSession) { s.mailboxCreated("user", []string{"mbox-öüäëæøå"}) s.flush("user") - c.doAppend(`mbox-&APYA,ADkAOsA5gD4AOU-`, `To: 1@pm.me`).expect("OK") + c.doAppend(`mbox-&APYA,ADkAOsA5gD4AOU-`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") c.C(`a STATUS mbox-&APYA,ADkAOsA5gD4AOU- (MESSAGES)`) c.S(`* STATUS "mbox-&APYA,ADkAOsA5gD4AOU-" (MESSAGES 1)`) c.OK(`a`) diff --git a/tests/store_test.go b/tests/store_test.go index 7387279c..9286190a 100644 --- a/tests/store_test.go +++ b/tests/store_test.go @@ -14,9 +14,9 @@ func TestStore(t *testing.T) { c.C("b001 CREATE saved-messages") c.S("b001 OK CREATE") - c.doAppend(`saved-messages`, `To: 1@pm.me`, `\Seen`).expect("OK") - c.doAppend(`saved-messages`, `To: 2@pm.me`).expect("OK") - c.doAppend(`saved-messages`, `To: 3@pm.me`, `\Seen`).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 3@pm.me`), `\Seen`).expect("OK") c.C(`A002 SELECT saved-messages`) c.Se(`A002 OK [READ-WRITE] SELECT`) @@ -61,7 +61,7 @@ func TestStore(t *testing.T) { func TestStoreSilent(t *testing.T) { runManyToOneTestWithAuth(t, defaultServerOptions(t), []int{1, 2}, func(c map[int]*testConnection, s *testSession) { // one message in INBOX - c[1].doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") // 2 sessions with INBOX selected // Message is only recent in the first. @@ -123,7 +123,7 @@ func TestStoreReadUnread(t *testing.T) { c[2].C(`tag select inbox`).OK(`tag`) for i := 1; i <= 10; i++ { - c[1].doAppend(`INBOX`, fmt.Sprintf(`To: %v@pm.me`, i)).expect("OK") + c[1].doAppend(`INBOX`, buildRFC5322TestLiteral(fmt.Sprintf(`To: %v@pm.me`, i))).expect("OK") c[1].C(`tag noop`).OK(`tag`) c[2].C(`tag noop`).OK(`tag`) @@ -157,9 +157,9 @@ func TestUIDStore(t *testing.T) { c.C("b001 CREATE saved-messages") c.S("b001 OK CREATE") - c.doAppend(`saved-messages`, `To: 1@pm.me`, `\Seen`).expect("OK") - c.doAppend(`saved-messages`, `To: 2@pm.me`).expect("OK") - c.doAppend(`saved-messages`, `To: 3@pm.me`, `\Seen`).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 1@pm.me`), `\Seen`).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 3@pm.me`), `\Seen`).expect("OK") c.C(`A002 SELECT saved-messages`) c.Se(`A002 OK [READ-WRITE] SELECT`) @@ -203,7 +203,7 @@ func TestUIDStore(t *testing.T) { func TestFlagsDuplicateAndCaseInsensitive(t *testing.T) { runOneToOneTestWithAuth(t, defaultServerOptions(t), func(c *testConnection, _ *testSession) { - c.doAppend(`INBOX`, `To: 1@pm.me`).expect("OK") + c.doAppend(`INBOX`, buildRFC5322TestLiteral(`To: 1@pm.me`)).expect("OK") c.C(`A001 SELECT INBOX`) c.Se(`A001 OK [READ-WRITE] SELECT`) @@ -259,7 +259,7 @@ func TestStoreFlagsPersistBetweenRuns(t *testing.T) { runOneToOneTestWithAuth(t, options, func(c *testConnection, _ *testSession) { c.C("b001 CREATE saved-messages") c.S("b001 OK CREATE") - c.doAppend(`saved-messages`, `To: 2@pm.me`).expect("OK") + c.doAppend(`saved-messages`, buildRFC5322TestLiteral(`To: 2@pm.me`)).expect("OK") }) // Check if recent flag was persisted and then mark the message as deleted. diff --git a/tests/testdata/embedded-rfc822.eml b/tests/testdata/embedded-rfc822.eml index 81d4d77c..2d98b21f 100644 --- a/tests/testdata/embedded-rfc822.eml +++ b/tests/testdata/embedded-rfc822.eml @@ -1,5 +1,6 @@ From: Nathaniel Borenstein To: Ned Freed +Date: 1 Jan 1970 00:00:00 +0000 Subject: Sample message MIME-Version: 1.0 Content-type: multipart/mixed; boundary="simple boundary"