contact/email.go

192 lines
3.9 KiB
Go
Executable File

package contact
import (
"crypto/tls"
"fmt"
"log"
"net/mail"
"net/smtp"
"os"
"strings"
"github.com/bytbox/go-pop3"
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
)
type Emailer struct {
From string
SMTP string
POP3 string
IMAP string
Password string
Limit int
}
func NewEmailer() *Emailer {
envOr := func(key, def string) string {
if v, ok := os.LookupEnv(strings.ToUpper(key)); ok {
return v
}
return def
}
return &Emailer{
From: envOr("FROM", "breellocaldev@gmail.com"),
SMTP: envOr("SMTP", "smtp.gmail.com:465"),
Password: envOr("PASSWORD", "lhnjijrvqaesiufp"),
}
}
func (e *Emailer) Read() (chan *mail.Message, error) {
return e.ReadIMAP()
}
func (e *Emailer) ReadIMAP() (chan *mail.Message, error) {
c, err := client.DialTLS(e.IMAP, nil)
if err != nil {
return nil, err
}
if err := c.Login(e.From, e.Password); err != nil {
return nil, err
}
mbox, err := c.Select("INBOX", true) //readonly
if err != nil {
return nil, err
}
from := uint32(1)
to := mbox.Messages
if e.Limit > 0 && to > uint32(e.Limit) {
from = to - uint32(e.Limit-1)
}
seqset := new(imap.SeqSet)
seqset.AddRange(from, to)
emails := make(chan *mail.Message)
messages := make(chan *imap.Message)
go func() {
defer c.Logout()
section := &imap.BodySectionName{}
if err := c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, section.FetchItem()}, messages); err != nil {
log.Println("error fetching:", err)
}
}()
go func() {
defer close(emails)
for msg := range messages {
for _, v := range msg.Body {
m, err := mail.ReadMessage(v)
if err != nil {
log.Println(err)
} else {
emails <- m
}
}
}
}()
return emails, nil
}
func (e *Emailer) ReadPOP3() (chan *mail.Message, error) {
limit := e.Limit
if limit < 1 {
limit = 1000
}
emails := make(chan *mail.Message, limit)
defer close(emails)
log.Printf("pop3.DialTLS(%s)", e.POP3)
c, err := pop3.DialTLS(e.POP3)
if err != nil {
return nil, err
}
defer c.Rset()
log.Printf("c.Auth(%s, xyz)", e.From)
if err := c.Auth(e.From, e.Password); err != nil {
return nil, err
}
log.Printf("c.ListAll()")
ids, _, err := c.ListAll()
if err != nil {
return nil, err
}
log.Printf("/c.ListAll() = %v", ids)
for i := len(ids) - 1; i >= 0; i-- {
id := ids[i]
log.Printf("c.Retr(%v)", id)
raw, err := c.Retr(id)
if err != nil {
return nil, err
}
msg, err := mail.ReadMessage(strings.NewReader(raw))
if err != nil {
return nil, err
}
select {
case emails <- msg:
default:
return emails, nil
}
}
return emails, nil
}
func (e *Emailer) Send(to, subj, msg string) error {
headers := e.headers(to, e.From, subj)
body := e.body(headers, msg)
smtp, err := e.smtp()
if err != nil {
return err
}
defer smtp.Quit()
if err := smtp.Mail(e.From); err != nil {
return err
}
if err := smtp.Rcpt(to); err != nil {
return err
}
w, err := smtp.Data()
if err != nil {
return err
}
if _, err := w.Write([]byte(body)); err != nil {
return err
}
return w.Close()
}
func (e *Emailer) headers(to, from, subj string) map[string]string {
return map[string]string{
"To": to,
"From": from,
"Subject": subj,
}
}
func (e *Emailer) body(headers map[string]string, body string) string {
b := ""
for k, v := range headers {
b += fmt.Sprintf("%s: %s\r\n", k, v)
}
return b + "\r\n" + body
}
func (e *Emailer) smtp() (*smtp.Client, error) {
host := strings.Split(e.SMTP, ":")[0]
auth := smtp.PlainAuth("", e.From, e.Password, host)
tlsconfig := &tls.Config{ServerName: host}
conn, err := tls.Dial("tcp", e.SMTP, tlsconfig)
if err != nil {
return nil, fmt.Errorf("cannot dial %v: %v", e.SMTP, err)
}
smtp, err := smtp.NewClient(conn, host)
if err != nil {
return nil, fmt.Errorf("cannot new client %v: %v", host, err)
}
if err := smtp.Auth(auth); err != nil {
return nil, fmt.Errorf("cannot auth client: %v", err)
}
return smtp, nil
}