Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
157 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package email | ||
|
||
import ( | ||
"errors" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var emailRegexp *regexp.Regexp | ||
|
||
func NormalizeAddress(address string) string { | ||
return strings.ToLower(strings.TrimSpace(address)) | ||
} | ||
|
||
func ValidateAddress(address string) (normalizedAddress string, err error) { | ||
if emailRegexp == nil { | ||
emailRegexp = regexp.MustCompile("[a-z0-9.\\-_%]+@[a-z0-9.\\-]+\\.[a-z][a-z]+") | ||
} | ||
normalizedAddress = NormalizeAddress(address) | ||
valid := emailRegexp.Match([]byte(normalizedAddress)) | ||
if !valid { | ||
return "", errors.New("Invalid email address: " + address) | ||
} | ||
return normalizedAddress, nil | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package email | ||
|
||
import "net/mail" | ||
|
||
type Configuration struct { | ||
Host string | ||
Port uint16 | ||
Username string | ||
Password string | ||
DefaultFrom mail.Address | ||
} | ||
|
||
var Config Configuration | ||
|
||
func InitGmail(email, password string) error { | ||
return InitGmailFrom(email, email, password) | ||
} | ||
|
||
// InitGmailFrom uses a different fromAddress for sending emails | ||
// than the loginAddress used together with the password for authentication | ||
// The fromAddress has to be verified as a valid sender address in Gmail. | ||
func InitGmailFrom(fromAddress, loginAddress, password string) error { | ||
if _, err := ValidateAddress(fromAddress); err != nil { | ||
return err | ||
} | ||
if _, err := ValidateAddress(loginAddress); err != nil { | ||
return err | ||
} | ||
Config = Configuration{ | ||
Host: "smtp.gmail.com", | ||
Port: 587, | ||
Username: loginAddress, | ||
Password: password, | ||
DefaultFrom: mail.Address{"", fromAddress}, | ||
} | ||
return nil | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Utilities to make sending emails easier. | ||
package email |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package email | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"net/mail" | ||
"net/smtp" | ||
"time" | ||
) | ||
|
||
func NewBriefMessage(subject, content string, to ...string) *Message { | ||
message := &Message{Subject: subject, Content: content, To: make([]mail.Address, len(to))} | ||
for i := range to { | ||
message.To[i].Address = to[i] | ||
} | ||
return message | ||
} | ||
|
||
func NewBriefMessageFrom(subject, content, from string, to ...string) *Message { | ||
message := NewBriefMessage(subject, content, to...) | ||
message.From.Address = from | ||
return message | ||
} | ||
|
||
const crlf = "\r\n" | ||
|
||
type Message struct { | ||
From mail.Address // if From.Address is empty, Config.DefaultFrom will be used | ||
To []mail.Address | ||
Cc []mail.Address | ||
Bcc []mail.Address | ||
Subject string | ||
Content string | ||
} | ||
|
||
// http://tools.ietf.org/html/rfc822 | ||
// http://tools.ietf.org/html/rfc2821 | ||
func (self *Message) String() string { | ||
var buf bytes.Buffer | ||
|
||
write := func(what string, recipients []mail.Address) { | ||
if len(recipients) == 0 { | ||
return | ||
} | ||
for i := range recipients { | ||
if i == 0 { | ||
buf.WriteString(what) | ||
} else { | ||
buf.WriteString(", ") | ||
} | ||
buf.WriteString(recipients[i].String()) | ||
} | ||
buf.WriteString(crlf) | ||
} | ||
|
||
from := &self.From | ||
if from.Address == "" { | ||
from = &Config.DefaultFrom | ||
} | ||
fmt.Fprintf(&buf, "From: %s%s", from.String(), crlf) | ||
write("To: ", self.To) | ||
write("Cc: ", self.Cc) | ||
write("Bcc: ", self.Bcc) | ||
fmt.Fprintf(&buf, "Date: %s%s", time.Now().UTC().Format(time.RFC822), crlf) | ||
fmt.Fprintf(&buf, "Subject: %s%s%s", self.Subject, crlf, self.Content) | ||
return buf.String() | ||
} | ||
|
||
// Returns the first error | ||
func (self *Message) Validate() error { | ||
if len(self.To) == 0 { | ||
return errors.New("Missing email recipient (email.Message.To)") | ||
} | ||
return nil | ||
} | ||
|
||
func (self *Message) Send() error { | ||
if err := self.Validate(); err != nil { | ||
return err | ||
} | ||
to := make([]string, len(self.To)) | ||
for i := range self.To { | ||
to[i] = self.To[i].Address | ||
} | ||
from := self.From.Address | ||
if from == "" { | ||
from = Config.DefaultFrom.Address | ||
} | ||
addr := fmt.Sprintf("%s:%d", Config.Host, Config.Port) | ||
auth := smtp.PlainAuth("", Config.Username, Config.Password, Config.Host) | ||
return smtp.SendMail(addr, auth, from, to, []byte(self.String())) | ||
} |