aboutsummaryrefslogtreecommitdiff
path: root/internal/imap/imap.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--internal/imap/imap.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/internal/imap/imap.go b/internal/imap/imap.go
new file mode 100644
index 0000000..18039d3
--- /dev/null
+++ b/internal/imap/imap.go
@@ -0,0 +1,126 @@
+package imap
+
+import (
+ "fmt"
+ "net/url"
+
+ "github.com/emersion/go-imap"
+ imapClient "github.com/emersion/go-imap/client"
+
+ "github.com/Necoro/feed2imap-go/internal/log"
+)
+
+const (
+ imapsPort = "993"
+ imapPort = "143"
+)
+
+type Client struct {
+ c *imapClient.Client
+ host string
+}
+
+func forceTLS(url *url.URL) bool {
+ return url.Scheme == "imaps" || url.Port() == imapsPort
+}
+
+func setDefaultScheme(url *url.URL) {
+ switch url.Scheme {
+ case "imap", "imaps":
+ return
+ default:
+ oldScheme := url.Scheme
+ if url.Port() == imapsPort {
+ url.Scheme = "imaps"
+ } else {
+ url.Scheme = "imap"
+ }
+
+ if oldScheme != "" {
+ log.Warnf("Unknown scheme '%s', defaulting to '%s'", oldScheme, url.Scheme)
+ }
+ }
+}
+
+func setDefaultPort(url *url.URL) {
+ if url.Port() == "" {
+ var port string
+ if url.Scheme == "imaps" {
+ port = imapsPort
+ } else {
+ port = imapPort
+ }
+ url.Host += ":" + port
+ }
+}
+
+func sanitizeUrl(url *url.URL) {
+ setDefaultScheme(url)
+ setDefaultPort(url)
+}
+
+func (client *Client) Disconnect() {
+ if client != nil {
+ connected := (client.c.State() & imap.ConnectedState) != 0
+ _ = client.c.Logout()
+
+ if connected {
+ log.Print("Disconnected from ", client.host)
+ }
+ }
+}
+
+func Connect(url *url.URL) (*Client, error) {
+ var c *imapClient.Client
+ var err error
+
+ sanitizeUrl(url)
+
+ forceTls := forceTLS(url)
+
+ if forceTls {
+ c, err = imapClient.DialTLS(url.Host, nil)
+ if err != nil {
+ return nil, fmt.Errorf("connecting (TLS) to %s: %w", url.Host, err)
+ }
+ log.Print("Connected to ", url.Host, " (TLS)")
+ } else {
+ c, err = imapClient.Dial(url.Host)
+ if err != nil {
+ return nil, fmt.Errorf("connecting to %s: %w", url.Host, err)
+ }
+ }
+
+ var client = Client{c, url.Host}
+
+ defer func() {
+ if err != nil {
+ client.Disconnect()
+ }
+ }()
+
+ if !forceTls {
+ var hasStartTls bool // explicit to avoid shadowing err
+
+ hasStartTls, err = c.SupportStartTLS()
+ if err != nil {
+ return nil, fmt.Errorf("checking for starttls for %s: %w", url.Host, err)
+ }
+
+ if hasStartTls {
+ if err = c.StartTLS(nil); err != nil {
+ return nil, fmt.Errorf("enabling starttls for %s: %w", url.Host, err)
+ }
+
+ log.Print("Connected to ", url.Host, " (STARTTLS)")
+ }
+ log.Print("Connected to ", url.Host, " (Plain)")
+ }
+
+ pwd, _ := url.User.Password()
+ if err = c.Login(url.User.Username(), pwd); err != nil {
+ return nil, fmt.Errorf("login to %s: %w", url.Host, err)
+ }
+
+ return &client, nil
+}