Skip to content

Commit f165230

Browse files
committed
parseMail: add support for mbox
Add support for reading email from an mbox.
1 parent ee0ceba commit f165230

File tree

6 files changed

+1310
-6
lines changed

6 files changed

+1310
-6
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# Description
22

3-
Generates a ranked addressbook from a maildir (or similar one-mail-per-file)
4-
folder. It can be used in MUA's like [aerc](http://aerc-mail.org) or
5-
[mutt](http://www.mutt.org/) by grepping the list.
3+
Generates a ranked addressbook from your locally available email. It can be
4+
used in MUA's like [aerc](http://aerc-mail.org) or [mutt](http://www.mutt.org/)
5+
by grepping the list.
66

77
Why? No need to manually edit an address book, yet the cached ranking is
88
available extremely fast.
99

1010
### Features:
1111

1212
- scans all your emails
13+
- support maildir or similar one-email-per-file formats, and mbox
1314
- ranks based on both recency and frequency of addresses
1415
- collects from To, Cc, Bcc, From, Sender and Reply-To fields
1516
- ranks addresses explicitly emailed by you higher
@@ -63,9 +64,9 @@ Supported flags:
6364
**maildir**
6465

6566
The paths to the folders that will be scanned. No default is set for this.
66-
These actually do NOT need to be maildir format folders, it only assumes that
67-
each file is an email (it will skip any hidden files and anything that is in
68-
a folder called `tmp`).
67+
These actually do NOT need to be maildir format folders, it will try to read
68+
all files as an email or an mbox (it will skip any hidden files and anything
69+
that is in a folder called `tmp` or `.notmuch`).
6970

7071
**outputpath**
7172

e2e_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,13 @@ func TestE2EListTemplateDisable(t *testing.T) {
282282
})
283283
}
284284
}
285+
286+
func TestE2EMbox(t *testing.T) {
287+
data := walkSources(
288+
[]string{"./testdata/endtoend"},
289+
[]*regexp.Regexp{regexp.MustCompile("[email protected]")},
290+
nil,
291+
)
292+
classeddata := calculateRanks(data, nil, nil)
293+
assert.Contains(t, classeddata[0], "[email protected]")
294+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.22.0
55
toolchain go1.23.4
66

77
require (
8+
github.com/emersion/go-mbox v1.0.3
89
github.com/emersion/go-message v0.18.2
910
github.com/mitchellh/go-homedir v1.1.0
1011
github.com/spf13/pflag v1.0.5

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
22
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
33
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/emersion/go-mbox v1.0.3 h1:Kac75r/EGi6KZAz48HXal9q7EiaXNl+U5HZfyDz0LKM=
5+
github.com/emersion/go-mbox v1.0.3/go.mod h1:Yp9IVuuOYLEuMv4yjgDHvhb5mHOcYH6x92Oas3QqEZI=
46
github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg=
57
github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
68
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=

parseMail.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package main
22

33
import (
4+
"errors"
45
"fmt"
6+
"io"
57
"mime"
68
"os"
79
"path/filepath"
@@ -11,10 +13,33 @@ import (
1113
"sync"
1214
"unicode/utf8"
1315

16+
"github.com/emersion/go-mbox"
17+
"github.com/emersion/go-message"
1418
_ "github.com/emersion/go-message/charset"
1519
"github.com/emersion/go-message/mail"
1620
)
1721

22+
func mboxParser(path string, headers chan<- *mail.Header) error {
23+
f, err := os.Open(path)
24+
defer f.Close()
25+
if err != nil {
26+
return err
27+
}
28+
mbr := mbox.NewReader(f)
29+
for {
30+
msg, err := mbr.NextMessage()
31+
if errors.Is(err, io.EOF) {
32+
break
33+
} else if err != nil {
34+
return err
35+
}
36+
entity, err := message.Read(msg)
37+
h := &mail.Header{Header: entity.Header}
38+
headers <- h
39+
}
40+
return nil
41+
}
42+
1843
func messageParser(
1944
paths chan string,
2045
headers chan<- *mail.Header,
@@ -28,6 +53,10 @@ func messageParser(
2853
}
2954
r, err := mail.CreateReader(f)
3055
if err != nil {
56+
mboxerr := mboxParser(path, headers)
57+
if mboxerr == nil {
58+
continue
59+
}
3160
if utf8.ValidString(err.Error()) {
3261
fmt.Fprintln(os.Stderr, path, err)
3362
} else {

0 commit comments

Comments
 (0)