aboutsummaryrefslogtreecommitdiff
path: root/internal/imap
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.eu>2020-05-03 00:41:36 +0200
committerRené 'Necoro' Neumann <necoro@necoro.eu>2020-05-03 00:41:36 +0200
commit168cce8a935de864eef95f423f128a7bf49aafda (patch)
tree9cc240dd479b1e85fbb55e1cda4a14d3a938d50e /internal/imap
parentfec3ecd257c34fba37703b2999ab5ea902314657 (diff)
downloadfeed2imap-go-168cce8a935de864eef95f423f128a7bf49aafda.tar.gz
feed2imap-go-168cce8a935de864eef95f423f128a7bf49aafda.tar.bz2
feed2imap-go-168cce8a935de864eef95f423f128a7bf49aafda.zip
Update support for IMAP
Diffstat (limited to 'internal/imap')
-rw-r--r--internal/imap/cmds.go16
-rw-r--r--internal/imap/connection.go109
2 files changed, 121 insertions, 4 deletions
diff --git a/internal/imap/cmds.go b/internal/imap/cmds.go
index d978d80..7c99fc3 100644
--- a/internal/imap/cmds.go
+++ b/internal/imap/cmds.go
@@ -24,3 +24,19 @@ func (cmd addCommando) execute(conn *connection) error {
func (client *Client) PutMessages(folder Folder, messages []string) error {
return client.commander.execute(addCommando{folder, messages})
}
+
+type replaceCommando struct {
+ folder Folder
+ header string
+ value string
+ newContent string
+ force bool
+}
+
+func (cmd replaceCommando) execute(conn *connection) error {
+ return conn.replace(cmd.folder, cmd.header, cmd.value, cmd.newContent, cmd.force)
+}
+
+func (client *Client) Replace(folder Folder, header, value, newContent string, force bool) error {
+ return client.commander.execute(replaceCommando{folder, header, value, newContent, force})
+}
diff --git a/internal/imap/connection.go b/internal/imap/connection.go
index 5f62586..68b7e6b 100644
--- a/internal/imap/connection.go
+++ b/internal/imap/connection.go
@@ -128,16 +128,117 @@ func (conn *connection) ensureFolder(folder Folder) error {
}
}
+func (conn *connection) delete(uids []uint32) error {
+ storeItem := imap.FormatFlagsOp(imap.AddFlags, true)
+ seqSet := new(imap.SeqSet)
+ seqSet.AddNum(uids...)
+
+ if err := conn.c.UidStore(seqSet, storeItem, imap.DeletedFlag, nil); err != nil {
+ return fmt.Errorf("marking as deleted: %w", err)
+ }
+
+ if err := conn.c.Expunge(nil); err != nil {
+ return fmt.Errorf("expunging: %w", err)
+ }
+
+ return nil
+}
+
+func (conn *connection) fetchFlags(uid uint32) ([]string, error) {
+ fetchItem := []imap.FetchItem{imap.FetchFlags}
+
+ seqSet := new(imap.SeqSet)
+ seqSet.AddNum(uid)
+
+ messages := make(chan *imap.Message, 1)
+ done := make(chan error, 1)
+ go func() {
+ done <- conn.c.UidFetch(seqSet, fetchItem, messages)
+ }()
+
+ msg := <-messages
+ err := <-done
+
+ if err != nil {
+ return nil, fmt.Errorf("fetching flags: %w", err)
+ }
+ return msg.Flags, nil
+}
+
+func (conn *connection) replace(folder Folder, header, value, newContent string, force bool) error {
+ var err error
+ var msgIds []uint32
+
+ if err = conn.selectFolder(folder); err != nil {
+ return err
+ }
+
+ if msgIds, err = conn.searchHeader(header, value); err != nil {
+ return err
+ }
+
+ if len(msgIds) == 0 {
+ if force {
+ return conn.append(folder, nil, newContent)
+ }
+ return nil // nothing to do
+ }
+
+ var flags []string
+ if flags, err = conn.fetchFlags(msgIds[0]); err != nil {
+ return err
+ }
+
+ if err = conn.delete(msgIds); err != nil {
+ return err
+ }
+
+ if err = conn.append(folder, flags, newContent); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (conn *connection) searchHeader(header, value string) ([]uint32, error) {
+ criteria := imap.NewSearchCriteria()
+ criteria.Header.Set(header, value)
+ ids, err := conn.search(criteria)
+ if err != nil {
+ return nil, fmt.Errorf("searching for header %q=%q: %w", header, value, err)
+ }
+ return ids, nil
+}
+
+func (conn *connection) search(criteria *imap.SearchCriteria) ([]uint32, error) {
+ return conn.c.UidSearch(criteria)
+}
+
+func (conn *connection) selectFolder(folder Folder) error {
+ if _, err := conn.c.Select(folder.str, false); err != nil {
+ return fmt.Errorf("selecting folder %s: %w", folder, err)
+ }
+
+ return nil
+}
+
+func (conn *connection) append(folder Folder, flags []string, msg string) error {
+ reader := strings.NewReader(msg)
+ if err := conn.c.Append(folder.str, flags, time.Now(), reader); err != nil {
+ return fmt.Errorf("uploading message to %s: %w", folder, err)
+ }
+
+ return nil
+}
+
func (conn *connection) putMessages(folder Folder, messages []string) error {
if len(messages) == 0 {
return nil
}
- now := time.Now()
for _, msg := range messages {
- reader := strings.NewReader(msg)
- if err := conn.c.Append(folder.str, nil, now, reader); err != nil {
- return fmt.Errorf("uploading message to %s: %w", folder, err)
+ if err := conn.append(folder, nil, msg); err != nil {
+ return err
}
}