~gitpocket/gitpocket

gitpocket/mail.go -rw-r--r--2.2 KiB ViewLogBlameView rawPermalink a3d84a990a — feat(basin): mail, password reset, totp, issues, stars, rename, transfer, archive, blame, compare, downloads, feeds, admin commands 13 hours ago
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net"
	"net/smtp"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type Mailer struct {
	Host     string `json:"host"`
	Port     int    `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
	From     string `json:"from"`
	StartTLS bool   `json:"starttls"`
	Insecure bool   `json:"insecure"`
}

func loadMailer(root string) *Mailer {
	b, err := os.ReadFile(filepath.Join(root, "smtp.json"))
	if err != nil {
		return nil
	}
	var m Mailer
	if json.Unmarshal(b, &m) != nil || m.Host == "" || m.From == "" {
		return nil
	}
	if m.Port == 0 {
		m.Port = 465
	}
	return &m
}

func (m *Mailer) Send(to, subject, body string) error {
	if m == nil {
		return errors.New("mail is not configured")
	}
	addr := net.JoinHostPort(m.Host, fmt.Sprint(m.Port))
	tlsConfig := &tls.Config{ServerName: m.Host, InsecureSkipVerify: m.Insecure}
	var client *smtp.Client
	var err error
	if m.StartTLS {
		client, err = smtp.Dial(addr)
		if err == nil {
			err = client.StartTLS(tlsConfig)
		}
	} else if m.Insecure && m.Port != 465 {
		client, err = smtp.Dial(addr)
	} else {
		var conn net.Conn
		conn, err = tls.DialWithDialer(&net.Dialer{Timeout: 15 * time.Second}, "tcp", addr, tlsConfig)
		if err == nil {
			client, err = smtp.NewClient(conn, m.Host)
		}
	}
	if err != nil {
		return err
	}
	defer client.Close()
	if m.User != "" {
		if err := client.Auth(smtp.PlainAuth("", m.User, m.Password, m.Host)); err != nil {
			return err
		}
	}
	if err := client.Mail(m.From); err != nil {
		return err
	}
	if err := client.Rcpt(to); err != nil {
		return err
	}
	w, err := client.Data()
	if err != nil {
		return err
	}
	msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n%s\r\n",
		m.From, to, strings.ReplaceAll(subject, "\n", " "), time.Now().Format(time.RFC1123Z), body)
	if _, err := w.Write([]byte(msg)); err != nil {
		return err
	}
	if err := w.Close(); err != nil {
		return err
	}
	return client.Quit()
}

func (s *Server) notify(to, subject, body string) {
	if to == "" || s.mailer == nil {
		return
	}
	go func() {
		if err := s.mailer.Send(to, subject, body); err != nil {
			log.Printf("mail to %s: %v", to, err)
		}
	}()
}