init: pristine aerc 0.20.0 source
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package mboxer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func createMailboxContainer(path string) (*mailboxContainer, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mbdata := &mailboxContainer{mailboxes: make(map[string]*container)}
|
||||
|
||||
openMboxFile := func(path string, r io.Reader) error {
|
||||
// read mbox file
|
||||
messages, err := Read(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, name := filepath.Split(path)
|
||||
name = strings.TrimSuffix(name, ".mbox")
|
||||
mbdata.mailboxes[name] = &container{filename: path, messages: messages}
|
||||
return nil
|
||||
}
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
files, err := filepath.Glob(filepath.Join(path, "*.mbox"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := openMboxFile(file, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
} else {
|
||||
if err := openMboxFile(path, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return mbdata, nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package mboxer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/rfc822"
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
"github.com/emersion/go-mbox"
|
||||
)
|
||||
|
||||
func Read(r io.Reader) ([]rfc822.RawMessage, error) {
|
||||
mbr := mbox.NewReader(r)
|
||||
messages := make([]rfc822.RawMessage, 0)
|
||||
for {
|
||||
msg, err := mbr.NextMessage()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
messages = append(messages, &message{
|
||||
uid: uidFromContents(content),
|
||||
flags: models.SeenFlag,
|
||||
content: content,
|
||||
})
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func Write(w io.Writer, reader io.Reader, from string, date time.Time) error {
|
||||
wc := mbox.NewWriter(w)
|
||||
mw, err := wc.CreateMessage(from, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(mw, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wc.Close()
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package mboxer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/rfc822"
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
)
|
||||
|
||||
type mailboxContainer struct {
|
||||
mailboxes map[string]*container
|
||||
}
|
||||
|
||||
func (md *mailboxContainer) Names() []string {
|
||||
files := make([]string, 0)
|
||||
for file := range md.mailboxes {
|
||||
files = append(files, file)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func (md *mailboxContainer) Mailbox(f string) (*container, bool) {
|
||||
mb, ok := md.mailboxes[f]
|
||||
return mb, ok
|
||||
}
|
||||
|
||||
func (md *mailboxContainer) Create(file string) *container {
|
||||
md.mailboxes[file] = &container{filename: file}
|
||||
return md.mailboxes[file]
|
||||
}
|
||||
|
||||
func (md *mailboxContainer) Remove(file string) error {
|
||||
delete(md.mailboxes, file)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (md *mailboxContainer) DirectoryInfo(file string) *models.DirectoryInfo {
|
||||
var exists int
|
||||
if md, ok := md.Mailbox(file); ok {
|
||||
exists = len(md.Uids())
|
||||
}
|
||||
return &models.DirectoryInfo{
|
||||
Name: file,
|
||||
Exists: exists,
|
||||
Recent: 0,
|
||||
Unseen: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (md *mailboxContainer) Copy(dest, src string, uids []models.UID) error {
|
||||
srcmbox, ok := md.Mailbox(src)
|
||||
if !ok {
|
||||
return fmt.Errorf("source %s not found", src)
|
||||
}
|
||||
destmbox, ok := md.Mailbox(dest)
|
||||
if !ok {
|
||||
return fmt.Errorf("destination %s not found", dest)
|
||||
}
|
||||
for _, uidSrc := range srcmbox.Uids() {
|
||||
found := false
|
||||
for _, uid := range uids {
|
||||
if uid == uidSrc {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
msg, err := srcmbox.Message(uidSrc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get message with uid %s from folder %s", uidSrc, src)
|
||||
}
|
||||
r, err := msg.NewReader()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get reader for message with uid %s", uidSrc)
|
||||
}
|
||||
flags, err := msg.ModelFlags()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get flags for message with uid %s", uidSrc)
|
||||
}
|
||||
err = destmbox.Append(r, flags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not append data to mbox: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
md.mailboxes[dest] = destmbox
|
||||
return nil
|
||||
}
|
||||
|
||||
type container struct {
|
||||
filename string
|
||||
messages []rfc822.RawMessage
|
||||
}
|
||||
|
||||
func (f *container) Uids() []models.UID {
|
||||
uids := make([]models.UID, len(f.messages))
|
||||
for i, m := range f.messages {
|
||||
uids[i] = m.UID()
|
||||
}
|
||||
return uids
|
||||
}
|
||||
|
||||
func (f *container) Message(uid models.UID) (rfc822.RawMessage, error) {
|
||||
for _, m := range f.messages {
|
||||
if uid == m.UID() {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return &message{}, fmt.Errorf("uid [%s] not found", uid)
|
||||
}
|
||||
|
||||
func (f *container) Delete(uids []models.UID) (deleted []models.UID) {
|
||||
newMessages := make([]rfc822.RawMessage, 0)
|
||||
for _, m := range f.messages {
|
||||
del := false
|
||||
for _, uid := range uids {
|
||||
if m.UID() == uid {
|
||||
del = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if del {
|
||||
deleted = append(deleted, m.UID())
|
||||
} else {
|
||||
newMessages = append(newMessages, m)
|
||||
}
|
||||
}
|
||||
f.messages = newMessages
|
||||
return
|
||||
}
|
||||
|
||||
func (f *container) Append(r io.Reader, flags models.Flags) error {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.messages = append(f.messages, &message{
|
||||
uid: uidFromContents(data),
|
||||
flags: flags,
|
||||
content: data,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func uidFromContents(data []byte) models.UID {
|
||||
sum := sha256.New()
|
||||
sum.Write(data)
|
||||
return models.UID(hex.EncodeToString(sum.Sum(nil)))
|
||||
}
|
||||
|
||||
// message implements the lib.RawMessage interface
|
||||
type message struct {
|
||||
uid models.UID
|
||||
flags models.Flags
|
||||
content []byte
|
||||
}
|
||||
|
||||
func (m *message) NewReader() (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader(m.content)), nil
|
||||
}
|
||||
|
||||
func (m *message) ModelFlags() (models.Flags, error) {
|
||||
return m.flags, nil
|
||||
}
|
||||
|
||||
func (m *message) Labels() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *message) UID() models.UID {
|
||||
return m.uid
|
||||
}
|
||||
|
||||
func (m *message) SetFlag(flag models.Flags, state bool) error {
|
||||
if state {
|
||||
m.flags |= flag
|
||||
} else {
|
||||
m.flags &^= flag
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
package mboxer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/log"
|
||||
"git.sr.ht/~rjarry/aerc/lib/rfc822"
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
"git.sr.ht/~rjarry/aerc/worker/handlers"
|
||||
"git.sr.ht/~rjarry/aerc/worker/lib"
|
||||
"git.sr.ht/~rjarry/aerc/worker/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
handlers.RegisterWorkerFactory("mbox", NewWorker)
|
||||
}
|
||||
|
||||
var errUnsupported = fmt.Errorf("unsupported command")
|
||||
|
||||
type mboxWorker struct {
|
||||
data *mailboxContainer
|
||||
name string
|
||||
folder *container
|
||||
worker *types.Worker
|
||||
|
||||
capabilities *models.Capabilities
|
||||
headers []string
|
||||
headersExclude []string
|
||||
}
|
||||
|
||||
func NewWorker(worker *types.Worker) (types.Backend, error) {
|
||||
return &mboxWorker{
|
||||
worker: worker,
|
||||
capabilities: &models.Capabilities{
|
||||
Sort: true,
|
||||
Thread: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *mboxWorker) handleMessage(msg types.WorkerMessage) error {
|
||||
var reterr error // will be returned at the end, needed to support idle
|
||||
|
||||
switch msg := msg.(type) {
|
||||
|
||||
case *types.Unsupported:
|
||||
// No-op
|
||||
|
||||
case *types.Configure:
|
||||
u, err := url.Parse(msg.Config.Source)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
if u.Host == "" && u.Path == "" {
|
||||
u, err = url.Parse("mbox://" + u.Opaque)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
if u.Host == "~" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
dir = filepath.Join(home, u.Path)
|
||||
} else {
|
||||
dir = filepath.Join(u.Host, u.Path)
|
||||
}
|
||||
w.headers = msg.Config.Headers
|
||||
w.headersExclude = msg.Config.HeadersExclude
|
||||
w.data, err = createMailboxContainer(dir)
|
||||
if err != nil || w.data == nil {
|
||||
w.data = &mailboxContainer{
|
||||
mailboxes: make(map[string]*container),
|
||||
}
|
||||
reterr = err
|
||||
break
|
||||
} else {
|
||||
w.worker.Debugf("configured with mbox file %s", dir)
|
||||
}
|
||||
|
||||
case *types.Connect, *types.Reconnect, *types.Disconnect:
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.ListDirectories:
|
||||
dirs := w.data.Names()
|
||||
sort.Strings(dirs)
|
||||
for _, name := range dirs {
|
||||
w.worker.PostMessage(&types.Directory{
|
||||
Message: types.RespondTo(msg),
|
||||
Dir: &models.Directory{
|
||||
Name: name,
|
||||
},
|
||||
}, nil)
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(name),
|
||||
}, nil)
|
||||
}
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.OpenDirectory:
|
||||
w.name = msg.Directory
|
||||
var ok bool
|
||||
w.folder, ok = w.data.Mailbox(w.name)
|
||||
if !ok {
|
||||
w.folder = w.data.Create(w.name)
|
||||
w.worker.PostMessage(&types.Done{
|
||||
Message: types.RespondTo(&types.CreateDirectory{}),
|
||||
}, nil)
|
||||
}
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(msg.Directory),
|
||||
}, nil)
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
w.worker.Debugf("%s opened", msg.Directory)
|
||||
|
||||
case *types.FetchDirectoryContents:
|
||||
uids, err := filterUids(w.folder, w.folder.Uids(), msg.Filter)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
uids, err = sortUids(w.folder, uids, msg.SortCriteria)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
if len(uids) == 0 {
|
||||
reterr = fmt.Errorf("mbox: no uids in directory")
|
||||
break
|
||||
}
|
||||
w.worker.PostMessage(&types.DirectoryContents{
|
||||
Message: types.RespondTo(msg),
|
||||
Uids: uids,
|
||||
}, nil)
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.FetchDirectoryThreaded:
|
||||
reterr = errUnsupported
|
||||
|
||||
case *types.CreateDirectory:
|
||||
w.data.Create(msg.Directory)
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.RemoveDirectory:
|
||||
if err := w.data.Remove(msg.Directory); err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.FetchMessageHeaders:
|
||||
for _, uid := range msg.Uids {
|
||||
m, err := w.folder.Message(uid)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
msgInfo, err := messageInfo(m, true)
|
||||
if err != nil {
|
||||
w.worker.PostMessage(&types.MessageInfo{
|
||||
Info: &models.MessageInfo{
|
||||
Envelope: &models.Envelope{},
|
||||
Flags: models.SeenFlag,
|
||||
Uid: uid,
|
||||
Error: err,
|
||||
},
|
||||
Message: types.RespondTo(msg),
|
||||
}, nil)
|
||||
continue
|
||||
} else {
|
||||
switch {
|
||||
case len(w.headersExclude) > 0:
|
||||
msgInfo.RFC822Headers = lib.LimitHeaders(msgInfo.RFC822Headers, w.headersExclude, true)
|
||||
case len(w.headers) > 0:
|
||||
msgInfo.RFC822Headers = lib.LimitHeaders(msgInfo.RFC822Headers, w.headers, false)
|
||||
}
|
||||
w.worker.PostMessage(&types.MessageInfo{
|
||||
Message: types.RespondTo(msg),
|
||||
Info: msgInfo,
|
||||
}, nil)
|
||||
}
|
||||
}
|
||||
w.worker.PostMessage(
|
||||
&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.FetchMessageBodyPart:
|
||||
m, err := w.folder.Message(msg.Uid)
|
||||
if err != nil {
|
||||
w.worker.Errorf("could not get message %d: %v", msg.Uid, err)
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
|
||||
contentReader, err := m.NewReader()
|
||||
if err != nil {
|
||||
reterr = fmt.Errorf("could not get message reader: %w", err)
|
||||
break
|
||||
}
|
||||
|
||||
fullMsg, err := rfc822.ReadMessage(contentReader)
|
||||
if err != nil {
|
||||
reterr = fmt.Errorf("could not read message: %w", err)
|
||||
break
|
||||
}
|
||||
|
||||
r, err := rfc822.FetchEntityPartReader(fullMsg, msg.Part)
|
||||
if err != nil {
|
||||
w.worker.Errorf(
|
||||
"could not get body part reader for message=%d, parts=%#v: %w",
|
||||
msg.Uid, msg.Part, err)
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
|
||||
w.worker.PostMessage(&types.MessageBodyPart{
|
||||
Message: types.RespondTo(msg),
|
||||
Part: &models.MessageBodyPart{
|
||||
Reader: r,
|
||||
Uid: msg.Uid,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
case *types.FetchFullMessages:
|
||||
for _, uid := range msg.Uids {
|
||||
m, err := w.folder.Message(uid)
|
||||
if err != nil {
|
||||
w.worker.Errorf("could not get message for uid %d: %v", uid, err)
|
||||
continue
|
||||
}
|
||||
r, err := m.NewReader()
|
||||
if err != nil {
|
||||
w.worker.Errorf("could not get message reader: %v", err)
|
||||
continue
|
||||
}
|
||||
defer r.Close()
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
w.worker.Errorf("could not get message reader: %v", err)
|
||||
continue
|
||||
}
|
||||
w.worker.PostMessage(&types.FullMessage{
|
||||
Message: types.RespondTo(msg),
|
||||
Content: &models.FullMessage{
|
||||
Uid: uid,
|
||||
Reader: bytes.NewReader(b),
|
||||
},
|
||||
}, nil)
|
||||
}
|
||||
w.worker.PostMessage(&types.Done{
|
||||
Message: types.RespondTo(msg),
|
||||
}, nil)
|
||||
|
||||
case *types.DeleteMessages:
|
||||
deleted := w.folder.Delete(msg.Uids)
|
||||
if len(deleted) > 0 {
|
||||
w.worker.PostMessage(&types.MessagesDeleted{
|
||||
Message: types.RespondTo(msg),
|
||||
Uids: deleted,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(w.name),
|
||||
}, nil)
|
||||
|
||||
w.worker.PostMessage(
|
||||
&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.FlagMessages:
|
||||
for _, uid := range msg.Uids {
|
||||
m, err := w.folder.Message(uid)
|
||||
if err != nil {
|
||||
w.worker.Errorf("could not get message: %v", err)
|
||||
continue
|
||||
}
|
||||
if err := m.(*message).SetFlag(msg.Flags, msg.Enable); err != nil {
|
||||
w.worker.Errorf("could not change flag %v to %t on message: %v",
|
||||
msg.Flags, msg.Enable, err)
|
||||
continue
|
||||
}
|
||||
info, err := rfc822.MessageInfo(m)
|
||||
if err != nil {
|
||||
w.worker.Errorf("could not get message info: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
w.worker.PostMessage(&types.MessageInfo{
|
||||
Message: types.RespondTo(msg),
|
||||
Info: info,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(w.name),
|
||||
}, nil)
|
||||
|
||||
w.worker.PostMessage(
|
||||
&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.CopyMessages:
|
||||
err := w.data.Copy(msg.Destination, w.name, msg.Uids)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(w.name),
|
||||
}, nil)
|
||||
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(msg.Destination),
|
||||
}, nil)
|
||||
|
||||
w.worker.PostMessage(
|
||||
&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
case *types.MoveMessages:
|
||||
err := w.data.Copy(msg.Destination, w.name, msg.Uids)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
deleted := w.folder.Delete(msg.Uids)
|
||||
if len(deleted) > 0 {
|
||||
w.worker.PostMessage(&types.MessagesDeleted{
|
||||
Message: types.RespondTo(msg),
|
||||
Uids: deleted,
|
||||
}, nil)
|
||||
}
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(msg.Destination),
|
||||
}, nil)
|
||||
w.worker.PostMessage(
|
||||
&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
|
||||
case *types.SearchDirectory:
|
||||
uids, err := filterUids(w.folder, w.folder.Uids(), msg.Criteria)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
break
|
||||
}
|
||||
w.worker.PostMessage(&types.SearchResults{
|
||||
Message: types.RespondTo(msg),
|
||||
Uids: uids,
|
||||
}, nil)
|
||||
|
||||
case *types.AppendMessage:
|
||||
if msg.Destination == "" {
|
||||
reterr = fmt.Errorf("AppendMessage with empty destination directory")
|
||||
break
|
||||
}
|
||||
folder, ok := w.data.Mailbox(msg.Destination)
|
||||
if !ok {
|
||||
folder = w.data.Create(msg.Destination)
|
||||
w.worker.PostMessage(&types.Done{
|
||||
Message: types.RespondTo(&types.CreateDirectory{}),
|
||||
}, nil)
|
||||
}
|
||||
|
||||
if err := folder.Append(msg.Reader, msg.Flags); err != nil {
|
||||
reterr = err
|
||||
break
|
||||
} else {
|
||||
w.worker.PostMessage(&types.DirectoryInfo{
|
||||
Info: w.data.DirectoryInfo(msg.Destination),
|
||||
}, nil)
|
||||
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
||||
}
|
||||
|
||||
case *types.AnsweredMessages:
|
||||
reterr = errUnsupported
|
||||
default:
|
||||
reterr = errUnsupported
|
||||
}
|
||||
|
||||
return reterr
|
||||
}
|
||||
|
||||
func (w *mboxWorker) Run() {
|
||||
for msg := range w.worker.Actions() {
|
||||
msg = w.worker.ProcessAction(msg)
|
||||
if err := w.handleMessage(msg); errors.Is(err, errUnsupported) {
|
||||
w.worker.PostMessage(&types.Unsupported{
|
||||
Message: types.RespondTo(msg),
|
||||
}, nil)
|
||||
} else if err != nil {
|
||||
w.worker.PostMessage(&types.Error{
|
||||
Message: types.RespondTo(msg),
|
||||
Error: err,
|
||||
}, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *mboxWorker) Capabilities() *models.Capabilities {
|
||||
return w.capabilities
|
||||
}
|
||||
|
||||
func (w *mboxWorker) PathSeparator() string {
|
||||
return "/"
|
||||
}
|
||||
|
||||
func filterUids(folder *container, uids []models.UID, criteria *types.SearchCriteria) ([]models.UID, error) {
|
||||
log.Debugf("Search with parsed criteria: %#v", criteria)
|
||||
m := make([]rfc822.RawMessage, 0, len(uids))
|
||||
for _, uid := range uids {
|
||||
msg, err := folder.Message(uid)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get message for uid: %d", uid)
|
||||
continue
|
||||
}
|
||||
m = append(m, msg)
|
||||
}
|
||||
return lib.Search(m, criteria)
|
||||
}
|
||||
|
||||
func sortUids(folder *container, uids []models.UID,
|
||||
criteria []*types.SortCriterion,
|
||||
) ([]models.UID, error) {
|
||||
var infos []*models.MessageInfo
|
||||
needSize := false
|
||||
for _, item := range criteria {
|
||||
if item.Field == types.SortSize {
|
||||
needSize = true
|
||||
}
|
||||
}
|
||||
for _, uid := range uids {
|
||||
m, err := folder.Message(uid)
|
||||
if err != nil {
|
||||
log.Errorf("could not get message %v", err)
|
||||
continue
|
||||
}
|
||||
info, err := messageInfo(m, needSize)
|
||||
if err != nil {
|
||||
log.Errorf("could not get message info %v", err)
|
||||
continue
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
return lib.Sort(infos, criteria)
|
||||
}
|
||||
|
||||
func messageInfo(m rfc822.RawMessage, needSize bool) (*models.MessageInfo, error) {
|
||||
info, err := rfc822.MessageInfo(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !needSize {
|
||||
return info, nil
|
||||
}
|
||||
r, err := m.NewReader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size, err := io.Copy(io.Discard, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.Size = uint32(size)
|
||||
return info, nil
|
||||
}
|
||||
Reference in New Issue
Block a user