236 lines
6.7 KiB
Go
236 lines
6.7 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/tls"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"html/template"
|
||
|
"log"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"net/smtp"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/spf13/viper"
|
||
|
)
|
||
|
|
||
|
//Request struct
|
||
|
type SendMailRequest struct {
|
||
|
from string
|
||
|
to []string
|
||
|
subject string
|
||
|
body string
|
||
|
}
|
||
|
|
||
|
// NewSendMailRequest creates a new instance to manage send mail
|
||
|
func NewSendMailRequest(from string, to []string, subject string) *SendMailRequest {
|
||
|
return &SendMailRequest{
|
||
|
from: from,
|
||
|
to: to,
|
||
|
subject: subject,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Execute processes the actual email sending
|
||
|
func (m *SendMailRequest) Execute() error {
|
||
|
|
||
|
mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
|
||
|
from := "From: " + m.from + "\n"
|
||
|
subject := "Subject: " + m.subject + "\n"
|
||
|
msg := []byte(from + subject + mime + "\n" + m.body)
|
||
|
|
||
|
// Connect to the SMTP Server
|
||
|
smtpServerAddr := viper.GetString("SMTP_SERVER_ADDR")
|
||
|
smtpServerHost, _, _ := net.SplitHostPort(smtpServerAddr)
|
||
|
smtpClientAuth := smtp.PlainAuth("",
|
||
|
viper.GetString("SMTP_CLIENT_USERNAME"),
|
||
|
viper.GetString("SMTP_CLIENT_PASSWORD"),
|
||
|
smtpServerHost)
|
||
|
|
||
|
// TLS config
|
||
|
tlsconfig := &tls.Config{
|
||
|
InsecureSkipVerify: true,
|
||
|
ServerName: smtpServerHost,
|
||
|
}
|
||
|
|
||
|
// Important: call tls.Dial instead of smtp.Dial for smtp servers running on 465.
|
||
|
// On port 465 ssl connection is required from the very beginning (no starttls)
|
||
|
conn, err := tls.Dial("tcp", smtpServerAddr, tlsconfig)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed initiating smtp connection (%s)", err)
|
||
|
}
|
||
|
|
||
|
smtpClient, err := smtp.NewClient(conn, smtpServerHost)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed creating the smtp client (%s)", err)
|
||
|
}
|
||
|
defer smtpClient.Quit()
|
||
|
|
||
|
if err = smtpClient.Auth(smtpClientAuth); err != nil {
|
||
|
return fmt.Errorf("failed authenticating to smtp server (%s)", err)
|
||
|
}
|
||
|
|
||
|
// Initialize a mail transaction
|
||
|
err = smtpClient.Mail(m.from)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed issuing MAIL command (%s)", err)
|
||
|
}
|
||
|
|
||
|
// Set recipents
|
||
|
for _, recipient := range m.to {
|
||
|
err = smtpClient.Rcpt(recipient)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed issuing RCPT command (%s)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
smtpWriter, err := smtpClient.Data()
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed issuing DATA command (%s)", err)
|
||
|
}
|
||
|
|
||
|
_, err = smtpWriter.Write([]byte(msg))
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed sending mail content (%s)", err)
|
||
|
}
|
||
|
|
||
|
err = smtpWriter.Close()
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed close smtp client (%s)", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ParseTemplate parses template and bing data and process the email sending
|
||
|
func (m *SendMailRequest) ParseTemplate(templateFileName string, data interface{}) error {
|
||
|
emailTpl, err := template.ParseFiles(templateFileName)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
buf := new(bytes.Buffer)
|
||
|
err = emailTpl.Execute(buf, data)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
m.body = buf.String()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SendMail handles HTTP request to send email
|
||
|
func SendMail(httpResp http.ResponseWriter, httpReq *http.Request) {
|
||
|
|
||
|
AllowedOriginDomain := viper.GetString("ALLOWED_ORIGIN_DOMAIN")
|
||
|
AllowedOrigins := map[string]bool{
|
||
|
fmt.Sprintf("http://%s", AllowedOriginDomain): true,
|
||
|
fmt.Sprintf("https://%s", AllowedOriginDomain): true,
|
||
|
fmt.Sprintf("http://www.%s", AllowedOriginDomain): true,
|
||
|
fmt.Sprintf("https://www.%s", AllowedOriginDomain): true,
|
||
|
}
|
||
|
|
||
|
if len(httpReq.Header["Origin"]) == 0 || len(httpReq.Header["Referer"]) == 0 {
|
||
|
rawHeader, _ := json.Marshal(httpReq.Header)
|
||
|
log.Println("request with unexpected headers:", string(rawHeader))
|
||
|
httpResp.WriteHeader(http.StatusForbidden)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
reqOrigin := httpReq.Header["Origin"][0]
|
||
|
if _, domainFound := AllowedOrigins[reqOrigin]; !domainFound {
|
||
|
log.Println("Not allowed origin:", reqOrigin)
|
||
|
httpResp.WriteHeader(http.StatusForbidden)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
httpReq.ParseForm()
|
||
|
|
||
|
contactRequest := ContactRequest{
|
||
|
Name: httpReq.FormValue("name"),
|
||
|
Email: strings.TrimSpace(httpReq.FormValue("email")),
|
||
|
Organization: httpReq.FormValue("organization"),
|
||
|
Subject: httpReq.FormValue("subject"),
|
||
|
Message: httpReq.FormValue("message"),
|
||
|
RequestTarget: httpReq.FormValue("target"),
|
||
|
}
|
||
|
|
||
|
var recipients []string
|
||
|
switch contactRequest.RequestTarget {
|
||
|
case "demo":
|
||
|
recipients = []string{contactRequest.Email, viper.GetString("CONTACT_REPLY_CC_EMAIL")}
|
||
|
case "contact":
|
||
|
recipients = []string{viper.GetString("CONTACT_REPLY_CC_EMAIL")}
|
||
|
default:
|
||
|
log.Println("Not allowed request type:", contactRequest.RequestTarget)
|
||
|
httpResp.WriteHeader(http.StatusForbidden)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
userData, _ := json.Marshal(contactRequest)
|
||
|
log.Println("New Request:", string(userData))
|
||
|
|
||
|
templateData := struct {
|
||
|
Name string
|
||
|
Email string
|
||
|
Organization string
|
||
|
Subject string
|
||
|
Message string
|
||
|
DemoURL string
|
||
|
}{
|
||
|
Name: contactRequest.Name,
|
||
|
Email: contactRequest.Email,
|
||
|
Organization: contactRequest.Organization,
|
||
|
Subject: contactRequest.Subject,
|
||
|
Message: contactRequest.Message,
|
||
|
DemoURL: viper.GetString("DEMO_URL"),
|
||
|
}
|
||
|
contactResponse := ContactResponse{}
|
||
|
contactEmail := viper.GetString("CONTACT_REPLY_EMAIL")
|
||
|
|
||
|
sendMailReq := NewSendMailRequest(
|
||
|
contactEmail,
|
||
|
recipients,
|
||
|
contactRequest.Subject,
|
||
|
)
|
||
|
|
||
|
err := error(nil)
|
||
|
if contactRequest.RequestTarget == "demo" {
|
||
|
err = sendMailReq.ParseTemplate(viper.GetString("TEMPLATE_DEMO_REQUEST_REPLY"), templateData)
|
||
|
} else {
|
||
|
err = sendMailReq.ParseTemplate(viper.GetString("TEMPLATE_CONTACT_REQUEST_REPLY"), templateData)
|
||
|
}
|
||
|
|
||
|
if err == nil {
|
||
|
err := sendMailReq.Execute()
|
||
|
if err != nil {
|
||
|
log.Printf("error: %s", err.Error())
|
||
|
contactResponse.Status = "error"
|
||
|
contactResponse.Message = fmt.Sprintf("An internal error occurred, please try later or send us an email at %s.", viper.GetString("CONTACT_REPLY_CC_EMAIL"))
|
||
|
} else {
|
||
|
contactResponse.Status = "success"
|
||
|
if contactRequest.RequestTarget == "demo" {
|
||
|
contactResponse.Message = "Thank you, if you supplied a correct email address then an email should have been sent to you."
|
||
|
} else {
|
||
|
contactResponse.Message = "Thank you, if you supplied a correct email address then we'll process your request within the next 48 hours."
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
log.Printf("error: %s", err.Error())
|
||
|
contactResponse.Status = "error"
|
||
|
contactResponse.Message = "Invalid request, please review your input and try again."
|
||
|
}
|
||
|
|
||
|
refererURL := strings.Split(httpReq.Header["Referer"][0], "?")[0]
|
||
|
respRawData, _ := json.Marshal(contactResponse)
|
||
|
|
||
|
httpResp.Header().Set("Location",
|
||
|
fmt.Sprintf("%s?status=%s&message=%s",
|
||
|
refererURL,
|
||
|
contactResponse.Status,
|
||
|
contactResponse.Message))
|
||
|
httpResp.WriteHeader(http.StatusSeeOther)
|
||
|
httpResp.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||
|
httpResp.Write(respRawData)
|
||
|
}
|