about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-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)