2021-01-26 07:16:53 +01:00
|
|
|
package mstore
|
|
|
|
|
|
|
|
import (
|
2021-01-27 10:07:31 +01:00
|
|
|
"fmt"
|
2021-01-29 12:29:23 +01:00
|
|
|
"io"
|
2021-01-29 17:22:07 +01:00
|
|
|
"io/ioutil"
|
2021-01-29 12:29:23 +01:00
|
|
|
"strings"
|
2021-01-27 15:01:42 +01:00
|
|
|
"time"
|
2021-01-27 10:07:31 +01:00
|
|
|
|
|
|
|
"github.com/emersion/go-imap"
|
2021-01-26 07:16:53 +01:00
|
|
|
"github.com/emersion/go-imap/client"
|
2021-01-29 17:22:07 +01:00
|
|
|
"github.com/emersion/go-message/mail"
|
2021-01-26 07:16:53 +01:00
|
|
|
)
|
|
|
|
|
2021-01-29 12:29:23 +01:00
|
|
|
type Body struct {
|
|
|
|
reader io.Reader
|
|
|
|
length int
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewBody(msg string) *Body {
|
|
|
|
|
|
|
|
return &Body{
|
|
|
|
reader: strings.NewReader(msg),
|
|
|
|
length: len([]byte(msg)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Body) Read(p []byte) (int, error) {
|
|
|
|
return b.reader.Read(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Body) Len() int {
|
|
|
|
return b.length
|
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
type ImapConfiguration struct {
|
|
|
|
ImapUrl string
|
|
|
|
ImapUsername string
|
|
|
|
ImapPassword string
|
2021-01-26 07:16:53 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (esc *ImapConfiguration) Valid() bool {
|
|
|
|
if esc.ImapUrl == "" {
|
2021-01-27 10:07:31 +01:00
|
|
|
return false
|
|
|
|
}
|
2021-01-29 17:22:07 +01:00
|
|
|
if esc.ImapUsername == "" || esc.ImapPassword == "" {
|
2021-01-27 10:07:31 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
type Imap struct {
|
2021-01-28 09:40:08 +01:00
|
|
|
imap *client.Client
|
|
|
|
mboxStatus *imap.MailboxStatus
|
2021-01-27 12:58:43 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func ImapConnect(conf *ImapConfiguration) (*Imap, error) {
|
|
|
|
imap, err := client.DialTLS(conf.ImapUrl, nil)
|
2021-01-27 10:07:31 +01:00
|
|
|
if err != nil {
|
2021-01-29 17:22:07 +01:00
|
|
|
return &Imap{}, err
|
2021-01-27 10:07:31 +01:00
|
|
|
}
|
2021-01-29 17:22:07 +01:00
|
|
|
if err := imap.Login(conf.ImapUsername, conf.ImapPassword); err != nil {
|
|
|
|
return &Imap{}, err
|
2021-01-27 10:07:31 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
return &Imap{
|
2021-01-27 12:58:43 +01:00
|
|
|
imap: imap,
|
2021-01-27 10:07:31 +01:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (es *Imap) Disconnect() {
|
2021-01-27 12:58:43 +01:00
|
|
|
es.imap.Logout()
|
2021-01-27 10:07:31 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (es *Imap) Folders() ([]string, error) {
|
2021-01-27 10:07:31 +01:00
|
|
|
boxes, done := make(chan *imap.MailboxInfo), make(chan error)
|
|
|
|
go func() {
|
2021-01-27 12:58:43 +01:00
|
|
|
done <- es.imap.List("", "*", boxes)
|
2021-01-27 10:07:31 +01:00
|
|
|
}()
|
|
|
|
|
2021-01-28 09:40:08 +01:00
|
|
|
folders := []string{}
|
2021-01-27 10:07:31 +01:00
|
|
|
for b := range boxes {
|
2021-01-28 09:40:08 +01:00
|
|
|
folders = append(folders, b.Name)
|
2021-01-27 10:07:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := <-done; err != nil {
|
2021-01-28 09:40:08 +01:00
|
|
|
return []string{}, err
|
2021-01-27 10:07:31 +01:00
|
|
|
}
|
2021-01-26 07:16:53 +01:00
|
|
|
|
2021-01-27 10:07:31 +01:00
|
|
|
return folders, nil
|
2021-01-26 07:16:53 +01:00
|
|
|
}
|
2021-01-27 12:58:43 +01:00
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (es *Imap) selectFolder(folder string) error {
|
2021-01-28 09:40:08 +01:00
|
|
|
status, err := es.imap.Select(folder, false)
|
2021-01-27 12:58:43 +01:00
|
|
|
if err != nil {
|
2021-01-28 09:40:08 +01:00
|
|
|
return err
|
2021-01-27 12:58:43 +01:00
|
|
|
}
|
|
|
|
|
2021-01-28 09:40:08 +01:00
|
|
|
es.mboxStatus = status
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (es *Imap) Messages(folder string) ([]*Message, error) {
|
2021-01-29 12:29:23 +01:00
|
|
|
if err := es.selectFolder(folder); err != nil {
|
|
|
|
return []*Message{}, err
|
2021-01-28 09:40:08 +01:00
|
|
|
}
|
2021-01-27 12:58:43 +01:00
|
|
|
|
2021-01-28 14:43:00 +01:00
|
|
|
if es.mboxStatus.Messages == 0 {
|
|
|
|
return []*Message{}, nil
|
|
|
|
}
|
|
|
|
|
2021-01-27 12:58:43 +01:00
|
|
|
seqset := new(imap.SeqSet)
|
2021-01-28 09:40:08 +01:00
|
|
|
seqset.AddRange(uint32(1), es.mboxStatus.Messages)
|
2021-01-27 12:58:43 +01:00
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
// Get the whole message body
|
|
|
|
section := &imap.BodySectionName{}
|
|
|
|
items := []imap.FetchItem{imap.FetchUid, section.FetchItem()}
|
|
|
|
|
2021-01-27 12:58:43 +01:00
|
|
|
imsg, done := make(chan *imap.Message), make(chan error)
|
|
|
|
go func() {
|
2021-01-29 17:22:07 +01:00
|
|
|
done <- es.imap.Fetch(seqset, items, imsg)
|
2021-01-27 12:58:43 +01:00
|
|
|
}()
|
|
|
|
|
|
|
|
messages := []*Message{}
|
|
|
|
for m := range imsg {
|
2021-01-29 17:22:07 +01:00
|
|
|
r := m.GetBody(section)
|
|
|
|
if r == nil {
|
|
|
|
return []*Message{}, fmt.Errorf("server didn't returned message body")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new mail reader
|
|
|
|
mr, err := mail.CreateReader(r)
|
|
|
|
if err != nil {
|
|
|
|
return []*Message{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print some info about the message
|
|
|
|
header := mr.Header
|
|
|
|
subject, err := header.Subject()
|
|
|
|
if err != nil {
|
|
|
|
return []*Message{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process each message's part
|
|
|
|
body := []byte(``)
|
|
|
|
for {
|
|
|
|
p, err := mr.NextPart()
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
} else if err != nil {
|
|
|
|
return []*Message{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch p.Header.(type) {
|
|
|
|
case *mail.InlineHeader:
|
|
|
|
// This is the message's text (can be plain-text or HTML)
|
|
|
|
body, _ = ioutil.ReadAll(p.Body)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 12:58:43 +01:00
|
|
|
messages = append(messages, &Message{
|
2021-01-28 14:43:00 +01:00
|
|
|
Uid: m.Uid,
|
2021-01-29 12:29:23 +01:00
|
|
|
Folder: folder,
|
2021-01-29 17:22:07 +01:00
|
|
|
Subject: subject,
|
|
|
|
Body: string(body),
|
2021-01-27 12:58:43 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := <-done; err != nil {
|
|
|
|
return []*Message{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return messages, nil
|
|
|
|
}
|
2021-01-27 15:01:42 +01:00
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (es *Imap) Add(folder, subject, body string) error {
|
2021-01-31 13:31:35 +01:00
|
|
|
msgStr := fmt.Sprintf(`From: todo <mstore@erikwinter.nl>
|
|
|
|
Date: %s
|
2021-01-29 12:29:23 +01:00
|
|
|
Subject: %s
|
|
|
|
|
2021-02-01 14:28:46 +01:00
|
|
|
%s`, time.Now().Format(time.RFC822Z), subject, body)
|
2021-01-29 12:29:23 +01:00
|
|
|
|
|
|
|
msg := NewBody(msgStr)
|
|
|
|
|
|
|
|
return es.imap.Append(folder, nil, time.Time{}, imap.Literal(msg))
|
2021-01-27 15:01:42 +01:00
|
|
|
}
|
2021-01-28 09:40:08 +01:00
|
|
|
|
2021-01-29 17:22:07 +01:00
|
|
|
func (es *Imap) Remove(msg *Message) error {
|
2021-01-30 11:20:12 +01:00
|
|
|
if msg == nil || !msg.Valid() {
|
2021-01-29 12:29:23 +01:00
|
|
|
return ErrInvalidMessage
|
2021-01-28 14:43:00 +01:00
|
|
|
}
|
2021-01-29 12:29:23 +01:00
|
|
|
|
|
|
|
if err := es.selectFolder(msg.Folder); err != nil {
|
|
|
|
return err
|
2021-01-28 09:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// set deleted flag
|
|
|
|
seqset := new(imap.SeqSet)
|
2021-01-29 12:29:23 +01:00
|
|
|
seqset.AddRange(msg.Uid, msg.Uid)
|
2021-01-28 09:40:08 +01:00
|
|
|
storeItem := imap.FormatFlagsOp(imap.SetFlags, true)
|
|
|
|
err := es.imap.UidStore(seqset, storeItem, imap.FormatStringList([]string{imap.DeletedFlag}), nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// expunge box
|
|
|
|
if err := es.imap.Expunge(nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|