aboutsummaryrefslogtreecommitdiff
path: root/internal/imap/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/imap/client.go')
-rw-r--r--internal/imap/client.go141
1 files changed, 110 insertions, 31 deletions
diff --git a/internal/imap/client.go b/internal/imap/client.go
index da06f54..5e2546b 100644
--- a/internal/imap/client.go
+++ b/internal/imap/client.go
@@ -3,6 +3,7 @@ package imap
import (
"fmt"
"strings"
+ "sync"
"time"
"github.com/emersion/go-imap"
@@ -11,20 +12,37 @@ import (
"github.com/Necoro/feed2imap-go/internal/log"
)
-type Client struct {
- c *imapClient.Client
+const numberConns = 5
+
+type connConf struct {
host string
- mailboxes mailboxes
delimiter string
toplevel Folder
+}
+
+type connection struct {
+ *connConf
+ mailboxes *mailboxes
+ c *imapClient.Client
+}
+
+type mailboxes struct {
+ mb map[string]*imap.MailboxInfo
+ mu sync.RWMutex
+}
+
+type Client struct {
+ connConf
+ mailboxes mailboxes
commander *commander
+ connections [numberConns]*connection
+ nextFreeIndex int
}
type Folder struct {
str string
delimiter string
}
-type mailboxes map[string]*imap.MailboxInfo
func (f Folder) String() string {
return f.str
@@ -40,21 +58,38 @@ func (f Folder) Append(other Folder) Folder {
}
}
-func (mbs mailboxes) contains(elem Folder) bool {
- _, ok := mbs[elem.str]
+func (mbs *mailboxes) contains(elem Folder) bool {
+ mbs.mu.RLock()
+ defer mbs.mu.RUnlock()
+
+ _, ok := mbs.mb[elem.str]
return ok
}
-func (mbs mailboxes) add(elem *imap.MailboxInfo) {
- mbs[elem.Name] = elem
+func (mbs *mailboxes) add(elem *imap.MailboxInfo) {
+ mbs.mu.Lock()
+ defer mbs.mu.Unlock()
+
+ mbs.mb[elem.Name] = elem
+}
+
+func (conn *connection) Disconnect() bool {
+ if conn != nil {
+ connected := (conn.c.State() & imap.ConnectedState) != 0
+ _ = conn.c.Logout()
+ return connected
+ }
+ return false
}
func (client *Client) Disconnect() {
if client != nil {
client.stopCommander()
- connected := (client.c.State() & imap.ConnectedState) != 0
- _ = client.c.Logout()
+ connected := false
+ for _, conn := range client.connections {
+ connected = conn.Disconnect() || connected
+ }
if connected {
log.Print("Disconnected from ", client.host)
@@ -73,13 +108,13 @@ func (client *Client) NewFolder(path []string) Folder {
return client.toplevel.Append(client.folderName(path))
}
-func (client *Client) createFolder(folder string) error {
- err := client.c.Create(folder)
+func (conn *connection) createFolder(folder string) error {
+ err := conn.c.Create(folder)
if err != nil {
return fmt.Errorf("creating folder '%s': %w", folder, err)
}
- err = client.c.Subscribe(folder)
+ err = conn.c.Subscribe(folder)
if err != nil {
return fmt.Errorf("subscribing to folder '%s': %w", folder, err)
}
@@ -89,11 +124,11 @@ func (client *Client) createFolder(folder string) error {
return nil
}
-func (client *Client) list(folder string) (*imap.MailboxInfo, int, error) {
+func (conn *connection) list(folder string) (*imap.MailboxInfo, int, error) {
mailboxes := make(chan *imap.MailboxInfo, 10)
done := make(chan error, 1)
go func() {
- done <- client.c.List("", folder, mailboxes)
+ done <- conn.c.List("", folder, mailboxes)
}()
found := 0
@@ -112,24 +147,23 @@ func (client *Client) list(folder string) (*imap.MailboxInfo, int, error) {
return mbox, found, nil
}
-func (client *Client) fetchDelimiter() error {
- mbox, _, err := client.list("")
+func (conn *connection) fetchDelimiter() (string, error) {
+ mbox, _, err := conn.list("")
if err != nil {
- return err
+ return "", err
}
- client.delimiter = mbox.Delimiter
- return nil
+ return mbox.Delimiter, nil
}
-func (client *Client) ensureFolder(folder Folder) error {
- if client.mailboxes.contains(folder) {
+func (conn *connection) ensureFolder(folder Folder) error {
+ if conn.mailboxes.contains(folder) {
return nil
}
log.Printf("Checking for folder '%s'", folder)
- mbox, found, err := client.list(folder.str)
+ mbox, found, err := conn.list(folder.str)
if err != nil {
return err
}
@@ -140,20 +174,20 @@ func (client *Client) ensureFolder(folder Folder) error {
switch found {
case 0:
- return client.createFolder(folder.str)
+ return conn.createFolder(folder.str)
case 1:
- client.mailboxes.add(mbox)
+ conn.mailboxes.add(mbox)
return nil
default:
return fmt.Errorf("Found multiple folders matching '%s'.", folder)
}
}
-func (client *Client) EnsureFolder(folder Folder, errorHandler ErrorHandler) {
- client.commander.execute(ensureCommando{folder}, errorHandler)
+func (client *Client) EnsureFolder(folder Folder) error {
+ return client.commander.execute(ensureCommando{folder})
}
-func (client *Client) putMessages(folder Folder, messages []string) error {
+func (conn *connection) putMessages(folder Folder, messages []string) error {
if len(messages) == 0 {
return nil
}
@@ -161,7 +195,7 @@ func (client *Client) putMessages(folder Folder, messages []string) error {
now := time.Now()
for _, msg := range messages {
reader := strings.NewReader(msg)
- if err := client.c.Append(folder.str, nil, now, reader); err != nil {
+ if err := conn.c.Append(folder.str, nil, now, reader); err != nil {
return fmt.Errorf("uploading message to %s: %w", folder, err)
}
}
@@ -169,6 +203,51 @@ func (client *Client) putMessages(folder Folder, messages []string) error {
return nil
}
-func (client *Client) PutMessages(folder Folder, messages []string, errorHandler ErrorHandler) {
- client.commander.execute(addCommando{folder, messages}, errorHandler)
+func (client *Client) PutMessages(folder Folder, messages []string) error {
+ return client.commander.execute(addCommando{folder, messages})
+}
+
+func (client *Client) createConnection(c *imapClient.Client) *connection{
+ if client.nextFreeIndex >= len(client.connections) {
+ panic("Too many connections")
+ }
+
+ conn := &connection{
+ connConf: &client.connConf,
+ mailboxes: &client.mailboxes,
+ c: c,
+ }
+
+ client.connections[client.nextFreeIndex] = conn
+ client.nextFreeIndex++
+
+ return conn
+}
+
+func (conn *connection) startTls() error {
+ hasStartTls, err := conn.c.SupportStartTLS()
+ if err != nil {
+ return fmt.Errorf("checking for starttls for %s: %w", conn.host, err)
+ }
+
+ if hasStartTls {
+ if err = conn.c.StartTLS(nil); err != nil {
+ return fmt.Errorf("enabling starttls for %s: %w", conn.host, err)
+ }
+
+ log.Print("Connected to ", conn.host, " (STARTTLS)")
+ } else {
+ log.Print("Connected to ", conn.host, " (Plain)")
+ }
+
+ return nil
}
+
+func NewClient() *Client {
+ return &Client{
+ mailboxes: mailboxes{
+ mb: map[string]*imap.MailboxInfo{},
+ mu: sync.RWMutex{},
+ },
+ }
+} \ No newline at end of file