~rockorager/offmap

offmap: config: implement folder mapping v1 PROPOSED

Tim Culverhouse: 1
 config: implement folder mapping

 5 files changed, 82 insertions(+), 11 deletions(-)
#912193 .build.yml failed



          
          
          
          
Next
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~rockorager/offmap/patches/37788/mbox | git am -3
Learn more about email & git

[PATCH offmap] config: implement folder mapping Export this patch

Implement folder mapping from remote folder name to local folder name.
Add test for mappings, update docs.

Maps must be of the form:
REMOTE = LOCAL

And it is recommended in the docs that the key should be quoted as well,
specifically when the remote uses '.' delimiters, as this will break
toml parsing. Quoted keys with '.' are parsed as raw strings and will
not break toml.

Example:

"INBOX.test" = "Inbox/test" // Works
INBOX.test = "Inbox/test" // Doesn't work
Implements: https://todo.sr.ht/~rockorager/offmap/19
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
---
 config.go          | 15 ++++++++-------
 doc/offmap.5.scd   | 17 +++++++++++++++++
 imap/imap.go       | 26 ++++++++++++++++++++++----
 imap/imap_test.go  | 33 +++++++++++++++++++++++++++++++++
 maildir/maildir.go |  2 ++
 5 files changed, 82 insertions(+), 11 deletions(-)
 create mode 100644 imap/imap_test.go

diff --git a/config.go b/config.go
index e6fc2016d5d0..2c6bd07d4cc8 100644
--- a/config.go
+++ b/config.go
@@ -11,13 +11,14 @@ import (

type Config struct {
	Name           string
	Maildir        string `toml:"maildir"`
	Host           string `toml:"host"`
	Username       string `toml:"username"`
	Password       string `toml:"password"`
	PasswordCmd    string `toml:"password-cmd"`
	AuthMech       string `toml:"auth-mech"`
	MaxConnections int    `toml:"max-connections"`
	Maildir        string            `toml:"maildir"`
	Host           string            `toml:"host"`
	Username       string            `toml:"username"`
	Password       string            `toml:"password"`
	PasswordCmd    string            `toml:"password-cmd"`
	AuthMech       string            `toml:"auth-mech"`
	MaxConnections int               `toml:"max-connections"`
	Map            map[string]string `toml:"map"`
}

func LoadConfig(accts []string) ([]*Config, error) {
diff --git a/doc/offmap.5.scd b/doc/offmap.5.scd
index da4ee3b7a0b0..e715439ecef0 100644
--- a/doc/offmap.5.scd
+++ b/doc/offmap.5.scd
@@ -68,3 +68,20 @@ auth-mech     = "oauth2"
	effect for IMAP servers who *do not* support LIST-STATUS.

	Default: 1

*map*
	A table of remote mailbox names mapped to local mailbox names. The key
	should always be the remote mailbox name, and is recommended to be in
	quotes. IMAP servers with '.' delimiters require quotes. Maps will apply
	to any children of the mapped mailbox.

	Example:
	```
	[account-name.map]
	"INBOX" = "inbox"
	"Lists/offmap" = "offmap"
	```

	In the above example, a child of "INBOX" would be mapped from
	"INBOX/child" to "inbox/child". However, siblings of "Lists/offmap"
	would not be mapped, and would still exist under "Lists/*".
diff --git a/imap/imap.go b/imap/imap.go
index 791248b04615..db1f7a853097 100644
--- a/imap/imap.go
+++ b/imap/imap.go
@@ -292,10 +292,7 @@ func (s *Store) listMailboxes() ([]*offmap.Mailbox, error) {

	// Process the statuses
	for _, status := range statuses {
		local := status.Name
		if s.Delimiter() != "/" {
			local = strings.ReplaceAll(local, s.Delimiter(), "/")
		}
		local := s.localName(status.Name)
		mailbox := offmap.NewMailbox(status.UidValidity, local, status.Name)
		mailbox.UIDNext = status.UidNext
		if s.SupportCondstore() {
@@ -306,6 +303,27 @@ func (s *Store) listMailboxes() ([]*offmap.Mailbox, error) {
	return s.mailboxes, nil
}

// Finds the local name by replacing delimiters and checking the configured map
func (s *Store) localName(remote string) string {
	local := remote
	nodes := strings.Split(remote, s.Delimiter())
	// Find mapped paths by starting at most specific, and moving up the
	// tree. The first mapped path breaks the loop, as this is the most
	// specific mapping and the only one we will apply
	for i := len(nodes); i > 0; i -= 1 {
		path := strings.Join(nodes[:i], s.Delimiter())
		mapped, ok := s.cfg.Map[path]
		if ok {
			local = strings.Replace(local, path, mapped, 1)
			break
		}
	}
	if s.Delimiter() != "/" {
		local = strings.ReplaceAll(local, s.Delimiter(), "/")
	}
	return local
}

// Returns the delimiter used on the server
func (s *Store) Delimiter() string {
	if s.delim == "" {
diff --git a/imap/imap_test.go b/imap/imap_test.go
new file mode 100644
index 000000000000..045bd0dd4c92
--- /dev/null
+++ b/imap/imap_test.go
@@ -0,0 +1,33 @@
package imap

import (
	"testing"

	"git.sr.ht/~rockorager/offmap"
	"github.com/stretchr/testify/assert"
)

func TestLocalName(t *testing.T) {
	cfg := &offmap.Config{}
	cfg.Map = map[string]string{
		"INBOX": "inbox",
		"INBOX/child": "child",
	}
	store := Store{cfg: cfg}
	store.delim = "/"
	assert := assert.New(t)
	assert.Equal("inbox", store.localName("INBOX"))
	assert.Equal("child", store.localName("INBOX/child"))
	assert.Equal("child/grandchild", store.localName("INBOX/child/grandchild"))
	assert.Equal("inbox/sibling", store.localName("INBOX/sibling"))

	store.delim = "."
	cfg.Map = map[string]string{
		"INBOX": "inbox",
		"INBOX.child": "child",
	}
	assert.Equal("inbox", store.localName("INBOX"))
	assert.Equal("child", store.localName("INBOX.child"))
	assert.Equal("child/grandchild", store.localName("INBOX.child.grandchild"))
	assert.Equal("inbox/sibling", store.localName("INBOX.sibling"))
}
diff --git a/maildir/maildir.go b/maildir/maildir.go
index a3ed66496a54..7743f40841d8 100644
--- a/maildir/maildir.go
+++ b/maildir/maildir.go
@@ -21,6 +21,7 @@ type Store struct {
	state *offmap.State
	// map of relpath to mailbox
	mailboxes map[string]*offmap.Mailbox
	dirmap    map[string]string
}

func NewStore(cfg *offmap.Config) (*Store, error) {
@@ -35,6 +36,7 @@ func NewStore(cfg *offmap.Config) (*Store, error) {
	if err != nil {
		return nil, fmt.Errorf("maildir: error creating root: %v", err)
	}
	store.dirmap = cfg.Map

	return store, nil
}
-- 
2.39.0
offmap/patches/.build.yml: FAILED in 45s

[config: implement folder mapping][0] from [Tim Culverhouse][1]

[0]: https://lists.sr.ht/~rockorager/offmap/patches/37788
[1]: mailto:tim@timculverhouse.com

✗ #912193 FAILED offmap/patches/.build.yml https://builds.sr.ht/~rockorager/job/912193