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 }