summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReto Brunner <reto@labrat.space>2020-08-19 12:01:45 +0200
committerReto Brunner <reto@labrat.space>2020-08-20 19:18:57 +0200
commitc84630714405a1e93766a6a6c023801302a3ea66 (patch)
tree156e9f45c6847a123f677594c8aff3a82322bce8
parentfe1cabb077cf6c6cb3de122b3f5532acbeba8c85 (diff)
downloadaerc-c84630714405a1e93766a6a6c023801302a3ea66.zip
base models.Address on the mail.Address type
This allows us to hook into the std libs implementation of parsing related stuff. For this, we need to get rid of the distinction between a mailbox and a host to just a single "address" field. However this is already the common case. All but one users immediately concatenated the mbox/domain to a single address. So this in effects makes it simpler for most cases and we simply do the transformation in the special case.
-rw-r--r--commands/msg/forward.go3
-rw-r--r--lib/format/format.go60
-rw-r--r--models/models.go34
-rw-r--r--widgets/msgviewer.go15
-rw-r--r--worker/imap/imap.go3
-rw-r--r--worker/lib/parse.go11
-rw-r--r--worker/lib/sort.go3
7 files changed, 67 insertions, 62 deletions
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index 3ff0194..0c6b0e0 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -11,6 +11,7 @@ import (
"strings"
"git.sr.ht/~sircmpwn/aerc/lib"
+ "git.sr.ht/~sircmpwn/aerc/lib/format"
"git.sr.ht/~sircmpwn/aerc/models"
"git.sr.ht/~sircmpwn/aerc/widgets"
"git.sr.ht/~sircmpwn/aerc/worker/types"
@@ -77,7 +78,7 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error {
addTab := func() (*widgets.Composer, error) {
if template != "" {
- original.From = models.FormatAddresses(msg.Envelope.From)
+ original.From = format.FormatAddresses(msg.Envelope.From)
original.Date = msg.Envelope.Date
}
diff --git a/lib/format/format.go b/lib/format/format.go
index cc46da8..06b4d3f 100644
--- a/lib/format/format.go
+++ b/lib/format/format.go
@@ -2,22 +2,46 @@ package format
import (
"errors"
- "fmt"
+ "mime"
gomail "net/mail"
"strings"
"time"
"unicode"
"git.sr.ht/~sircmpwn/aerc/models"
+ "github.com/emersion/go-message"
)
-func parseAddress(address string) *gomail.Address {
+func ParseAddress(address string) (*models.Address, error) {
addrs, err := gomail.ParseAddress(address)
if err != nil {
- return nil
+ return nil, err
+ }
+ return (*models.Address)(addrs), nil
+}
+
+func ParseAddressList(s string) ([]*models.Address, error) {
+ parser := gomail.AddressParser{
+ &mime.WordDecoder{message.CharsetReader},
+ }
+ list, err := parser.ParseList(s)
+ if err != nil {
+ return nil, err
}
- return addrs
+ addrs := make([]*models.Address, len(list))
+ for i, a := range list {
+ addrs[i] = (*models.Address)(a)
+ }
+ return addrs, nil
+}
+
+func FormatAddresses(l []*models.Address) string {
+ formatted := make([]string, len(l))
+ for i, a := range l {
+ formatted[i] = a.Format()
+ }
+ return strings.Join(formatted, ", ")
}
func ParseMessageFormat(
@@ -29,7 +53,10 @@ func ParseMessageFormat(
retval := make([]byte, 0, len(format))
var args []interface{}
- accountFromAddress := parseAddress(fromAddress)
+ accountFromAddress, err := ParseAddress(fromAddress)
+ if err != nil {
+ return "", nil, err
+ }
var c rune
for i, ni := 0, 0; i < len(format); {
@@ -87,8 +114,7 @@ func ParseMessageFormat(
}
addr := msg.Envelope.From[0]
retval = append(retval, 's')
- args = append(args,
- fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
+ args = append(args, addr.Address)
case 'A':
if msg.Envelope == nil {
return "", nil,
@@ -106,8 +132,7 @@ func ParseMessageFormat(
addr = msg.Envelope.ReplyTo[0]
}
retval = append(retval, 's')
- args = append(args,
- fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
+ args = append(args, addr.Address)
case 'C':
retval = append(retval, 'd')
args = append(args, number)
@@ -158,7 +183,7 @@ func ParseMessageFormat(
if addr.Name != "" {
val = addr.Name
} else {
- val = fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
+ val = addr.Address
}
retval = append(retval, 's')
args = append(args, val)
@@ -188,7 +213,7 @@ func ParseMessageFormat(
if addr.Name != "" {
val = addr.Name
} else {
- val = fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
+ val = addr.Address
}
retval = append(retval, 's')
args = append(args, val)
@@ -197,7 +222,7 @@ func ParseMessageFormat(
return "", nil,
errors.New("no envelope available for this message")
}
- addrs := models.FormatAddresses(msg.Envelope.To)
+ addrs := FormatAddresses(msg.Envelope.To)
retval = append(retval, 's')
args = append(args, addrs)
case 'R':
@@ -205,7 +230,7 @@ func ParseMessageFormat(
return "", nil,
errors.New("no envelope available for this message")
}
- addrs := models.FormatAddresses(msg.Envelope.Cc)
+ addrs := FormatAddresses(msg.Envelope.Cc)
retval = append(retval, 's')
args = append(args, addrs)
case 's':
@@ -226,8 +251,7 @@ func ParseMessageFormat(
}
addr := msg.Envelope.To[0]
retval = append(retval, 's')
- args = append(args,
- fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
+ args = append(args, addr.Address)
case 'T':
retval = append(retval, 's')
args = append(args, accountName)
@@ -241,8 +265,12 @@ func ParseMessageFormat(
errors.New("found no address for sender")
}
addr := msg.Envelope.From[0]
+ mailbox := addr.Address // fallback if there's no @ sign
+ if split := strings.SplitN(addr.Address, "@", 2); len(split) == 2 {
+ mailbox = split[1]
+ }
retval = append(retval, 's')
- args = append(args, addr.Mailbox)
+ args = append(args, mailbox)
case 'v':
if msg.Envelope == nil {
return "", nil,
diff --git a/models/models.go b/models/models.go
index d61b774..b7d7e05 100644
--- a/models/models.go
+++ b/models/models.go
@@ -1,9 +1,9 @@
package models
import (
- "bytes"
"fmt"
"io"
+ gomail "net/mail"
"regexp"
"strings"
"time"
@@ -134,38 +134,24 @@ type Envelope struct {
MessageId string
}
-type Address struct {
- Name string
- Mailbox string
- Host string
-}
+type Address gomail.Address
var atom *regexp.Regexp = regexp.MustCompile("^[a-z0-9!#$%7'*+-/=?^_`{}|~ ]+$")
-func (a Address) Format() string {
+// String formats the address. If the address's name
+// contains non-ASCII characters it will be quoted but not encoded.
+// Meant for display purposes to the humans, not for sending over the wire.
+func (a *Address) Format() string {
if a.Name != "" {
if atom.MatchString(a.Name) {
- return fmt.Sprintf("%s <%s@%s>", a.Name, a.Mailbox, a.Host)
+ return fmt.Sprintf("%s <%s>", a.Name, a.Address)
} else {
- return fmt.Sprintf("\"%s\" <%s@%s>",
- strings.ReplaceAll(a.Name, "\"", "'"),
- a.Mailbox, a.Host)
+ return fmt.Sprintf("\"%s\" <%s>",
+ strings.ReplaceAll(a.Name, "\"", "'"), a.Address)
}
} else {
- return fmt.Sprintf("<%s@%s>", a.Mailbox, a.Host)
- }
-}
-
-// FormatAddresses formats a list of addresses, separating each by a comma
-func FormatAddresses(addrs []*Address) string {
- val := bytes.Buffer{}
- for i, addr := range addrs {
- val.WriteString(addr.Format())
- if i != len(addrs)-1 {
- val.WriteString(", ")
- }
+ return fmt.Sprintf("<%s>", a.Address)
}
- return val.String()
}
// OriginalMail is helper struct used for reply/forward
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index 6a91741..587959b 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -17,6 +17,7 @@ import (
"git.sr.ht/~sircmpwn/aerc/config"
"git.sr.ht/~sircmpwn/aerc/lib"
+ "git.sr.ht/~sircmpwn/aerc/lib/format"
"git.sr.ht/~sircmpwn/aerc/lib/ui"
"git.sr.ht/~sircmpwn/aerc/models"
)
@@ -130,13 +131,13 @@ func NewMessageViewer(acct *AccountView,
func fmtHeader(msg *models.MessageInfo, header string, timefmt string) string {
switch header {
case "From":
- return models.FormatAddresses(msg.Envelope.From)
+ return format.FormatAddresses(msg.Envelope.From)
case "To":
- return models.FormatAddresses(msg.Envelope.To)
+ return format.FormatAddresses(msg.Envelope.To)
case "Cc":
- return models.FormatAddresses(msg.Envelope.Cc)
+ return format.FormatAddresses(msg.Envelope.Cc)
case "Bcc":
- return models.FormatAddresses(msg.Envelope.Bcc)
+ return format.FormatAddresses(msg.Envelope.Bcc)
case "Date":
return msg.Envelope.Date.Local().Format(timefmt)
case "Subject":
@@ -496,11 +497,11 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
case "subject":
header = info.Envelope.Subject
case "from":
- header = models.FormatAddresses(info.Envelope.From)
+ header = format.FormatAddresses(info.Envelope.From)
case "to":
- header = models.FormatAddresses(info.Envelope.To)
+ header = format.FormatAddresses(info.Envelope.To)
case "cc":
- header = models.FormatAddresses(info.Envelope.Cc)
+ header = format.FormatAddresses(info.Envelope.Cc)
}
if f.Regex.Match([]byte(header)) {
filter = exec.Command("sh", "-c", f.Command)
diff --git a/worker/imap/imap.go b/worker/imap/imap.go
index 7afab02..aa1854d 100644
--- a/worker/imap/imap.go
+++ b/worker/imap/imap.go
@@ -64,8 +64,7 @@ func translateAddresses(addrs []*imap.Address) []*models.Address {
for _, addr := range addrs {
converted = append(converted, &models.Address{
Name: addr.PersonalName,
- Mailbox: addr.MailboxName,
- Host: addr.HostName,
+ Address: addr.Address(),
})
}
return converted
diff --git a/worker/lib/parse.go b/worker/lib/parse.go
index 58327c9..b003d96 100644
--- a/worker/lib/parse.go
+++ b/worker/lib/parse.go
@@ -205,18 +205,9 @@ func parseAddressList(h *mail.Header, key string) ([]*models.Address, error) {
return nil, err
}
for _, addr := range addrs {
- parts := strings.Split(addr.Address, "@")
- var mbox, host string
- if len(parts) > 1 {
- mbox = strings.Join(parts[0:len(parts)-1], "@")
- host = parts[len(parts)-1]
- } else {
- mbox = addr.Address
- }
converted = append(converted, &models.Address{
Name: addr.Name,
- Mailbox: mbox,
- Host: host,
+ Address: addr.Address,
})
}
return converted, nil
diff --git a/worker/lib/sort.go b/worker/lib/sort.go
index ac8ed07..9d1f50a 100644
--- a/worker/lib/sort.go
+++ b/worker/lib/sort.go
@@ -1,7 +1,6 @@
package lib
import (
- "fmt"
"sort"
"strings"
@@ -83,7 +82,7 @@ func sortAddresses(messageInfos []*models.MessageInfo, criterion *types.SortCrit
if addr.Name != "" {
return addr.Name
} else {
- return fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
+ return addr.Address
}
}
return getName(firstI) < getName(firstJ)