mirror of
https://github.com/axllent/mailpit.git
synced 2025-03-21 21:47:19 +02:00
Merge branch 'feature/reply-to' into develop
This commit is contained in:
commit
a856ce0cfa
@ -146,15 +146,15 @@ func Store(body *[]byte) (string, error) {
|
|||||||
from = &mail.Address{Name: env.GetHeader("From")}
|
from = &mail.Address{Name: env.GetHeader("From")}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageID := strings.Trim(env.Root.Header.Get("Message-ID"), "<>")
|
|
||||||
|
|
||||||
obj := DBMailSummary{
|
obj := DBMailSummary{
|
||||||
From: from,
|
From: from,
|
||||||
To: addressToSlice(env, "To"),
|
To: addressToSlice(env, "To"),
|
||||||
Cc: addressToSlice(env, "Cc"),
|
Cc: addressToSlice(env, "Cc"),
|
||||||
Bcc: addressToSlice(env, "Bcc"),
|
Bcc: addressToSlice(env, "Bcc"),
|
||||||
|
ReplyTo: addressToSlice(env, "Reply-To"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messageID := strings.Trim(env.Root.Header.Get("Message-ID"), "<>")
|
||||||
created := time.Now()
|
created := time.Now()
|
||||||
|
|
||||||
// use message date instead of created date
|
// use message date instead of created date
|
||||||
@ -294,6 +294,10 @@ func List(start, limit int) ([]MessageSummary, error) {
|
|||||||
em.Attachments = attachments
|
em.Attachments = attachments
|
||||||
em.Read = read == 1
|
em.Read = read == 1
|
||||||
em.Snippet = snippet
|
em.Snippet = snippet
|
||||||
|
// artificially generate ReplyTo if legacy data is missing Reply-To field
|
||||||
|
if em.ReplyTo == nil {
|
||||||
|
em.ReplyTo = []*mail.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
results = append(results, em)
|
results = append(results, em)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"net/mail"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/axllent/mailpit/internal/logger"
|
"github.com/axllent/mailpit/internal/logger"
|
||||||
@ -43,6 +45,7 @@ func ReindexAll() {
|
|||||||
ID string
|
ID string
|
||||||
SearchText string
|
SearchText string
|
||||||
Snippet string
|
Snippet string
|
||||||
|
Metadata string
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ids := range chunks {
|
for _, ids := range chunks {
|
||||||
@ -63,6 +66,28 @@ func ReindexAll() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
from := &mail.Address{}
|
||||||
|
fromJSON := addressToSlice(env, "From")
|
||||||
|
if len(fromJSON) > 0 {
|
||||||
|
from = fromJSON[0]
|
||||||
|
} else if env.GetHeader("From") != "" {
|
||||||
|
from = &mail.Address{Name: env.GetHeader("From")}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := DBMailSummary{
|
||||||
|
From: from,
|
||||||
|
To: addressToSlice(env, "To"),
|
||||||
|
Cc: addressToSlice(env, "Cc"),
|
||||||
|
Bcc: addressToSlice(env, "Bcc"),
|
||||||
|
ReplyTo: addressToSlice(env, "Reply-To"),
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataJSON, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log().Errorf("[message] %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
searchText := createSearchText(env)
|
searchText := createSearchText(env)
|
||||||
snippet := tools.CreateSnippet(env.Text, env.HTML)
|
snippet := tools.CreateSnippet(env.Text, env.HTML)
|
||||||
|
|
||||||
@ -70,6 +95,7 @@ func ReindexAll() {
|
|||||||
u.ID = id
|
u.ID = id
|
||||||
u.SearchText = searchText
|
u.SearchText = searchText
|
||||||
u.Snippet = snippet
|
u.Snippet = snippet
|
||||||
|
u.Metadata = string(MetadataJSON)
|
||||||
|
|
||||||
updates = append(updates, u)
|
updates = append(updates, u)
|
||||||
}
|
}
|
||||||
@ -86,7 +112,7 @@ func ReindexAll() {
|
|||||||
|
|
||||||
// insert mail summary data
|
// insert mail summary data
|
||||||
for _, u := range updates {
|
for _, u := range updates {
|
||||||
_, err = tx.Exec("UPDATE mailbox SET SearchText = ?, Snippet = ? WHERE ID = ?", u.SearchText, u.Snippet, u.ID)
|
_, err = tx.Exec("UPDATE mailbox SET SearchText = ?, Snippet = ?, Metadata = ? WHERE ID = ?", u.SearchText, u.Snippet, u.Metadata, u.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log().Errorf("[db] %s", err.Error())
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
continue
|
continue
|
||||||
|
@ -42,7 +42,7 @@ func Search(search string, start, limit int) ([]MessageSummary, int, error) {
|
|||||||
var ignore string
|
var ignore string
|
||||||
em := MessageSummary{}
|
em := MessageSummary{}
|
||||||
|
|
||||||
if err := row.Scan(&created, &id, &messageID, &subject, &metadata, &size, &attachments, &read, &snippet, &ignore, &ignore, &ignore, &ignore); err != nil {
|
if err := row.Scan(&created, &id, &messageID, &subject, &metadata, &size, &attachments, &read, &snippet, &ignore, &ignore, &ignore, &ignore, &ignore); err != nil {
|
||||||
logger.Log().Errorf("[db] %s", err.Error())
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ func DeleteSearch(search string) error {
|
|||||||
var snippet string
|
var snippet string
|
||||||
var ignore string
|
var ignore string
|
||||||
|
|
||||||
if err := row.Scan(&created, &id, &messageID, &subject, &metadata, &size, &attachments, &read, &snippet, &ignore, &ignore, &ignore, &ignore); err != nil {
|
if err := row.Scan(&created, &id, &messageID, &subject, &metadata, &size, &attachments, &read, &snippet, &ignore, &ignore, &ignore, &ignore, &ignore); err != nil {
|
||||||
logger.Log().Errorf("[db] %s", err.Error())
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -214,7 +214,8 @@ func searchQueryBuilder(searchString string) *sqlf.Stmt {
|
|||||||
IFNULL(json_extract(Metadata, '$.To'), '{}') as ToJSON,
|
IFNULL(json_extract(Metadata, '$.To'), '{}') as ToJSON,
|
||||||
IFNULL(json_extract(Metadata, '$.From'), '{}') as FromJSON,
|
IFNULL(json_extract(Metadata, '$.From'), '{}') as FromJSON,
|
||||||
IFNULL(json_extract(Metadata, '$.Cc'), '{}') as CcJSON,
|
IFNULL(json_extract(Metadata, '$.Cc'), '{}') as CcJSON,
|
||||||
IFNULL(json_extract(Metadata, '$.Bcc'), '{}') as BccJSON
|
IFNULL(json_extract(Metadata, '$.Bcc'), '{}') as BccJSON,
|
||||||
|
IFNULL(json_extract(Metadata, '$.ReplyTo'), '{}') as ReplyToJSON
|
||||||
`).
|
`).
|
||||||
OrderBy("m.Created DESC")
|
OrderBy("m.Created DESC")
|
||||||
|
|
||||||
@ -275,6 +276,15 @@ func searchQueryBuilder(searchString string) *sqlf.Stmt {
|
|||||||
q.Where("BccJSON LIKE ?", "%"+escPercentChar(w)+"%")
|
q.Where("BccJSON LIKE ?", "%"+escPercentChar(w)+"%")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if strings.HasPrefix(lw, "reply-to:") {
|
||||||
|
w = cleanString(w[9:])
|
||||||
|
if w != "" {
|
||||||
|
if exclude {
|
||||||
|
q.Where("ReplyToJSON NOT LIKE ?", "%"+escPercentChar(w)+"%")
|
||||||
|
} else {
|
||||||
|
q.Where("ReplyToJSON LIKE ?", "%"+escPercentChar(w)+"%")
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if strings.HasPrefix(lw, "subject:") {
|
} else if strings.HasPrefix(lw, "subject:") {
|
||||||
w = w[8:]
|
w = w[8:]
|
||||||
if w != "" {
|
if w != "" {
|
||||||
|
@ -17,9 +17,13 @@ func TestSearch(t *testing.T) {
|
|||||||
for i := 0; i < testRuns; i++ {
|
for i := 0; i < testRuns; i++ {
|
||||||
msg := enmime.Builder().
|
msg := enmime.Builder().
|
||||||
From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)).
|
From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)).
|
||||||
|
CC(fmt.Sprintf("CC %d", i), fmt.Sprintf("cc-%d@example.com", i)).
|
||||||
|
CC(fmt.Sprintf("CC2 %d", i), fmt.Sprintf("cc2-%d@example.com", i)).
|
||||||
Subject(fmt.Sprintf("Subject line %d end", i)).
|
Subject(fmt.Sprintf("Subject line %d end", i)).
|
||||||
Text([]byte(fmt.Sprintf("This is the email body %d <jdsauk;dwqmdqw;>.", i))).
|
Text([]byte(fmt.Sprintf("This is the email body %d <jdsauk;dwqmdqw;>.", i))).
|
||||||
To(fmt.Sprintf("To %d", i), fmt.Sprintf("to-%d@example.com", i))
|
To(fmt.Sprintf("To %d", i), fmt.Sprintf("to-%d@example.com", i)).
|
||||||
|
To(fmt.Sprintf("To2 %d", i), fmt.Sprintf("to2-%d@example.com", i)).
|
||||||
|
ReplyTo(fmt.Sprintf("Reply To %d", i), fmt.Sprintf("reply-to-%d@example.com", i))
|
||||||
|
|
||||||
env, err := msg.Build()
|
env, err := msg.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -44,18 +48,26 @@ func TestSearch(t *testing.T) {
|
|||||||
|
|
||||||
for i := 1; i < 51; i++ {
|
for i := 1; i < 51; i++ {
|
||||||
// search a random something that will return a single result
|
// search a random something that will return a single result
|
||||||
searchIdx := rand.Intn(4) + 1
|
uniqueSearches := []string{
|
||||||
var search string
|
fmt.Sprintf("from-%d@example.com", i),
|
||||||
switch searchIdx {
|
fmt.Sprintf("from:from-%d@example.com", i),
|
||||||
case 1:
|
fmt.Sprintf("to-%d@example.com", i),
|
||||||
search = fmt.Sprintf("from-%d@example.com", i)
|
fmt.Sprintf("to:to-%d@example.com", i),
|
||||||
case 2:
|
fmt.Sprintf("to2-%d@example.com", i),
|
||||||
search = fmt.Sprintf("to-%d@example.com", i)
|
fmt.Sprintf("to:to2-%d@example.com", i),
|
||||||
case 3:
|
fmt.Sprintf("cc-%d@example.com", i),
|
||||||
search = fmt.Sprintf("\"Subject line %d end\"", i)
|
fmt.Sprintf("cc:cc-%d@example.com", i),
|
||||||
default:
|
fmt.Sprintf("cc2-%d@example.com", i),
|
||||||
search = fmt.Sprintf("\"the email body %d jdsauk dwqmdqw\"", i)
|
fmt.Sprintf("cc:cc2-%d@example.com", i),
|
||||||
|
fmt.Sprintf("reply-to-%d@example.com", i),
|
||||||
|
fmt.Sprintf("reply-to:\"reply-to-%d@example.com\"", i),
|
||||||
|
fmt.Sprintf("\"Subject line %d end\"", i),
|
||||||
|
fmt.Sprintf("subject:\"Subject line %d end\"", i),
|
||||||
|
fmt.Sprintf("\"the email body %d jdsauk dwqmdqw\"", i),
|
||||||
}
|
}
|
||||||
|
searchIdx := rand.Intn(len(uniqueSearches))
|
||||||
|
|
||||||
|
search := uniqueSearches[searchIdx]
|
||||||
|
|
||||||
summaries, _, err := Search(search, 0, 100)
|
summaries, _, err := Search(search, 0, 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -63,7 +75,7 @@ func TestSearch(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEqual(t, len(summaries), 1, "1 search result expected")
|
assertEqual(t, len(summaries), 1, "search result expected")
|
||||||
|
|
||||||
assertEqual(t, summaries[0].From.Name, fmt.Sprintf("From %d", i), "\"From\" name does not match")
|
assertEqual(t, summaries[0].From.Name, fmt.Sprintf("From %d", i), "\"From\" name does not match")
|
||||||
assertEqual(t, summaries[0].From.Address, fmt.Sprintf("from-%d@example.com", i), "\"From\" address does not match")
|
assertEqual(t, summaries[0].From.Address, fmt.Sprintf("from-%d@example.com", i), "\"From\" address does not match")
|
||||||
|
@ -82,6 +82,8 @@ type MessageSummary struct {
|
|||||||
Cc []*mail.Address
|
Cc []*mail.Address
|
||||||
// Bcc addresses
|
// Bcc addresses
|
||||||
Bcc []*mail.Address
|
Bcc []*mail.Address
|
||||||
|
// Reply-To address
|
||||||
|
ReplyTo []*mail.Address
|
||||||
// Email subject
|
// Email subject
|
||||||
Subject string
|
Subject string
|
||||||
// Created time
|
// Created time
|
||||||
@ -105,10 +107,11 @@ type MailboxStats struct {
|
|||||||
|
|
||||||
// DBMailSummary struct for storing mail summary
|
// DBMailSummary struct for storing mail summary
|
||||||
type DBMailSummary struct {
|
type DBMailSummary struct {
|
||||||
From *mail.Address
|
From *mail.Address
|
||||||
To []*mail.Address
|
To []*mail.Address
|
||||||
Cc []*mail.Address
|
Cc []*mail.Address
|
||||||
Bcc []*mail.Address
|
Bcc []*mail.Address
|
||||||
|
ReplyTo []*mail.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachmentSummary returns a summary of the attachment without any binary data
|
// AttachmentSummary returns a summary of the attachment without any binary data
|
||||||
|
@ -1259,6 +1259,13 @@
|
|||||||
"description": "Read status",
|
"description": "Read status",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"ReplyTo": {
|
||||||
|
"description": "Reply-To address",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Address"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Size": {
|
"Size": {
|
||||||
"description": "Message size in bytes (total)",
|
"description": "Message size in bytes (total)",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user