fix(vacation): descodifica o assunto e responde a cada pessoa uma vez

O assunto que chegou a uma caixa real: "Fora do escritorio: Re:
=?UTF-8?Q?Re=3A_Fora_do_escrit=C3=B3rio?=". Duas coisas erradas, e a
segunda maior do que parece.

Um assunto com acento viaja como palavra codificada (RFC 2047). Colei o
cabecalho em bruto, portanto aquilo foi lido tal e qual por uma pessoa.
E foi tambem o que derrotou a verificacao do "Re:" ja existente:
codificado, todo o assunto comeca por "=?", nunca por "Re:", e por isso
foi acrescentado outro por cima. Descodificar corrige os dois sintomas
de uma vez. Os prefixos acumulados ("Re: RE: Re:") passam a ser
reduzidos a um.

O que estava por tras: aquele assunto era "Re: Fora do escritorio" -
alguem tinha respondido a propria mensagem automatica, e ela respondeu
de novo. Faltava a supressao por remetente. O RFC 5230 seccao 4.1 pede
exactamente isto e propoe sete dias: sem ela, uma conversa de cinco
mensagens produzia cinco copias do mesmo aviso, e quem respondesse ao
aviso recebia outro - que e como uma pessoa e um respondedor automatico
acabam a falar um para o outro indefinidamente.

A supressao falha aberta: um registo que nao se consegue ler deixa a
resposta sair. Ser avisado duas vezes e uma falha menor do que nunca
saber que a caixa esta ausente, que e o proposito da funcionalidade. O
envio so e registado depois de a resposta estar mesmo na fila, para que
um envio falhado nao silencie a mensagem seguinte da mesma pessoa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Opus 5 committed 2026-08-22 15:15:58 -03:00
1 parent 866bd21846
commit e4491479ef
10 files changed
+451 -6

No files matched your search

@@ -218,3 +218,65 @@ func TestDeliveredMessageProducesAThreadedReply(t *testing.T) {
}
}
}
// --- the subject as it actually arrives ----------------------------------
// A subject with an accent travels as an RFC 2047 encoded word. Pasting the
// raw header into the reply put "=?UTF-8?Q?Re=3A_Fora...?=" in front of a real
// person, and it also defeated the check for an existing "Re:" - encoded, the
// subject starts with "=?", so a second "Re:" was added on top.
func TestOriginalSubjectIsDecodedBeforeUse(t *testing.T) {
reply := replyTo("<abc@example.test>", "")
reply.OriginalSubject = "=?UTF-8?Q?Proposta_comercial?="
subject := vacationHeaders(t, reply)["subject"]
decoded := decodeHeaderValue(subject)
if strings.Contains(decoded, "=?UTF-8?") {
t.Errorf("an encoded word reached the reader: %q", decoded)
}
if !strings.Contains(decoded, "Proposta comercial") {
t.Errorf("subject %q", decoded)
}
}
// The same, for the "Re:" that is only visible once decoded.
func TestEncodedReplyPrefixIsNotDuplicated(t *testing.T) {
reply := replyTo("<abc@example.test>", "")
// "Re: Fora do escritorio", encoded the way a mail client sends it.
reply.OriginalSubject = "=?UTF-8?Q?Re=3A_Fora_do_escrit=C3=B3rio?="
decoded := decodeHeaderValue(vacationHeaders(t, reply)["subject"])
if strings.Count(strings.ToLower(decoded), "re:") > 1 {
t.Errorf("stacked Re: prefixes: %q", decoded)
}
}
// A thread that has been round several times accumulates prefixes. The reply
// carries one at most.
func TestManyReplyPrefixesCollapseToOne(t *testing.T) {
reply := replyTo("<abc@example.test>", "")
reply.OriginalSubject = "Re: RE: Re: Proposta comercial"
decoded := decodeHeaderValue(vacationHeaders(t, reply)["subject"])
if strings.Count(strings.ToLower(decoded), "re:") > 1 {
t.Errorf("prefixes were not collapsed: %q", decoded)
}
if !strings.Contains(decoded, "Proposta comercial") {
t.Errorf("the subject itself was lost: %q", decoded)
}
}
// The reply must not quote back the out-of-office text as though it were the
// subject of the conversation, which is what happens when someone replies to
// the automatic message itself.
func TestReplyToTheAutoReplyDoesNotEchoItself(t *testing.T) {
reply := replyTo("<abc@example.test>", "")
reply.Subject = "Fora do escritorio"
reply.OriginalSubject = "Re: Fora do escritorio"
decoded := decodeHeaderValue(vacationHeaders(t, reply)["subject"])
if strings.Count(strings.ToLower(decoded), "fora do escritorio") > 1 {
t.Errorf("the reply repeated its own subject back: %q", decoded)
}
}