Last active
August 29, 2015 14:05
-
-
Save mark-kubacki/73ee2abe30e13af93a18 to your computer and use it in GitHub Desktop.
how to accept incoming email utilizing services of Mailgun
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func BindHexEncodedField(field *[]byte) func(string, []string, binding.Errors) binding.Errors { | |
return func(fieldName string, formVals []string, errs binding.Errors) binding.Errors { | |
var err error | |
*field, err = hex.DecodeString(formVals[0]) | |
if err != nil { | |
errs.Add([]string{fieldName}, binding.DeserializationError, err.Error()) | |
} | |
return errs | |
} | |
} | |
func BindCustomDateField(field *time.Time, layout string) func(string, []string, binding.Errors) binding.Errors { | |
return func(fieldName string, formVals []string, errs binding.Errors) binding.Errors { | |
var err error | |
*field, err = time.Parse(layout, formVals[0]) | |
if err != nil { | |
errs.Add([]string{fieldName}, binding.DeserializationError, err.Error()) | |
} | |
return errs | |
} | |
} | |
func BindJsonEncodedField(field interface{}) func(string, []string, binding.Errors) binding.Errors { | |
return func(fieldName string, formVals []string, errs binding.Errors) binding.Errors { | |
err := json.Unmarshal([]byte(formVals[0]), &field) | |
if err != nil { | |
errs.Add([]string{fieldName}, binding.DeserializationError, err.Error()) | |
} | |
return errs | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// receiving of emails | |
type MailgunParsedMessage struct { | |
ContentType string | |
Date time.Time | |
From string | |
InReplyTo string | |
MessageId string | |
MimeVersion string | |
Received string | |
References string | |
Sender string | |
Subject string | |
To string | |
UserAgent string | |
XMailgunVariables map[string]string | |
BodyHtml string | |
BodyPlain string | |
ContentIdMap map[string]string | |
EnvelopeFrom string | |
MessageHeaders [][]string | |
Recipient string | |
EnvelopeSender string | |
Signature []byte | |
StrippedHtml string | |
StrippedSignature string | |
StrippedText string | |
Timestamp uint64 | |
Token string | |
AttachmentCount uint8 | |
Attachments map[string][]*multipart.FileHeader | |
} | |
func (f *MailgunParsedMessage) FieldMap() binding.FieldMap { | |
return binding.FieldMap{ | |
&f.ContentType: "Content-Type", | |
&f.From: "From", | |
&f.InReplyTo: "In-Reply-To", | |
&f.MessageId: "Message-Id", | |
&f.MimeVersion: "Mime-Version", | |
&f.Received: "Received", | |
&f.References: "References", | |
&f.Sender: "Sender", | |
&f.Subject: "Subject", | |
&f.To: "To", | |
&f.UserAgent: "User-Agent", | |
&f.BodyHtml: "body-html", | |
&f.BodyPlain: "body-plain", | |
&f.EnvelopeFrom: "from", | |
&f.Recipient: "recipient", | |
&f.EnvelopeSender: "sender", | |
&f.StrippedHtml: "stripped-html", | |
&f.StrippedSignature: "stripped-signature", | |
&f.StrippedText: "stripped-text", | |
&f.Timestamp: "timestamp", | |
&f.Token: "token", | |
&f.AttachmentCount: "attachment-count", | |
&f.Signature: binding.Field{ | |
Form: "signature", | |
Binder: BindHexEncodedField(&f.Signature), | |
}, | |
&f.Date: binding.Field{ | |
Form: "Date", | |
Binder: BindCustomDateField(&f.Date, time.RFC1123Z), | |
}, | |
&f.ContentIdMap: binding.Field{ | |
Form: "content-id-map", | |
Binder: BindJsonEncodedField(&f.ContentIdMap), | |
}, | |
&f.XMailgunVariables: binding.Field{ | |
Form: "X-Mailgun-Variables", | |
Binder: BindJsonEncodedField(&f.XMailgunVariables), | |
}, | |
&f.MessageHeaders: binding.Field{ | |
Form: "message-headers", | |
Binder: BindJsonEncodedField(&f.MessageHeaders), | |
}, | |
} | |
} | |
// see http://documentation.mailgun.com/user_manual.html#securing-webhooks | |
func (f *MailgunParsedMessage) CheckMAC() bool { | |
mac := hmac.New(sha256.New, []byte(mailgunApiKey)) | |
mac.Write([]byte(strconv.FormatUint(f.Timestamp, 10))) | |
mac.Write([]byte(f.Token)) | |
expectedMAC := mac.Sum(nil) | |
return hmac.Equal(f.Signature, expectedMAC) | |
} | |
// Yields an error if the signature doesn't match. | |
func (f MailgunParsedMessage) Validate(req *http.Request, errs binding.Errors) binding.Errors { | |
if !f.CheckMAC() { | |
fmt.Println(mailgunApiKey, string(f.Timestamp), f.Token, f.Signature) | |
errs = append(errs, binding.Error{ | |
FieldNames: []string{"signature", "timestamp", "token", "private-api-key"}, | |
Classification: "HMAC Failure", | |
Message: "The given signature does not match HMAC.", | |
}) | |
} | |
return errs | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func receiveEmail(w http.ResponseWriter, req *http.Request) { | |
fmt.Println("remote address:", req.RemoteAddr) | |
parsedMail := new(MailgunParsedMessage) | |
errs := binding.Bind(req, parsedMail) | |
if errs != nil { | |
errOutput, _ := json.Marshal(errs) | |
fmt.Println(string(errOutput)) | |
return | |
} | |
fmt.Println(parsedMail.From, parsedMail.To, parsedMail.Date.UTC(), parsedMail.Subject) | |
// attachments | |
if req.MultipartForm != nil { | |
parsedMail.Attachments = req.MultipartForm.File | |
} // else we didn't get the input as MimeMultipart | |
// is the sender whitelisted? | |
if not … { | |
w.WriteHeader(406) | |
fmt.Fprint(w, "The sender is not on the whitelist. Try sending from your other email account?") | |
return | |
} | |
// is the recipient known? | |
if not … { | |
w.WriteHeader(406) | |
fmt.Fprint(w, "Sorry, unknown recipient.") | |
return | |
} | |
// do something with the email | |
… | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
:WARNING: Although this example works, its purpose is to illustrate shortcomings of https://github.com/mholt/binding which we want to address.
Follow:
mholt/binding#17Exemplary: You could make
MailgunParsedMessage.Signature
a standalone type—which wouldn't require any additional Binder—like this: