Add utility to generate and save hashes of email and unread state.
This commit is contained in:
parent
8523965168
commit
9a42a60278
94
cmd/mailhash/mailhash.go
Normal file
94
cmd/mailhash/mailhash.go
Normal file
@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"xinu.tv/email"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
saveFile = flag.String("save", "", "filename to store unread status.")
|
||||
loadFile = flag.String("load", "", "filename to load unread status.")
|
||||
maildir = flag.String("maildir", "", "Maildir root")
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
Hash string `json:"hash"`
|
||||
Path string `json:"path,omitempthashy"`
|
||||
Unread bool `json:"unread,omitempty"`
|
||||
}
|
||||
|
||||
type Messages []Status
|
||||
|
||||
func (m *Messages) hashMail(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
}
|
||||
headers := []string{"to", "from", "cc", "date", "subject"}
|
||||
h, err := email.Hash(r, headers)
|
||||
if err != nil {
|
||||
glog.Errorln(path, err)
|
||||
return nil
|
||||
}
|
||||
md := email.NewInfo(path)
|
||||
//fmt.Printf("%x %s\n", h.Sum(nil), path)
|
||||
*m = append(*m, Status{
|
||||
Path: path,
|
||||
Hash: fmt.Sprintf("%x", h.Sum(nil)),
|
||||
Unread: !md.Seen,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Messages) SaveStatus(maildir, saveFile string) error {
|
||||
if err := filepath.Walk(maildir, m.hashMail); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(saveFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
n, err := f.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(b) {
|
||||
return fmt.Errorf("Short write of save status: wrote %d of %d", n,
|
||||
len(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer glog.Flush()
|
||||
flag.Parse()
|
||||
m := Messages{}
|
||||
|
||||
if *maildir == "" {
|
||||
glog.Fatal("Must specify Maildir with -maildir")
|
||||
}
|
||||
|
||||
if *saveFile != "" {
|
||||
if err := m.SaveStatus(*maildir, *saveFile); err != nil {
|
||||
glog.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
hash.go
Normal file
38
hash.go
Normal file
@ -0,0 +1,38 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
"io"
|
||||
"net/mail"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Hash will parse r as an email, and return a hash.Hash that has been applied
|
||||
// to the values of the specified headers.
|
||||
func Hash(r io.Reader, headers []string) (hash.Hash, error) {
|
||||
// Add deterministic behavior regardless of the order the users specified.
|
||||
sort.Strings(headers)
|
||||
var name string
|
||||
if f, ok := r.(*os.File); ok {
|
||||
name = f.Name()
|
||||
}
|
||||
|
||||
h := sha1.New()
|
||||
msg, err := mail.ReadMessage(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, header := range headers {
|
||||
v := msg.Header.Get(header)
|
||||
if v == "" {
|
||||
glog.V(2).Infoln(name, "Empty", header, "header")
|
||||
}
|
||||
io.WriteString(h, v)
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user