aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/format/format.go20
-rw-r--r--worker/lib/parse.go73
2 files changed, 67 insertions, 26 deletions
diff --git a/lib/format/format.go b/lib/format/format.go
index 5f45e588..cc46da8c 100644
--- a/lib/format/format.go
+++ b/lib/format/format.go
@@ -5,6 +5,7 @@ import (
"fmt"
gomail "net/mail"
"strings"
+ "time"
"unicode"
"git.sr.ht/~sircmpwn/aerc/models"
@@ -111,13 +112,21 @@ func ParseMessageFormat(
retval = append(retval, 'd')
args = append(args, number)
case 'd':
+ date := msg.Envelope.Date
+ if date.IsZero() {
+ date = msg.InternalDate
+ }
retval = append(retval, 's')
args = append(args,
- msg.InternalDate.Format(timestampformat))
+ dummyIfZeroDate(date, timestampformat))
case 'D':
+ date := msg.Envelope.Date
+ if date.IsZero() {
+ date = msg.InternalDate
+ }
retval = append(retval, 's')
args = append(args,
- msg.InternalDate.Local().Format(timestampformat))
+ dummyIfZeroDate(date, timestampformat))
case 'f':
if msg.Envelope == nil {
return "", nil,
@@ -335,3 +344,10 @@ handle_end_error:
return "", nil,
errors.New("reached end of string while parsing message format")
}
+
+func dummyIfZeroDate(date time.Time, format string) string {
+ if date.IsZero() {
+ return strings.Repeat("?", len(format))
+ }
+ return date.Format(format)
+}
diff --git a/worker/lib/parse.go b/worker/lib/parse.go
index a14a6d33..58327c9b 100644
--- a/worker/lib/parse.go
+++ b/worker/lib/parse.go
@@ -102,11 +102,9 @@ func ParseEntityStructure(e *message.Entity) (*models.BodyStructure, error) {
return &body, nil
}
+var DateParseError = errors.New("date parsing failed")
+
func parseEnvelope(h *mail.Header) (*models.Envelope, error) {
- date, err := parseDate(h)
- if err != nil {
- return nil, fmt.Errorf("could not parse date header: %v", err)
- }
from, err := parseAddressList(h, "from")
if err != nil {
return nil, fmt.Errorf("could not read from address: %v", err)
@@ -135,6 +133,12 @@ func parseEnvelope(h *mail.Header) (*models.Envelope, error) {
if err != nil {
return nil, fmt.Errorf("could not read message id: %v", err)
}
+ date, err := parseDate(h)
+ if err != nil {
+ // still return a valid struct plus a sentinel date parsing error
+ // if only the date parsing failed
+ err = fmt.Errorf("%w: %v", DateParseError, err)
+ }
return &models.Envelope{
Date: date,
Subject: subj,
@@ -144,28 +148,24 @@ func parseEnvelope(h *mail.Header) (*models.Envelope, error) {
To: to,
Cc: cc,
Bcc: bcc,
- }, nil
+ }, err
}
-// parseDate extends the built-in date parser with additional layouts which are
-// non-conforming but appear in the wild.
+// parseDate tries to parse the date from the Date header with non std formats
+// if this fails it tries to parse the received header as well
func parseDate(h *mail.Header) (time.Time, error) {
- t, parseErr := h.Date()
- if parseErr == nil {
+ t, err := h.Date()
+ if err == nil {
return t, nil
}
text, err := h.Text("date")
- if err != nil {
- return time.Time{}, errors.New("no date header")
- }
- // sometimes, no error occurs but the date is empty. In this case, guess time from received header field
- if text == "" {
- guess, err := h.Text("received")
- if err != nil {
- return time.Time{}, errors.New("no received header")
+ // sometimes, no error occurs but the date is empty.
+ // In this case, guess time from received header field
+ if err != nil || text == "" {
+ t, err := parseReceivedHeader(h)
+ if err == nil {
+ return t, nil
}
- t, _ := time.Parse(time.RFC1123Z, dateRe.FindString(guess))
- return t, nil
}
layouts := []string{
// X-Mailer: EarthLink Zoo Mail 1.0
@@ -176,7 +176,21 @@ func parseDate(h *mail.Header) (time.Time, error) {
return t, nil
}
}
- return time.Time{}, fmt.Errorf("unrecognized date format: %s", text)
+ // still no success, try the received header as a last resort
+ t, err = parseReceivedHeader(h)
+ if err != nil {
+ return time.Time{}, fmt.Errorf("unrecognized date format: %s", text)
+ }
+ return t, nil
+}
+
+func parseReceivedHeader(h *mail.Header) (time.Time, error) {
+ guess, err := h.Text("received")
+ if err != nil {
+ return time.Time{}, fmt.Errorf("received header not parseable: %v",
+ err)
+ }
+ return time.Parse(time.RFC1123Z, dateRe.FindString(guess))
}
func parseAddressList(h *mail.Header, key string) ([]*models.Address, error) {
@@ -231,9 +245,20 @@ func MessageInfo(raw RawMessage) (*models.MessageInfo, error) {
if err != nil {
return nil, fmt.Errorf("could not get structure: %v", err)
}
- env, err := parseEnvelope(&mail.Header{msg.Header})
- if err != nil {
- return nil, fmt.Errorf("could not get envelope: %v", err)
+ h := &mail.Header{msg.Header}
+ env, err := parseEnvelope(h)
+ if err != nil && !errors.Is(err, DateParseError) {
+ return nil, fmt.Errorf("could not parse envelope: %v", err)
+ // if only the date parsing failed we still get the rest of the
+ // envelop structure in a valid state.
+ // Date parsing errors are fairly common and it's better to be
+ // slightly off than to not be able to read the mails at all
+ // hence we continue here
+ }
+ recDate, _ := parseReceivedHeader(h)
+ if recDate.IsZero() {
+ // better than nothing, if incorrect
+ recDate = env.Date
}
flags, err := raw.ModelFlags()
if err != nil {
@@ -248,7 +273,7 @@ func MessageInfo(raw RawMessage) (*models.MessageInfo, error) {
Envelope: env,
Flags: flags,
Labels: labels,
- InternalDate: env.Date,
+ InternalDate: recDate,
RFC822Headers: &mail.Header{msg.Header},
Size: 0,
Uid: raw.UID(),