From 6fe1bcf384a34fdfc1704ff98ee5151a75d031a2 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 5 Jul 2017 19:45:56 +0200 Subject: pkg/email: add AddAddrContext/RemoveAddrContext Replace extractBugID function with more general AddAddrContext/RemoveAddrContext. --- pkg/email/parser.go | 69 +++++++++++++++++++++++------------------ pkg/email/parser_test.go | 81 +++++++++++++++++++++++++++++------------------- 2 files changed, 88 insertions(+), 62 deletions(-) diff --git a/pkg/email/parser.go b/pkg/email/parser.go index 5d8c54d2c..f0297a588 100644 --- a/pkg/email/parser.go +++ b/pkg/email/parser.go @@ -50,11 +50,12 @@ func Parse(r io.Reader, ownEmail string) (*Email, error) { bugID := "" var ccList []string for _, addr := range append(cc, to...) { - bugID1, own := extractBugID(addr.Address, ownEmail) - if bugID == "" { - bugID = bugID1 - } - if !own { + cleaned, context, _ := RemoveAddrContext(addr.Address) + if cleaned == ownEmail { + if bugID == "" { + bugID = context + } + } else { ccList = append(ccList, addr.String()) } } @@ -87,31 +88,39 @@ func Parse(r io.Reader, ownEmail string) (*Email, error) { return email, nil } -// extractBugID extracts bug ID encoded in receiver email. -// We send emails from . -// from is potentially such email address, canonical is . -// This function returns BUG_ID_HASH, or an empty string if from does not contain -// the hash or is different from canonical. -func extractBugID(from, canonical string) (string, bool) { - if email, err := mail.ParseAddress(canonical); err == nil { - canonical = email.Address - } - canonical = strings.ToLower(canonical) - plusPos := strings.IndexByte(from, '+') - if plusPos == -1 { - return "", strings.ToLower(from) == canonical - } - atPos := strings.IndexByte(from[plusPos:], '@') - if atPos == -1 { - return "", false - } - user := from[:plusPos] - domain := from[plusPos+atPos:] - hash := from[plusPos+1 : plusPos+atPos] - if strings.ToLower(user+domain) != canonical { - return "", false - } - return hash, true +// AddAddrContext embeds context into local part of the provided email address using '+'. +// Returns the resulting email address. +func AddAddrContext(email, context string) (string, error) { + addr, err := mail.ParseAddress(email) + if err != nil { + return "", fmt.Errorf("failed to parse %q as email: %v", email, err) + } + at := strings.IndexByte(addr.Address, '@') + if at == -1 { + return "", fmt.Errorf("failed to parse %q as email: no @", email) + } + addr.Address = addr.Address[:at] + "+" + context + addr.Address[at:] + return addr.String(), nil +} + +// RemoveAddrContext extracts context after '+' from the local part of the provided email address. +// Returns address without the context and the context. +func RemoveAddrContext(email string) (string, string, error) { + addr, err := mail.ParseAddress(email) + if err != nil { + return "", "", fmt.Errorf("failed to parse %q as email: %v", email, err) + } + at := strings.IndexByte(addr.Address, '@') + if at == -1 { + return "", "", fmt.Errorf("failed to parse %q as email: no @", email) + } + plus := strings.LastIndexByte(addr.Address[:at], '+') + if plus == -1 { + return email, "", nil + } + context := addr.Address[plus+1 : at] + addr.Address = addr.Address[:plus] + addr.Address[at:] + return addr.String(), context, nil } // extractCommand extracts command to syzbot from email body. diff --git a/pkg/email/parser_test.go b/pkg/email/parser_test.go index 7b2aa9a0f..d05a597be 100644 --- a/pkg/email/parser_test.go +++ b/pkg/email/parser_test.go @@ -23,16 +23,55 @@ func TestExtractCommand(t *testing.T) { } } -func TestExtractBugID(t *testing.T) { - for i, test := range extractBugIDTests { - t.Run(fmt.Sprint(i), func(t *testing.T) { - bugID, own := extractBugID(test.email, `"Foo Bar" `) - if bugID != test.bugID || own != test.own { - t.Logf("expect: own=%v %q", test.own, test.bugID) - t.Logf("got : own=%v %q", test.own, bugID) - t.Fail() - } - }) +func TestAddRemoveAddrContext(t *testing.T) { + email := `"Foo Bar" ` + email00, context00, err := RemoveAddrContext(email) + if err != nil { + t.Fatal(err) + } + if email != email00 { + t.Fatalf("want: %q, got %q", email, email00) + } + if context00 != "" { + t.Fatalf("want context: %q, got %q", "", context00) + } + context1 := "context1" + email1, err := AddAddrContext(email, context1) + if err != nil { + t.Fatal(err) + } + want1 := `"Foo Bar" ` + if want1 != email1 { + t.Fatalf("want: %q, got %q", want1, email1) + } + context2 := "context2" + email2, err := AddAddrContext(email1, context2) + if err != nil { + t.Fatal(err) + } + want2 := `"Foo Bar" ` + if want2 != email2 { + t.Fatalf("want: %q, got %q", want2, email2) + } + email1, context20, err := RemoveAddrContext(email2) + if err != nil { + t.Fatal(err) + } + if want1 != email1 { + t.Fatalf("want: %q, got %q", want1, email1) + } + if context2 != context20 { + t.Fatalf("want context: %q, got %q", context2, context20) + } + email0, context10, err := RemoveAddrContext(email1) + if err != nil { + t.Fatal(err) + } + if email != email0 { + t.Fatalf("want: %q, got %q", email, email0) + } + if context1 != context10 { + t.Fatalf("want context: %q, got %q", context1, context10) } } @@ -86,28 +125,6 @@ line 2 }, } -var extractBugIDTests = []struct { - email string - bugID string - own bool -}{ - { - `foo@bar.com`, - ``, - true, - }, - { - `foo+123@baz.com`, - ``, - false, - }, - { - `foo+123@bar.com`, - `123`, - true, - }, -} - var parseTests = []struct { email string res *Email -- cgit mrf-deployment