package email import ( "crypto/sha1" "fmt" "hash" "io" "net/mail" "sort" "github.com/golang/glog" ) // 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 (h Hasher) HashMessage(msg *mail.Message) (hash.Hash, error) { // Add deterministic behavior regardless of the order the users specified. if !sort.IsSorted(sort.StringSlice(h)) { sort.Strings(h) } hsh := sha1.New() for _, header := range h { v := msg.Header.Get(header) if v == "" { glog.V(2).Infoln("Empty", header, "header") } 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"}) // 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 }