aboutsummaryrefslogtreecommitdiff
path: root/pkg/config
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkg/config/url.go46
-rw-r--r--pkg/config/yaml.go45
-rw-r--r--pkg/config/yaml_test.go98
3 files changed, 177 insertions, 12 deletions
diff --git a/pkg/config/url.go b/pkg/config/url.go
index 39cce16..7e91411 100644
--- a/pkg/config/url.go
+++ b/pkg/config/url.go
@@ -4,6 +4,7 @@ import (
"fmt"
"net"
"net/url"
+ "strings"
"gopkg.in/yaml.v3"
)
@@ -63,7 +64,7 @@ func (u *Url) UnmarshalYAML(value *yaml.Node) (err error) {
return nil
}
-func (u *Url) String() string {
+func (u Url) String() string {
scheme := u.Scheme + "://"
var pwd string
@@ -86,12 +87,28 @@ func (u *Url) HostPort() string {
}
const (
- imapsPort = "993"
- imapPort = "143"
- imapsSchema = "imaps"
- imapSchema = "imap"
+ imapsPort = "993"
+ imapPort = "143"
+ imapsSchema = "imaps"
+ imapsSchemaFull = imapsSchema + "://"
+ imapSchema = "imap"
+ imapSchemaFull = imapSchema + "://"
+ maildirSchemaFull = "maildir://"
)
+func isRecognizedUrl(s string) bool {
+ return isImapUrl(s) || isMaildirUrl(s)
+
+}
+
+func isImapUrl(s string) bool {
+ return strings.HasPrefix(s, imapsSchemaFull) || strings.HasPrefix(s, imapSchemaFull)
+}
+
+func isMaildirUrl(s string) bool {
+ return strings.HasPrefix(s, maildirSchemaFull)
+}
+
func (u *Url) ForceTLS() bool {
return u.Scheme == imapsSchema || u.Port == imapsPort
}
@@ -132,3 +149,22 @@ func (u *Url) validate() (errors []string) {
return
}
+
+func (u Url) BaseUrl() Url {
+ // 'u' is not a pointer and thus a copy, modification is fine
+ u.Root = ""
+ return u
+}
+
+func (u *Url) CommonBaseUrl(other Url) bool {
+ other.Root = ""
+ return other == u.BaseUrl()
+}
+
+func (u *Url) RootPath() []string {
+ path := u.Root
+ if path[0] == '/' {
+ path = path[1:]
+ }
+ return strings.Split(path, "/")
+}
diff --git a/pkg/config/yaml.go b/pkg/config/yaml.go
index 48269ba..57991eb 100644
--- a/pkg/config/yaml.go
+++ b/pkg/config/yaml.go
@@ -118,7 +118,7 @@ func (cfg *Config) parse(in io.Reader) error {
cfg.fixGlobalOptions(parsedCfg.GlobalConfig)
- if err := buildFeeds(parsedCfg.Feeds, []string{}, cfg.Feeds, &cfg.FeedOptions, cfg.AutoTarget); err != nil {
+ if err := buildFeeds(parsedCfg.Feeds, []string{}, cfg.Feeds, &cfg.FeedOptions, cfg.AutoTarget, &cfg.Target); err != nil {
return fmt.Errorf("while parsing: %w", err)
}
@@ -182,12 +182,45 @@ func buildOptions(globalFeedOptions *Options, options Map) (feedOptions Options,
}
// Fetch the group structure and populate the `targetStr` fields in the feeds
-func buildFeeds(cfg []configGroupFeed, target []string, feeds Feeds, globalFeedOptions *Options, autoTarget bool) error {
+func buildFeeds(cfg []configGroupFeed, target []string, feeds Feeds,
+ globalFeedOptions *Options, autoTarget bool, globalTarget *Url) error {
+
for _, f := range cfg {
- target := appTarget(target, f.target(autoTarget))
+ var fTarget []string
+
+ rawTarget := f.target(autoTarget)
+ if isRecognizedUrl(rawTarget) {
+ // this whole block is solely for compatibility with old feed2imap
+ // there it was common to specify the whole URL for each feed
+ if isMaildirUrl(rawTarget) {
+ // old feed2imap supported maildir, we don't
+ return fmt.Errorf("Line %d: Maildir is not supported.", f.Target.Line)
+ }
+
+ url := Url{}
+ if err := url.UnmarshalYAML(&f.Target); err != nil {
+ return err
+ }
+
+ if globalTarget.Empty() {
+ // assign first feed as global url
+ *globalTarget = url.BaseUrl()
+ } else if !globalTarget.CommonBaseUrl(url) {
+ // if we have a url, it must be the same prefix as the global url
+ return fmt.Errorf("Line %d: Given URL endpoint '%s' does not match previous endpoint '%s'.",
+ f.Target.Line,
+ url.BaseUrl(),
+ globalTarget.BaseUrl())
+ }
+
+ fTarget = url.RootPath() // we are given the absolute path, so now appending trickery
+ } else {
+ fTarget = appTarget(target, rawTarget)
+ }
+
switch {
case f.isFeed() && f.isGroup():
- return fmt.Errorf("Entry with targetStr %s is both a Feed and a group", target)
+ return fmt.Errorf("Entry with targetStr %s is both a Feed and a group", fTarget)
case f.isFeed():
name := f.Feed.Name
@@ -211,7 +244,7 @@ func buildFeeds(cfg []configGroupFeed, target []string, feeds Feeds, globalFeedO
Url: f.Feed.Url,
Exec: f.Feed.Exec,
Options: opt,
- Target: target,
+ Target: fTarget,
}
case f.isGroup():
@@ -221,7 +254,7 @@ func buildFeeds(cfg []configGroupFeed, target []string, feeds Feeds, globalFeedO
log.Warnf("Unknown option '%s' for group '%s'. Ignored!", optName, f.Group.Group)
}
- if err := buildFeeds(f.Group.Feeds, target, feeds, &opt, autoTarget); err != nil {
+ if err := buildFeeds(f.Group.Feeds, fTarget, feeds, &opt, autoTarget, globalTarget); err != nil {
return err
}
}
diff --git a/pkg/config/yaml_test.go b/pkg/config/yaml_test.go
index e649175..78a3243 100644
--- a/pkg/config/yaml_test.go
+++ b/pkg/config/yaml_test.go
@@ -148,6 +148,29 @@ func TestBuildFeeds(tst *testing.T) {
"bar": &Feed{Name: "bar", Target: t("moep.bar")},
},
},
+ {name: "URL Target", wantErr: false, target: "",
+ feeds: []configGroupFeed{
+ {Target: n("imap://foo.bar:443/INBOX/Feed"), Feed: feed{Name: "muh"}},
+ },
+ result: Feeds{"muh": &Feed{Name: "muh", Target: t("INBOX.Feed")}},
+ },
+ {name: "Multiple URL Targets", wantErr: false, target: "",
+ feeds: []configGroupFeed{
+ {Target: n("imap://foo.bar:443/INBOX/Feed"), Feed: feed{Name: "muh"}},
+ {Target: n("imap://foo.bar:443/INBOX/Feed2"), Feed: feed{Name: "bar"}},
+ },
+ result: Feeds{
+ "muh": &Feed{Name: "muh", Target: t("INBOX.Feed")},
+ "bar": &Feed{Name: "bar", Target: t("INBOX.Feed2")},
+ },
+ },
+ {name: "Mixed URL Targets", wantErr: true, target: "",
+ feeds: []configGroupFeed{
+ {Target: n("imap://foo.bar:443/INBOX/Feed"), Feed: feed{Name: "muh"}},
+ {Target: n("imap://other.bar:443/INBOX/Feed"), Feed: feed{Name: "bar"}},
+ },
+ result: Feeds{},
+ },
{name: "Empty Group", wantErr: false, target: "",
feeds: []configGroupFeed{
{Group: group{Group: "G1"}},
@@ -206,7 +229,8 @@ func TestBuildFeeds(tst *testing.T) {
tst.Run(tt.name, func(tst *testing.T) {
var feeds = Feeds{}
var opts = Options{}
- err := buildFeeds(tt.feeds, t(tt.target), feeds, &opts, !tt.noAutoTarget)
+ var globalTarget = Url{}
+ err := buildFeeds(tt.feeds, t(tt.target), feeds, &opts, !tt.noAutoTarget, &globalTarget)
if (err != nil) != tt.wantErr {
tst.Errorf("buildFeeds() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -447,3 +471,75 @@ feeds:
}
}
}
+
+func TestURLFeedWithoutGlobalTarget(tst *testing.T) {
+ inp := `
+feeds:
+ - name: Foo
+ target: imap://foo.bar:443/INBOX/Feed
+`
+ res := Feeds{
+ "Foo": &Feed{Name: "Foo", Target: t("INBOX.Feed")},
+ }
+
+ c := WithDefault()
+ c.FeedOptions = Options{}
+
+ if err := c.parse(strings.NewReader(inp)); err != nil {
+ tst.Error(err)
+ } else {
+ if diff := cmp.Diff(res, c.Feeds); diff != "" {
+ tst.Error(diff)
+ }
+ if diff := cmp.Diff("imap://foo.bar:443", c.Target.String()); diff != "" {
+ tst.Error(diff)
+ }
+ }
+}
+
+func TestURLFeedWithGlobalTarget(tst *testing.T) {
+ inp := `
+target: imaps://foo.bar/INBOX/Feeds
+feeds:
+ - name: Foo
+ target: imaps://foo.bar:993/Some/Other/Path
+`
+ res := Feeds{
+ "Foo": &Feed{Name: "Foo", Target: t("Some.Other.Path")},
+ }
+
+ c := WithDefault()
+ c.FeedOptions = Options{}
+
+ if err := c.parse(strings.NewReader(inp)); err != nil {
+ tst.Error(err)
+ } else {
+ if diff := cmp.Diff(res, c.Feeds); diff != "" {
+ tst.Error(diff)
+ }
+ if diff := cmp.Diff("imaps://foo.bar:993/INBOX/Feeds", c.Target.String()); diff != "" {
+ tst.Error(diff)
+ }
+ }
+}
+
+func TestURLFeedWithDifferentGlobalTarget(tst *testing.T) {
+ inp := `
+target: imaps://foo.bar/INBOX/Feeds
+feeds:
+ - name: Foo
+ target: imaps://other.bar/INBOX/Feeds
+`
+ errorText := "while parsing: Line 5: Given URL endpoint 'imaps://other.bar:993' does not match previous endpoint 'imaps://foo.bar:993'."
+ c := WithDefault()
+ c.FeedOptions = Options{}
+
+ err := c.parse(strings.NewReader(inp))
+ if err == nil {
+ tst.Error("Expected error.")
+ } else {
+ if diff := cmp.Diff(errorText, err.Error()); diff != "" {
+ tst.Error(diff)
+ }
+ }
+} \ No newline at end of file