diff --git a/Changelog.md b/Changelog.md index ae1d6b76..abd9f5c1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -36,7 +36,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/) * Alternative Routing is enabled/disabled by `ClientManager` * Logging out of `Clients` is handled/retried asynchronously by `ClientManager` * GODT-265 Alternative Routing v2 (more resiliant to short term connection drops) - +* GODT-310 Alternative parsing of `References` header (old parsing probably malformed message IDs) ### Fixed * Use correct binary name when finding location of addcert.scpt diff --git a/internal/imap/mailbox_message.go b/internal/imap/mailbox_message.go index e1d0e19c..1f907d47 100644 --- a/internal/imap/mailbox_message.go +++ b/internal/imap/mailbox_message.go @@ -148,10 +148,10 @@ func (im *imapMailbox) CreateMessage(flags []string, date time.Time, body imap.L if len(referenceList) > 0 { lastReference := referenceList[len(referenceList)-1] // In case we are using a mail client which corrupts headers, try "References" too. - re := regexp.MustCompile(`(?U)<.*@protonmail.internalid>`) - match := re.FindString(lastReference) - if match != "" { - internalID = match[1 : len(match)-len("@protonmail.internalid>")] + re := regexp.MustCompile(pmapi.InternalReferenceFormat) + match := re.FindStringSubmatch(lastReference) + if len(match) > 0 { + internalID = match[1] } } diff --git a/internal/smtp/user.go b/internal/smtp/user.go index b2064cd8..5381e8bc 100644 --- a/internal/smtp/user.go +++ b/internal/smtp/user.go @@ -370,12 +370,12 @@ func (su *smtpUser) handleReferencesHeader(m *pmapi.Message) (draftID, parentID references := m.Header.Get("References") newReferences := []string{} for _, reference := range strings.Fields(references) { - if !strings.Contains(reference, "@protonmail.internalid") { + if !strings.Contains(reference, "@"+pmapi.InternalIDDomain) { newReferences = append(newReferences, reference) } else { // internalid is the parentID. - idMatch := regexp.MustCompile(`(?U)<.*@protonmail.internalid>`).FindString(reference) - if idMatch != "" { - lastID := idMatch[1 : len(idMatch)-len("@protonmail.internalid>")] + idMatch := regexp.MustCompile(pmapi.InternalReferenceFormat).FindStringSubmatch(reference) + if len(idMatch) > 0 { + lastID := idMatch[1] filter := &pmapi.MessagesFilter{ID: []string{lastID}} if su.addressID != "" { filter.AddressID = su.addressID diff --git a/pkg/message/header.go b/pkg/message/header.go index 388065e1..f2b780f0 100644 --- a/pkg/message/header.go +++ b/pkg/message/header.go @@ -74,19 +74,19 @@ func GetHeader(msg *pmapi.Message) textproto.MIMEHeader { //nolint[funlen] } if msg.ID != "" { if h.Get("Message-Id") == "" { - h.Set("Message-Id", "<"+msg.ID+"@protonmail.internalid>") + h.Set("Message-Id", "<"+msg.ID+"@"+pmapi.InternalIDDomain+">") } h.Set("X-Pm-Internal-Id", msg.ID) // Forward References, and include the message ID here (to improve outlook support). if references := h.Get("References"); !strings.Contains(references, msg.ID) { - references += " <" + msg.ID + "@protonmail.internalid>" + references += " <" + msg.ID + "@" + pmapi.InternalIDDomain + ">" h.Set("References", references) } } if msg.ConversationID != "" { h.Set("X-Pm-ConversationID-Id", msg.ConversationID) if references := h.Get("References"); !strings.Contains(references, msg.ConversationID) { - references += " <" + msg.ConversationID + "@protonmail.conversationid>" + references += " <" + msg.ConversationID + "@" + pmapi.ConversationIDDomain + ">" h.Set("References", references) } } diff --git a/pkg/pmapi/messages.go b/pkg/pmapi/messages.go index 696fb443..f2c8ddd4 100644 --- a/pkg/pmapi/messages.go +++ b/pkg/pmapi/messages.go @@ -141,6 +141,15 @@ const ( // Due to API limitations, we shouldn't make requests with more than 100 message IDs at a time. const messageIDPageSize = 100 +// ConversationIDDomain is used as a placeholder for conversation reference headers to improve compatibility with various clients +const ConversationIDDomain = `protonmail.conversationid` + +// InternalIDDomain is used as a placeholder for reference/message ID headers to improve compatibility with various clients +const InternalIDDomain = `protonmail.internalid` + +// InternalReferenceFormat describes format of the message ID (as regex) used for parsing reference headers +const InternalReferenceFormat = `(?U)<.*@` + InternalIDDomain + `>` + // Message structure. type Message struct { ID string `json:",omitempty"`