2023-08-13 03:04:16 +01:00
|
|
|
package imap
|
|
|
|
|
|
|
|
import (
|
2023-11-25 12:22:58 +00:00
|
|
|
"encoding/json"
|
2023-09-11 22:42:51 +01:00
|
|
|
"errors"
|
2023-11-19 23:36:45 +00:00
|
|
|
"github.com/1f349/lotus/imap/marshal"
|
2023-08-13 03:04:16 +01:00
|
|
|
"github.com/emersion/go-imap"
|
|
|
|
"github.com/emersion/go-imap/client"
|
|
|
|
)
|
|
|
|
|
|
|
|
var imapStatusFlags = []imap.StatusItem{
|
|
|
|
imap.StatusMessages,
|
|
|
|
imap.StatusRecent,
|
|
|
|
imap.StatusUidNext,
|
|
|
|
imap.StatusUidValidity,
|
|
|
|
imap.StatusUnseen,
|
|
|
|
}
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
ic *client.Client
|
|
|
|
}
|
|
|
|
|
2023-09-11 22:42:51 +01:00
|
|
|
var ErrInvalidArguments = errors.New("invalid arguments")
|
|
|
|
|
2023-11-25 12:22:58 +00:00
|
|
|
func (c *Client) HandleWS(action string, args json.RawMessage) (map[string]any, error) {
|
2023-09-11 17:23:44 +01:00
|
|
|
switch action {
|
|
|
|
case "copy":
|
|
|
|
// TODO: implementation
|
|
|
|
case "create":
|
|
|
|
// TODO: implementation
|
|
|
|
case "delete":
|
|
|
|
// TODO: implementation
|
|
|
|
case "list":
|
2023-11-25 12:22:58 +00:00
|
|
|
var listArgs []string
|
|
|
|
err := json.Unmarshal(args, &listArgs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(listArgs) != 2 {
|
2023-09-11 22:42:51 +01:00
|
|
|
return nil, ErrInvalidArguments
|
|
|
|
}
|
|
|
|
|
|
|
|
// do list
|
2023-11-25 12:22:58 +00:00
|
|
|
list, err := c.list(listArgs[0], listArgs[1])
|
2023-09-11 22:42:51 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return map[string]any{"type": "list", "value": list}, nil
|
|
|
|
case "fetch":
|
2023-11-25 12:22:58 +00:00
|
|
|
var fetchArgs struct {
|
|
|
|
Sync uint64 `json:"sync"`
|
|
|
|
Path string `json:"path"`
|
|
|
|
Start uint32 `json:"start"`
|
|
|
|
End uint32 `json:"end"`
|
|
|
|
Limit uint32 `json:"limit"`
|
2023-09-11 22:42:51 +01:00
|
|
|
}
|
2023-11-25 12:22:58 +00:00
|
|
|
err := json.Unmarshal(args, &fetchArgs)
|
2023-09-11 22:42:51 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-25 12:22:58 +00:00
|
|
|
|
|
|
|
if fetchArgs.Sync == 0 || len(fetchArgs.Path) == 0 || fetchArgs.Start == 0 || fetchArgs.End == 0 || fetchArgs.Limit == 0 {
|
|
|
|
return nil, ErrInvalidArguments
|
2023-09-11 22:42:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// do fetch
|
2023-11-25 12:22:58 +00:00
|
|
|
fetch, err := c.fetch(fetchArgs.Path, fetchArgs.Start, fetchArgs.End, fetchArgs.Limit)
|
2023-09-11 22:42:51 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-25 12:22:58 +00:00
|
|
|
return map[string]any{"type": "fetch", "sync": 0, "value": marshal.MessageSliceJson(fetch)}, nil
|
2023-09-11 17:23:44 +01:00
|
|
|
case "move":
|
|
|
|
// TODO: implementation
|
|
|
|
case "rename":
|
|
|
|
// TODO: implementation
|
|
|
|
case "search":
|
|
|
|
// TODO: implementation
|
|
|
|
case "status":
|
|
|
|
// TODO: implementation
|
|
|
|
}
|
2023-09-11 22:42:51 +01:00
|
|
|
return map[string]any{"error": "Not implemented"}, nil
|
2023-08-13 03:04:16 +01:00
|
|
|
}
|
|
|
|
|
2023-09-11 22:42:51 +01:00
|
|
|
func (c *Client) fetch(folder string, start, end, limit uint32) ([]*imap.Message, error) {
|
2023-08-13 03:04:16 +01:00
|
|
|
// select the mailbox
|
|
|
|
mbox, err := c.ic.Select(folder, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup fetch range
|
|
|
|
if end > mbox.Messages {
|
|
|
|
end = mbox.Messages
|
|
|
|
}
|
|
|
|
if end-start > limit {
|
|
|
|
start = end - (limit - 1)
|
|
|
|
}
|
|
|
|
seqSet := new(imap.SeqSet)
|
|
|
|
seqSet.AddRange(start, end)
|
|
|
|
|
|
|
|
messages := make(chan *imap.Message, limit)
|
|
|
|
done := make(chan error, 1)
|
|
|
|
go func() {
|
2023-09-11 22:54:55 +01:00
|
|
|
done <- c.ic.Fetch(seqSet, []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid, imap.FetchFlags, imap.FetchInternalDate}, messages)
|
2023-08-13 03:04:16 +01:00
|
|
|
}()
|
|
|
|
|
2023-08-21 00:26:22 +01:00
|
|
|
out := make([]*imap.Message, 0, limit)
|
2023-08-13 03:04:16 +01:00
|
|
|
for msg := range messages {
|
2023-08-21 00:26:22 +01:00
|
|
|
out = append(out, msg)
|
|
|
|
}
|
|
|
|
if err := <-done; err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2023-09-11 22:42:51 +01:00
|
|
|
func (c *Client) list(ref, name string) ([]*imap.MailboxInfo, error) {
|
2023-08-21 00:26:22 +01:00
|
|
|
infos := make(chan *imap.MailboxInfo, 1)
|
|
|
|
done := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
done <- c.ic.List(ref, name, infos)
|
|
|
|
}()
|
|
|
|
|
|
|
|
out := make([]*imap.MailboxInfo, 0)
|
|
|
|
for info := range infos {
|
|
|
|
out = append(out, info)
|
2023-08-13 03:04:16 +01:00
|
|
|
}
|
|
|
|
if err := <-done; err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-08-21 00:26:22 +01:00
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Move(seqset *imap.SeqSet, dest string) error {
|
|
|
|
return c.ic.Move(seqset, dest)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Noop() error {
|
|
|
|
return c.ic.Noop()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Rename(existingName, newName string) error {
|
|
|
|
return c.ic.Rename(existingName, newName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Search(criteria *imap.SearchCriteria) ([]uint32, error) {
|
|
|
|
return c.ic.Search(criteria)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Status(name string) (*imap.MailboxStatus, error) {
|
|
|
|
mbox, err := c.ic.Status(name, imapStatusFlags)
|
|
|
|
return mbox, err
|
2023-08-13 03:04:16 +01:00
|
|
|
}
|