Replace hand-rolled IMAP with go-imap
This commit is contained in:
parent
21aa6e39e1
commit
253435d87f
7 changed files with 449 additions and 346 deletions
|
|
@ -1,41 +1,44 @@
|
|||
package backend
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap/v2"
|
||||
)
|
||||
|
||||
// TargetMailbox = das Ziel-Postfach (neuer Host). Der Umzug schreibt hier per
|
||||
// IMAP APPEND hinein -- NICHT per SMTP. APPEND erhaelt Ordner, Flags und
|
||||
// Original-Datum; SMTP wuerde alles als "neu/ungelesen" mit falschem Datum
|
||||
// zustellen und die Ordnerstruktur zerstoeren.
|
||||
type TargetMailbox interface {
|
||||
Folders() ([]TargetFolder, error) // vorhandene Ziel-Ordner (fuer Rollen-Zuordnung)
|
||||
Delim() string // Hierarchie-Trenner des Ziels
|
||||
EnsureFolder(name string) error // anlegen + subscriben, falls fehlt
|
||||
Append(folder string, m RawMessage) error // mit m.Flags und m.InternalDate
|
||||
Folders() ([]TargetFolder, error) // vorhandene Ziel-Ordner (fuer Rollen-Zuordnung)
|
||||
Delim() string // Hierarchie-Trenner des Ziels
|
||||
EnsureFolder(name string) error // anlegen + subscriben, falls fehlt
|
||||
Append(folder string, m RawMessage) error // mit m.Flags und m.InternalDate
|
||||
Fetch(folder string) ([]RawMessage, error) // Ziel-Audit: geschriebene Mail wiederfinden
|
||||
Close() error
|
||||
}
|
||||
|
||||
// OpenIMAPTarget verbindet das Ziel per go-imap/v2 (Security wie Quelle, siehe
|
||||
// a.DstSecurity/a.DstInsecure; meist "tls").
|
||||
func OpenIMAPTarget(a Account) (TargetMailbox, error) {
|
||||
c, err := dialIMAP(a.DstHost, a.DstPort, a.DstSecurity, a.DstInsecure)
|
||||
c, err := dialIMAPClient(a.DstHost, a.DstPort, a.DstSecurity, a.DstInsecure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.login(a.DstUser, a.DstPass); err != nil {
|
||||
if err := c.Login(a.DstUser, a.DstPass).Wait(); err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &imapTarget{c: c}, nil
|
||||
return &imapTarget{mailbox: &imapClientMailbox{c: c}}, nil
|
||||
}
|
||||
|
||||
type imapTarget struct {
|
||||
c *simpleIMAP
|
||||
delim string
|
||||
mailbox *imapClientMailbox
|
||||
delim string
|
||||
}
|
||||
|
||||
func (t *imapTarget) Folders() ([]TargetFolder, error) {
|
||||
boxes, err := t.c.listMailboxes()
|
||||
boxes, err := t.mailbox.listMailboxes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -63,29 +66,64 @@ func (t *imapTarget) Delim() string {
|
|||
}
|
||||
|
||||
func (t *imapTarget) EnsureFolder(name string) error {
|
||||
if name == "INBOX" {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" || strings.EqualFold(name, "INBOX") {
|
||||
return nil
|
||||
}
|
||||
if err := t.c.simple("CREATE " + imapQuote(name)); err != nil {
|
||||
if !strings.Contains(strings.ToLower(err.Error()), "exist") && !strings.Contains(strings.ToLower(err.Error()), "already") {
|
||||
return err
|
||||
}
|
||||
if err := t.ensureParentFolders(name); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = t.c.simple("SUBSCRIBE " + imapQuote(name))
|
||||
if err := t.createFolder(name); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = t.mailbox.c.Subscribe(name).Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *imapTarget) ensureParentFolders(name string) error {
|
||||
delim := t.Delim()
|
||||
if delim == "" || !strings.Contains(name, delim) {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(name, delim)
|
||||
for i := 1; i < len(parts); i++ {
|
||||
parent := strings.Join(parts[:i], delim)
|
||||
if strings.TrimSpace(parent) == "" || strings.EqualFold(parent, "INBOX") {
|
||||
continue
|
||||
}
|
||||
if err := t.createFolder(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *imapTarget) createFolder(name string) error {
|
||||
err := t.mailbox.c.Create(name, nil).Wait()
|
||||
if err == nil || isAlreadyExistsError(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("imap create %q failed: %w", name, err)
|
||||
}
|
||||
|
||||
func (t *imapTarget) Append(folder string, m RawMessage) error {
|
||||
return t.c.appendMessage(folder, m)
|
||||
opts := &imap.AppendOptions{Flags: stringsToFlags(m.Flags), Time: m.InternalDate}
|
||||
cmd := t.mailbox.c.Append(folder, int64(len(m.Body)), opts)
|
||||
if _, err := cmd.Write(m.Body); err != nil {
|
||||
_ = cmd.Close()
|
||||
return err
|
||||
}
|
||||
if err := cmd.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := cmd.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *imapTarget) Fetch(folder string) ([]RawMessage, error) {
|
||||
if err := t.c.selectMailbox(folder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.c.fetchAll()
|
||||
return t.mailbox.fetchAll(folder)
|
||||
}
|
||||
|
||||
func (t *imapTarget) Close() error {
|
||||
return t.c.Close()
|
||||
return t.mailbox.Close()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue