email/hash.go

64 lines
1.4 KiB
Go

package email
import (
"crypto/sha1"
"fmt"
"hash"
"io"
"net/http"
"net/mail"
"sort"
)
// Hasher is a list of headers that should be considered when hashing an email
// message.
type Hasher []string
// Hash will parse r as an email, and return a hash.Hash that has been applied
// to the values of the headers in h.
func (hsr Hasher) HashMessage(msg *mail.Message) (hash.Hash, error) {
// Add deterministic behavior regardless of the order the users specified.
if !sort.IsSorted(sort.StringSlice(hsr)) {
sort.Strings(hsr)
}
hsh := sha1.New()
for _, h := range hsr {
h := http.CanonicalHeaderKey(h)
vs, ok := msg.Header[h]
if ok {
for _, v := range vs {
io.WriteString(hsh, v)
}
}
}
return hsh, nil
}
func (h Hasher) HashReader(r io.Reader) (hash.Hash, error) {
msg, err := mail.ReadMessage(r)
if err != nil {
return nil, err
}
return h.HashMessage(msg)
}
var std = Hasher([]string{"to", "from", "cc", "date", "subject", "message-id", "received"})
// Hash will parse r as an email, and return the hash as a hexadecimal string
// using a default set of headers.
func HashReader(r io.Reader) (string, error) {
h, err := std.HashReader(r)
if err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
func HashMessage(msg *mail.Message) (string, error) {
h, err := std.HashMessage(msg)
if err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}