package main import ( "bytes" "fmt" "github.com/Masterminds/sprig" "gopkg.in/gomail.v2" "text/template" ) type NotificationMail interface { GetData() interface{} GetTemplate() string GetSubject() string GetHeaders() map[string]string GetRecipient() (string, string) } var NotifyMailChannel = make(chan NotificationMail, 1) func MailNotifier(quitMailNotifier chan int) { logger.Println("Launched mail notifier") for { select { case notification := <-NotifyMailChannel: mailText, err := buildMail(notification.GetTemplate(), notification.GetData()) if err != nil { logger.Println("ERROR building mail:", err) continue } m := gomail.NewMessage() m.SetHeader("From", config.NotificationSenderAddress) address, name := notification.GetRecipient() m.SetAddressHeader("To", address, name) m.SetHeader("Subject", notification.GetSubject()) for header, value := range notification.GetHeaders() { m.SetHeader(header, value) } m.SetBody("text/plain", mailText.String()) d := gomail.NewDialer(config.MailServer.Host, config.MailServer.Port, "", "") if err := d.DialAndSend(m); err != nil { logger.Println("ERROR sending mail:", err) } case <-quitMailNotifier: fmt.Println("Ending mail notifier") return } } } func buildMail(templateName string, context interface{}) (mailText *bytes.Buffer, err error) { t, err := template.New(templateName).Funcs( sprig.GenericFuncMap()).ParseFiles(fmt.Sprintf("templates/%s", templateName)) if err != nil { return } mailText = bytes.NewBufferString("") t.Execute(mailText, context) return } type notificationBase struct{} func (n *notificationBase) GetRecipient() (address string, name string) { address, name = config.BoardMailAddress, "CAcert board mailing list" return } type decisionReplyBase struct { decision Decision } func (n *decisionReplyBase) GetHeaders() map[string]string { return map[string]string{ "References": fmt.Sprintf("<%s>", n.decision.Tag), "In-Reply-To": fmt.Sprintf("<%s>", n.decision.Tag), } } type NotificationClosedDecision struct { notificationBase decisionReplyBase voteSums VoteSums } func NewNotificationClosedDecision(decision *Decision, voteSums *VoteSums) *NotificationClosedDecision { notification := &NotificationClosedDecision{voteSums: *voteSums} notification.decision = *decision return notification } func (n *NotificationClosedDecision) GetData() interface{} { return struct { *Decision *VoteSums }{&n.decision, &n.voteSums} } func (n *NotificationClosedDecision) GetTemplate() string { return "closed_motion_mail.txt" } func (n *NotificationClosedDecision) GetSubject() string { return fmt.Sprintf("Re: %s - %s - finalised", n.decision.Tag, n.decision.Title) } type NotificationCreateMotion struct { notificationBase decision Decision voter Voter } func (n *NotificationCreateMotion) GetData() interface{} { voteURL := fmt.Sprintf("%s/vote", config.BaseURL) unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL) return struct { *Decision Name string VoteURL string UnvotedURL string }{&n.decision, n.voter.Name, voteURL, unvotedURL} } func (n *NotificationCreateMotion) GetTemplate() string { return "create_motion_mail.txt" } func (n *NotificationCreateMotion) GetSubject() string { return fmt.Sprintf("%s - %s", n.decision.Tag, n.decision.Title) } func (n *NotificationCreateMotion) GetHeaders() map[string]string { return map[string]string{"Message-ID": fmt.Sprintf("<%s>", n.decision.Tag)} } type NotificationUpdateMotion struct { notificationBase decisionReplyBase voter Voter } func NewNotificationUpdateMotion(decision Decision, voter Voter) *NotificationUpdateMotion { notification := NotificationUpdateMotion{voter: voter} notification.decision = decision return ¬ification } func (n *NotificationUpdateMotion) GetData() interface{} { voteURL := fmt.Sprintf("%s/vote", config.BaseURL) unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL) return struct { *Decision Name string VoteURL string UnvotedURL string }{&n.decision, n.voter.Name, voteURL, unvotedURL} } func (n *NotificationUpdateMotion) GetTemplate() string { return "update_motion_mail.txt" } func (n *NotificationUpdateMotion) GetSubject() string { return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title) } type NotificationWithDrawMotion struct { notificationBase decisionReplyBase voter Voter } func NewNotificationWithDrawMotion(decision *Decision, voter *Voter) *NotificationWithDrawMotion { notification := &NotificationWithDrawMotion{voter: *voter} notification.decision = *decision return notification } func (n *NotificationWithDrawMotion) GetData() interface{} { return struct { *Decision Name string }{&n.decision, n.voter.Name} } func (n *NotificationWithDrawMotion) GetTemplate() string { return "withdraw_motion_mail.txt" } func (n *NotificationWithDrawMotion) GetSubject() string { return fmt.Sprintf("Re: %s - %s - withdrawn", n.decision.Tag, n.decision.Title) } type RemindVoterNotification struct { voter Voter decisions []Decision } func (n *RemindVoterNotification) GetData() interface{} { return struct { Decisions []Decision Name string BaseURL string }{n.decisions, n.voter.Name, config.BaseURL} } func (n *RemindVoterNotification) GetTemplate() string { return "remind_voter_mail.txt" } func (n *RemindVoterNotification) GetSubject() string { return "Outstanding CAcert board votes" } func (n *RemindVoterNotification) GetHeaders() map[string]string { return map[string]string{} } func (n *RemindVoterNotification) GetRecipient() (address string, name string) { address, name = n.voter.Reminder, n.voter.Name return } type voteNotificationBase struct{} func (n *voteNotificationBase) GetRecipient() (address string, name string) { address, name = config.VoteNoticeAddress, "CAcert board votes mailing list" return } type NotificationProxyVote struct { voteNotificationBase decisionReplyBase proxy Voter voter Voter vote Vote justification string } func NewNotificationProxyVote(decision *Decision, proxy *Voter, voter *Voter, vote *Vote, justification string) *NotificationProxyVote { notification := &NotificationProxyVote{proxy: *proxy, voter: *voter, vote: *vote, justification: justification} notification.decision = *decision return notification } func (n *NotificationProxyVote) GetData() interface{} { return struct { Proxy string Vote VoteChoice Voter string Decision *Decision Justification string }{n.proxy.Name, n.vote.Vote, n.voter.Name, &n.decision, n.justification} } func (n *NotificationProxyVote) GetTemplate() string { return "proxy_vote_mail.txt" } func (n *NotificationProxyVote) GetSubject() string { return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title) }