Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions shortcuts/mail/emlbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ type Builder struct {
inReplyTo string // raw value, without angle brackets
references string // space-separated list of message IDs, with angle brackets
lmsReplyToMessageID string // Lark internal message_id of the original message
lmsReplyType string // REPLY or FORWARD, written as X-LMS-Reply-Type header
textBody []byte
htmlBody []byte
calendarBody []byte
Expand Down Expand Up @@ -391,6 +392,26 @@ func (b Builder) LMSReplyToMessageID(id string) Builder {
return b
}

// LMSReplyType sets the reply type so the backend can label the original message
// as replied ("REPLY") or forwarded ("FORWARD"). Written as the X-LMS-Reply-Type
// header. Only "REPLY" and "FORWARD" are accepted; any other value is a no-op
// (no header emitted) to avoid leaking unexpected values.
// Returns an error builder if the value contains CR or LF.
func (b Builder) LMSReplyType(t string) Builder {
if b.err != nil {
return b
}
if t != "REPLY" && t != "FORWARD" {
return b
}
if err := validateHeaderValue(t); err != nil {
b.err = err
return b
}
b.lmsReplyType = t
return b
}

// References sets the References header value verbatim.
// Typically a space-separated list of message IDs including angle brackets,
// e.g. "<id1@host> <id2@host>".
Expand Down Expand Up @@ -721,6 +742,9 @@ func (b Builder) Build() ([]byte, error) {
if b.lmsReplyToMessageID != "" {
writeHeader(&buf, "X-LMS-Reply-To-Message-Id", b.lmsReplyToMessageID)
}
if b.lmsReplyType != "" {
writeHeader(&buf, "X-LMS-Reply-Type", b.lmsReplyType)
}
}
if b.references != "" {
writeHeader(&buf, "References", b.references)
Expand Down
49 changes: 49 additions & 0 deletions shortcuts/mail/emlbuilder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,55 @@ func TestBuild_LMSReplyToMessageID(t *testing.T) {
}
}

// TestBuild_LMSReplyType verifies build LMS reply type header.
func TestBuild_LMSReplyType(t *testing.T) {
raw, err := New().
From("", "alice@example.com").
To("", "bob@example.com").
Subject("Re: hello").
Date(fixedDate).
InReplyTo("original@smtp").
LMSReplyToMessageID("740000000000000067").
LMSReplyType("REPLY").
TextBody([]byte("my reply")).
Build()
if err != nil {
t.Fatal(err)
}
eml := string(raw)

got := headerValue(eml, "X-LMS-Reply-Type")
if got != "REPLY" {
t.Errorf("X-LMS-Reply-Type: got %q, want REPLY", got)
}
if !strings.Contains(eml, "X-LMS-Reply-Type: REPLY") {
t.Errorf("eml should contain header line \"X-LMS-Reply-Type: REPLY\", got:\n%s", eml)
}
}

// TestBuild_LMSReplyType_InvalidValueNotWritten verifies an invalid reply type is ignored.
func TestBuild_LMSReplyType_InvalidValueNotWritten(t *testing.T) {
raw, err := New().
From("", "alice@example.com").
To("", "bob@example.com").
Subject("Re: hello").
Date(fixedDate).
InReplyTo("original@smtp").
LMSReplyToMessageID("740000000000000067").
LMSReplyType("BOGUS").
TextBody([]byte("my reply")).
Build()
if err != nil {
t.Fatal(err)
}
eml := string(raw)

got := headerValue(eml, "X-LMS-Reply-Type")
if got != "" {
t.Errorf("X-LMS-Reply-Type should be absent for invalid value, got %q", got)
}
}

// TestBuild_LMSReplyToMessageID_NotWrittenWithoutInReplyTo verifies build LMS reply to message ID not written without in reply to.
func TestBuild_LMSReplyToMessageID_NotWrittenWithoutInReplyTo(t *testing.T) {
raw, err := New().
Expand Down
1 change: 1 addition & 0 deletions shortcuts/mail/mail_forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ var MailForward = common.Shortcut{
}
if messageId != "" {
bld = bld.LMSReplyToMessageID(messageId)
bld = bld.LMSReplyType("FORWARD")
}
useHTML := !plainText && (bodyIsHTML(body) || bodyIsHTML(orig.bodyRaw) || sigResult != nil)
if strings.TrimSpace(inlineFlag) != "" && !useHTML {
Expand Down
1 change: 1 addition & 0 deletions shortcuts/mail/mail_reply.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ var MailReply = common.Shortcut{
}
if messageId != "" {
bld = bld.LMSReplyToMessageID(messageId)
bld = bld.LMSReplyType("REPLY")
}
var autoResolvedPaths []string
var composedHTMLBody string
Expand Down
1 change: 1 addition & 0 deletions shortcuts/mail/mail_reply_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ var MailReplyAll = common.Shortcut{
}
if messageId != "" {
bld = bld.LMSReplyToMessageID(messageId)
bld = bld.LMSReplyType("REPLY")
}
var autoResolvedPaths []string
var composedHTMLBody string
Expand Down
Loading