64 lines
1.4 KiB
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
|
|
}
|