-
-
-
-
- New Device
- {{ event.time | date:"yyyy-MM-dd HH:mm:ss" }}
-
-
-
-
-
- Device "{%name%}" ({%device%} at {%address%}) wants to connect. Add new device?
-
-
-
-
diff --git a/lib/config/config.go b/lib/config/config.go
index 2fcb6a74..0e497dde 100644
--- a/lib/config/config.go
+++ b/lib/config/config.go
@@ -130,8 +130,8 @@ type Configuration struct {
Devices []DeviceConfiguration `xml:"device" json:"devices"`
GUI GUIConfiguration `xml:"gui" json:"gui"`
Options OptionsConfiguration `xml:"options" json:"options"`
- IgnoredDevices []protocol.DeviceID `xml:"ignoredDevice" json:"ignoredDevices"`
- IgnoredFolders []string `xml:"ignoredFolder" json:"ignoredFolders"`
+ IgnoredDevices []ObservedDevice `xml:"remoteIgnoredDevice" json:"remoteIgnoredDevices"`
+ PendingDevices []ObservedDevice `xml:"pendingDevice" json:"pendingDevices"`
XMLName xml.Name `xml:"configuration" json:"-"`
MyID protocol.DeviceID `xml:"-" json:"-"` // Provided by the instantiator.
@@ -157,12 +157,11 @@ func (cfg Configuration) Copy() Configuration {
newCfg.GUI = cfg.GUI.Copy()
// DeviceIDs are values
- newCfg.IgnoredDevices = make([]protocol.DeviceID, len(cfg.IgnoredDevices))
+ newCfg.IgnoredDevices = make([]ObservedDevice, len(cfg.IgnoredDevices))
copy(newCfg.IgnoredDevices, cfg.IgnoredDevices)
- // FolderConfiguraion.ID is type string
- newCfg.IgnoredFolders = make([]string, len(cfg.IgnoredFolders))
- copy(newCfg.IgnoredFolders, cfg.IgnoredFolders)
+ newCfg.PendingDevices = make([]ObservedDevice, len(cfg.PendingDevices))
+ copy(newCfg.PendingDevices, cfg.PendingDevices)
return newCfg
}
@@ -213,28 +212,11 @@ found:
func (cfg *Configuration) clean() error {
util.FillNilSlices(&cfg.Options)
- // Initialize any empty slices
- if cfg.Folders == nil {
- cfg.Folders = []FolderConfiguration{}
- }
- if cfg.IgnoredDevices == nil {
- cfg.IgnoredDevices = []protocol.DeviceID{}
- }
- if cfg.IgnoredFolders == nil {
- cfg.IgnoredFolders = []string{}
- }
- if cfg.Options.AlwaysLocalNets == nil {
- cfg.Options.AlwaysLocalNets = []string{}
- }
- if cfg.Options.UnackedNotificationIDs == nil {
- cfg.Options.UnackedNotificationIDs = []string{}
- }
-
// Prepare folders and check for duplicates. Duplicates are bad and
// dangerous, can't currently be resolved in the GUI, and shouldn't
// happen when configured by the GUI. We return with an error in that
// situation.
- seenFolders := make(map[string]struct{})
+ existingFolders := make(map[string]*FolderConfiguration)
for i := range cfg.Folders {
folder := &cfg.Folders[i]
folder.prepare()
@@ -243,18 +225,10 @@ func (cfg *Configuration) clean() error {
return fmt.Errorf("folder with empty ID in configuration")
}
- if _, ok := seenFolders[folder.ID]; ok {
+ if _, ok := existingFolders[folder.ID]; ok {
return fmt.Errorf("duplicate folder ID %q in configuration", folder.ID)
}
- seenFolders[folder.ID] = struct{}{}
- }
-
- // Remove ignored folders that are anyway part of the configuration.
- for i := 0; i < len(cfg.IgnoredFolders); i++ {
- if _, ok := seenFolders[cfg.IgnoredFolders[i]]; ok {
- cfg.IgnoredFolders = append(cfg.IgnoredFolders[:i], cfg.IgnoredFolders[i+1:]...)
- i-- // IgnoredFolders[i] now points to something else, so needs to be rechecked
- }
+ existingFolders[folder.ID] = folder
}
cfg.Options.ListenAddresses = util.UniqueStrings(cfg.Options.ListenAddresses)
@@ -330,25 +304,36 @@ func (cfg *Configuration) clean() error {
// - free from duplicates
// - sorted by ID
cfg.Devices = ensureNoDuplicateDevices(cfg.Devices)
- sort.Sort(DeviceConfigurationList(cfg.Devices))
+ sort.Slice(cfg.Devices, func(a, b int) bool {
+ return cfg.Devices[a].DeviceID.Compare(cfg.Devices[b].DeviceID) == -1
+ })
// Ensure that the folder list is sorted by ID
- sort.Sort(FolderConfigurationList(cfg.Folders))
+ sort.Slice(cfg.Folders, func(a, b int) bool {
+ return cfg.Folders[a].ID < cfg.Folders[b].ID
+ })
+
// Ensure that in all folder configs
// - any loose devices are not present in the wrong places
// - there are no duplicate devices
// - the versioning configuration parameter map is not nil
+ sharedFolders := make(map[protocol.DeviceID][]string, len(cfg.Devices))
for i := range cfg.Folders {
cfg.Folders[i].Devices = ensureExistingDevices(cfg.Folders[i].Devices, existingDevices)
cfg.Folders[i].Devices = ensureNoDuplicateFolderDevices(cfg.Folders[i].Devices)
if cfg.Folders[i].Versioning.Params == nil {
cfg.Folders[i].Versioning.Params = map[string]string{}
}
- sort.Sort(FolderDeviceConfigurationList(cfg.Folders[i].Devices))
+ sort.Slice(cfg.Folders[i].Devices, func(a, b int) bool {
+ return cfg.Folders[i].Devices[a].DeviceID.Compare(cfg.Folders[i].Devices[b].DeviceID) == -1
+ })
+ for _, dev := range cfg.Folders[i].Devices {
+ sharedFolders[dev.DeviceID] = append(sharedFolders[dev.DeviceID], cfg.Folders[i].ID)
+ }
}
for i := range cfg.Devices {
- cfg.Devices[i].prepare()
+ cfg.Devices[i].prepare(sharedFolders[cfg.Devices[i].DeviceID])
}
// Very short reconnection intervals are annoying
@@ -362,14 +347,39 @@ func (cfg *Configuration) clean() error {
// The list of ignored devices should not contain any devices that have
// been manually added to the config.
- newIgnoredDevices := []protocol.DeviceID{}
+ var newIgnoredDevices []ObservedDevice
+ ignoredDevices := make(map[protocol.DeviceID]bool)
for _, dev := range cfg.IgnoredDevices {
- if !existingDevices[dev] {
+ if !existingDevices[dev.ID] {
+ ignoredDevices[dev.ID] = true
newIgnoredDevices = append(newIgnoredDevices, dev)
}
}
cfg.IgnoredDevices = newIgnoredDevices
+ // The list of pending devices should not contain devices that were added manually, nor should it contain
+ // ignored devices.
+
+ // Sort by time, so that in case of duplicates latest "time" is used.
+ sort.Slice(cfg.PendingDevices, func(i, j int) bool {
+ return cfg.PendingDevices[i].Time.Before(cfg.PendingDevices[j].Time)
+ })
+
+ var newPendingDevices []ObservedDevice
+nextPendingDevice:
+ for _, pendingDevice := range cfg.PendingDevices {
+ if !existingDevices[pendingDevice.ID] && !ignoredDevices[pendingDevice.ID] {
+ // Deduplicate
+ for _, existingPendingDevice := range newPendingDevices {
+ if existingPendingDevice.ID == pendingDevice.ID {
+ continue nextPendingDevice
+ }
+ }
+ newPendingDevices = append(newPendingDevices, pendingDevice)
+ }
+ }
+ cfg.PendingDevices = newPendingDevices
+
// Deprecated protocols are removed from the list of listeners and
// device addresses. So far just kcp*.
for _, prefix := range []string{"kcp"} {
@@ -380,6 +390,23 @@ func (cfg *Configuration) clean() error {
}
}
+ // Initialize any empty slices
+ if cfg.Folders == nil {
+ cfg.Folders = []FolderConfiguration{}
+ }
+ if cfg.IgnoredDevices == nil {
+ cfg.IgnoredDevices = []ObservedDevice{}
+ }
+ if cfg.PendingDevices == nil {
+ cfg.PendingDevices = []ObservedDevice{}
+ }
+ if cfg.Options.AlwaysLocalNets == nil {
+ cfg.Options.AlwaysLocalNets = []string{}
+ }
+ if cfg.Options.UnackedNotificationIDs == nil {
+ cfg.Options.UnackedNotificationIDs = []string{}
+ }
+
return nil
}
diff --git a/lib/config/config_test.go b/lib/config/config_test.go
index 74716692..dd994643 100644
--- a/lib/config/config_test.go
+++ b/lib/config/config_test.go
@@ -132,6 +132,8 @@ func TestDeviceConfig(t *testing.T) {
Addresses: []string{"tcp://a"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
{
DeviceID: device4,
@@ -139,6 +141,8 @@ func TestDeviceConfig(t *testing.T) {
Addresses: []string{"tcp://b"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
}
expectedDeviceIDs := []protocol.DeviceID{device1, device4}
@@ -230,16 +234,22 @@ func TestDeviceAddressesDynamic(t *testing.T) {
DeviceID: device1,
Addresses: []string{"dynamic"},
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device2: {
DeviceID: device2,
Addresses: []string{"dynamic"},
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device3: {
DeviceID: device3,
Addresses: []string{"dynamic"},
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device4: {
DeviceID: device4,
@@ -247,6 +257,8 @@ func TestDeviceAddressesDynamic(t *testing.T) {
Addresses: []string{"dynamic"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
}
@@ -269,18 +281,24 @@ func TestDeviceCompression(t *testing.T) {
Addresses: []string{"dynamic"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device2: {
DeviceID: device2,
Addresses: []string{"dynamic"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device3: {
DeviceID: device3,
Addresses: []string{"dynamic"},
Compression: protocol.CompressNever,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device4: {
DeviceID: device4,
@@ -288,6 +306,8 @@ func TestDeviceCompression(t *testing.T) {
Addresses: []string{"dynamic"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
}
@@ -309,16 +329,22 @@ func TestDeviceAddressesStatic(t *testing.T) {
DeviceID: device1,
Addresses: []string{"tcp://192.0.2.1", "tcp://192.0.2.2"},
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device2: {
DeviceID: device2,
Addresses: []string{"tcp://192.0.2.3:6070", "tcp://[2001:db8::42]:4242"},
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device3: {
DeviceID: device3,
Addresses: []string{"tcp://[2001:db8::44]:4444", "tcp://192.0.2.4:6090"},
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
device4: {
DeviceID: device4,
@@ -326,6 +352,8 @@ func TestDeviceAddressesStatic(t *testing.T) {
Addresses: []string{"dynamic"},
Compression: protocol.CompressMetadata,
AllowedNetworks: []string{},
+ IgnoredFolders: []ObservedFolder{},
+ PendingFolders: []ObservedFolder{},
},
}
@@ -560,8 +588,8 @@ func TestCopy(t *testing.T) {
t.Error("Config should have changed")
}
if !bytes.Equal(bsOrig, bsCopy) {
- //ioutil.WriteFile("a", bsOrig, 0644)
- //ioutil.WriteFile("b", bsCopy, 0644)
+ // ioutil.WriteFile("a", bsOrig, 0644)
+ // ioutil.WriteFile("b", bsCopy, 0644)
t.Error("Copy should be unchanged")
}
}
@@ -697,7 +725,6 @@ func TestEmptyFolderPaths(t *testing.T) {
func TestV14ListenAddressesMigration(t *testing.T) {
tcs := [][3][]string{
-
// Default listen plus default relays is now "default"
{
{"tcp://0.0.0.0:22000"},
@@ -710,7 +737,7 @@ func TestV14ListenAddressesMigration(t *testing.T) {
// config to start with...
{
{"tcp://0.0.0.0:22000"}, // old listen addrs
- {""}, // old relay addrs
+ {""}, // old relay addrs
{"tcp://0.0.0.0:22000"}, // new listen addrs
},
// Default listen plus non-default relays gets copied verbatim
@@ -770,6 +797,44 @@ func TestIgnoredDevices(t *testing.T) {
}
}
+func TestIgnoredFolders(t *testing.T) {
+ // Verify that ignored folder that are also present in the
+ // configuration are not in fact ignored.
+ // Also, verify that folders that are shared with a device are not ignored.
+
+ wrapper, err := Load("testdata/ignoredfolders.xml", device1)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if wrapper.IgnoredFolder(device2, "folder1") {
+ t.Errorf("Device %v should not be ignored", device2)
+ }
+ if !wrapper.IgnoredFolder(device3, "folder1") {
+ t.Errorf("Device %v should be ignored", device3)
+ }
+ // Should be removed, hence not ignored.
+ if wrapper.IgnoredFolder(device4, "folder1") {
+ t.Errorf("Device %v should not be ignored", device4)
+ }
+
+ if !wrapper.IgnoredFolder(device2, "folder2") {
+ t.Errorf("Device %v should not be ignored", device2)
+ }
+ if !wrapper.IgnoredFolder(device3, "folder2") {
+ t.Errorf("Device %v should be ignored", device3)
+ }
+
+ // 2 for folder2, 1 for folder1, as non-existing device and device the folder is shared with is removed.
+ expectedIgnoredFolders := 3
+ for _, dev := range wrapper.cfg.Devices {
+ expectedIgnoredFolders -= len(dev.IgnoredFolders)
+ }
+ if expectedIgnoredFolders != 0 {
+ t.Errorf("Left with %d ignored folders", expectedIgnoredFolders)
+ }
+}
+
func TestGetDevice(t *testing.T) {
// Verify that the Device() call does the right thing
@@ -827,10 +892,43 @@ func TestIssue4219(t *testing.T) {
// Adding a folder that was previously ignored should make it unignored.
r := bytes.NewReader([]byte(`{
- "folders": [
- {"id": "abcd123"}
+ "devices": [
+ {
+ "deviceID": "GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY",
+ "ignoredFolders": [
+ {
+ "id": "t1"
+ },
+ {
+ "id": "abcd123"
+ }
+ ]
+ },
+ {
+ "deviceID": "LGFPDIT-7SKNNJL-VJZA4FC-7QNCRKA-CE753K7-2BW5QDK-2FOZ7FR-FEP57QJ",
+ "ignoredFolders": [
+ {
+ "id": "t1"
+ },
+ {
+ "id": "abcd123"
+ }
+ ]
+ }
],
- "ignoredFolders": ["t1", "abcd123", "t2"]
+ "folders": [
+ {
+ "id": "abcd123",
+ "devices":[
+ {"deviceID": "GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY"}
+ ]
+ }
+ ],
+ "remoteIgnoredDevices": [
+ {
+ "deviceID": "GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY"
+ }
+ ]
}`))
cfg, err := ReadJSON(r, protocol.LocalDeviceID)
@@ -838,19 +936,31 @@ func TestIssue4219(t *testing.T) {
t.Fatal(err)
}
- if len(cfg.IgnoredFolders) != 2 {
- t.Errorf("There should be two ignored folders, not %d", len(cfg.IgnoredFolders))
+ if len(cfg.IgnoredDevices) != 0 { // 1 gets removed
+ t.Errorf("There should be zero ignored devices, not %d", len(cfg.IgnoredDevices))
+ }
+
+ ignoredFolders := 0
+ for _, dev := range cfg.Devices {
+ ignoredFolders += len(dev.IgnoredFolders)
+ }
+
+ if ignoredFolders != 3 { // 1 gets removed
+ t.Errorf("There should be three ignored folders, not %d", ignoredFolders)
}
w := Wrap("/tmp/cfg", cfg)
- if !w.IgnoredFolder("t1") {
- t.Error("Folder t1 should be ignored")
+ if !w.IgnoredFolder(device2, "t1") {
+ t.Error("Folder device2 t1 should be ignored")
}
- if !w.IgnoredFolder("t2") {
- t.Error("Folder t2 should be ignored")
+ if !w.IgnoredFolder(device3, "t1") {
+ t.Error("Folder device3 t1 should be ignored")
}
- if w.IgnoredFolder("abcd123") {
- t.Error("Folder abcd123 should not be ignored")
+ if w.IgnoredFolder(device2, "abcd123") {
+ t.Error("Folder device2 abcd123 should not be ignored")
+ }
+ if !w.IgnoredFolder(device3, "abcd123") {
+ t.Error("Folder device3 abcd123 should be ignored")
}
}
diff --git a/lib/config/deviceconfiguration.go b/lib/config/deviceconfiguration.go
index 2011a14b..1315c863 100644
--- a/lib/config/deviceconfiguration.go
+++ b/lib/config/deviceconfiguration.go
@@ -6,7 +6,11 @@
package config
-import "github.com/syncthing/syncthing/lib/protocol"
+import (
+ "sort"
+
+ "github.com/syncthing/syncthing/lib/protocol"
+)
type DeviceConfiguration struct {
DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
@@ -22,6 +26,8 @@ type DeviceConfiguration struct {
AutoAcceptFolders bool `xml:"autoAcceptFolders" json:"autoAcceptFolders"`
MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"`
MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"`
+ IgnoredFolders []ObservedFolder `xml:"ignoredFolder" json:"ignoredFolders"`
+ PendingFolders []ObservedFolder `xml:"pendingFolder" json:"pendingFolders"`
}
func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {
@@ -29,7 +35,7 @@ func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfigurati
DeviceID: id,
Name: name,
}
- d.prepare()
+ d.prepare(nil)
return d
}
@@ -39,28 +45,64 @@ func (cfg DeviceConfiguration) Copy() DeviceConfiguration {
copy(c.Addresses, cfg.Addresses)
c.AllowedNetworks = make([]string, len(cfg.AllowedNetworks))
copy(c.AllowedNetworks, cfg.AllowedNetworks)
+ c.IgnoredFolders = make([]ObservedFolder, len(cfg.IgnoredFolders))
+ copy(c.IgnoredFolders, cfg.IgnoredFolders)
+ c.PendingFolders = make([]ObservedFolder, len(cfg.PendingFolders))
+ copy(c.PendingFolders, cfg.PendingFolders)
return c
}
-func (cfg *DeviceConfiguration) prepare() {
+func (cfg *DeviceConfiguration) prepare(sharedFolders []string) {
if len(cfg.Addresses) == 0 || len(cfg.Addresses) == 1 && cfg.Addresses[0] == "" {
cfg.Addresses = []string{"dynamic"}
}
if len(cfg.AllowedNetworks) == 0 {
cfg.AllowedNetworks = []string{}
}
+
+ ignoredFolders := deduplicateObservedFoldersToMap(cfg.IgnoredFolders)
+ pendingFolders := deduplicateObservedFoldersToMap(cfg.PendingFolders)
+
+ for id := range ignoredFolders {
+ delete(pendingFolders, id)
+ }
+
+ for _, sharedFolder := range sharedFolders {
+ delete(ignoredFolders, sharedFolder)
+ delete(pendingFolders, sharedFolder)
+ }
+
+ cfg.IgnoredFolders = sortedObservedFolderSlice(ignoredFolders)
+ cfg.PendingFolders = sortedObservedFolderSlice(pendingFolders)
}
-type DeviceConfigurationList []DeviceConfiguration
-
-func (l DeviceConfigurationList) Less(a, b int) bool {
- return l[a].DeviceID.Compare(l[b].DeviceID) == -1
+func (cfg *DeviceConfiguration) IgnoredFolder(folder string) bool {
+ for _, ignoredFolder := range cfg.IgnoredFolders {
+ if ignoredFolder.ID == folder {
+ return true
+ }
+ }
+ return false
}
-func (l DeviceConfigurationList) Swap(a, b int) {
- l[a], l[b] = l[b], l[a]
+func sortedObservedFolderSlice(input map[string]ObservedFolder) []ObservedFolder {
+ output := make([]ObservedFolder, 0, len(input))
+ for _, folder := range input {
+ output = append(output, folder)
+ }
+ sort.Slice(output, func(i, j int) bool {
+ return output[i].Time.Before(output[j].Time)
+ })
+ return output
}
-func (l DeviceConfigurationList) Len() int {
- return len(l)
+func deduplicateObservedFoldersToMap(input []ObservedFolder) map[string]ObservedFolder {
+ output := make(map[string]ObservedFolder, len(input))
+ for _, folder := range input {
+ if existing, ok := output[folder.ID]; !ok || existing.Time.Before(folder.Time) {
+ output[folder.ID] = folder
+ }
+ }
+
+ return output
}
diff --git a/lib/config/folderconfiguration.go b/lib/config/folderconfiguration.go
index 2f06b695..fd3f5f04 100644
--- a/lib/config/folderconfiguration.go
+++ b/lib/config/folderconfiguration.go
@@ -268,20 +268,6 @@ func (f *FolderConfiguration) SharedWith(device protocol.DeviceID) bool {
return false
}
-type FolderDeviceConfigurationList []FolderDeviceConfiguration
-
-func (l FolderDeviceConfigurationList) Less(a, b int) bool {
- return l[a].DeviceID.Compare(l[b].DeviceID) == -1
-}
-
-func (l FolderDeviceConfigurationList) Swap(a, b int) {
- l[a], l[b] = l[b], l[a]
-}
-
-func (l FolderDeviceConfigurationList) Len() int {
- return len(l)
-}
-
func (f *FolderConfiguration) CheckAvailableSpace(req int64) error {
fs := f.Filesystem()
usage, err := fs.Usage(".")
@@ -296,17 +282,3 @@ func (f *FolderConfiguration) CheckAvailableSpace(req int64) error {
}
return fmt.Errorf("insufficient space in %v %v", fs.Type(), fs.URI())
}
-
-type FolderConfigurationList []FolderConfiguration
-
-func (l FolderConfigurationList) Len() int {
- return len(l)
-}
-
-func (l FolderConfigurationList) Less(a, b int) bool {
- return l[a].ID < l[b].ID
-}
-
-func (l FolderConfigurationList) Swap(a, b int) {
- l[a], l[b] = l[b], l[a]
-}
diff --git a/lib/config/observed.go b/lib/config/observed.go
new file mode 100644
index 00000000..afe2b044
--- /dev/null
+++ b/lib/config/observed.go
@@ -0,0 +1,26 @@
+// Copyright (C) 2018 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
+
+package config
+
+import (
+ "time"
+
+ "github.com/syncthing/syncthing/lib/protocol"
+)
+
+type ObservedFolder struct {
+ Time time.Time `xml:"time,attr" json:"time"`
+ ID string `xml:"id,attr" json:"id"`
+ Label string `xml:"label,attr" json:"label"`
+}
+
+type ObservedDevice struct {
+ Time time.Time `xml:"time,attr" json:"time"`
+ ID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
+ Name string `xml:"name,attr" json:"name"`
+ Address string `xml:"address,attr" json:"address"`
+}
diff --git a/lib/config/testdata/ignoreddevices.xml b/lib/config/testdata/ignoreddevices.xml
index f8d24889..e460726c 100644
--- a/lib/config/testdata/ignoreddevices.xml
+++ b/lib/config/testdata/ignoreddevices.xml
@@ -5,6 +5,6 @@
dynamic
-
AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR
-
LGFPDIT-7SKNNJL-VJZA4FC-7QNCRKA-CE753K7-2BW5QDK-2FOZ7FR-FEP57QJ
+
+
diff --git a/lib/config/testdata/ignoredfolders.xml b/lib/config/testdata/ignoredfolders.xml
new file mode 100644
index 00000000..5d0c3a66
--- /dev/null
+++ b/lib/config/testdata/ignoredfolders.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/config/wrapper.go b/lib/config/wrapper.go
index 17c1f5fd..4a666a70 100644
--- a/lib/config/wrapper.go
+++ b/lib/config/wrapper.go
@@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"sync/atomic"
+ "time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
@@ -326,7 +327,7 @@ func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool {
w.mut.Lock()
defer w.mut.Unlock()
for _, device := range w.cfg.IgnoredDevices {
- if device == id {
+ if device.ID == id {
return true
}
}
@@ -335,15 +336,12 @@ func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool {
// IgnoredFolder returns whether or not share attempts for the given
// folder should be silently ignored.
-func (w *Wrapper) IgnoredFolder(folder string) bool {
- w.mut.Lock()
- defer w.mut.Unlock()
- for _, nfolder := range w.cfg.IgnoredFolders {
- if folder == nfolder {
- return true
- }
+func (w *Wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool {
+ dev, ok := w.Device(device)
+ if !ok {
+ return false
}
- return false
+ return dev.IgnoredFolder(folder)
}
// Device returns the configuration for the given device and an "ok" bool.
@@ -442,6 +440,56 @@ func (w *Wrapper) MyName() string {
return cfg.Name
}
+func (w *Wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) {
+ defer w.Save()
+
+ w.mut.Lock()
+ defer w.mut.Unlock()
+
+ for i := range w.cfg.PendingDevices {
+ if w.cfg.PendingDevices[i].ID == device {
+ w.cfg.PendingDevices[i].Time = time.Now()
+ w.cfg.PendingDevices[i].Name = name
+ w.cfg.PendingDevices[i].Address = address
+ return
+ }
+ }
+
+ w.cfg.PendingDevices = append(w.cfg.PendingDevices, ObservedDevice{
+ Time: time.Now(),
+ ID: device,
+ Name: name,
+ Address: address,
+ })
+}
+
+func (w *Wrapper) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) {
+ defer w.Save()
+
+ w.mut.Lock()
+ defer w.mut.Unlock()
+
+ for _, dev := range w.cfg.Devices {
+ if dev.DeviceID == device {
+ for i := range dev.PendingFolders {
+ if dev.PendingFolders[i].ID == id {
+ dev.PendingFolders[i].Label = label
+ dev.PendingFolders[i].Time = time.Now()
+ return
+ }
+ }
+ dev.PendingFolders = append(dev.PendingFolders, ObservedFolder{
+ Time: time.Now(),
+ ID: id,
+ Label: label,
+ })
+ return
+ }
+ }
+
+ panic("bug: adding pending folder for non-existing device")
+}
+
// CheckHomeFreeSpace returns nil if the home disk has the required amount of
// free space, or if home disk free space checking is disabled.
func (w *Wrapper) CheckHomeFreeSpace() error {
diff --git a/lib/model/model.go b/lib/model/model.go
index fbad91b7..dbbd1892 100644
--- a/lib/model/model.go
+++ b/lib/model/model.go
@@ -347,9 +347,7 @@ func (m *Model) tearDownFolderLocked(cfg config.FolderConfiguration) {
// Must happen before stopping the folder service to abort ongoing
// transmissions and thus allow timely service termination.
for _, dev := range cfg.Devices {
- if conn, ok := m.conn[dev.DeviceID]; ok {
- closeRawConn(conn)
- }
+ m.closeLocked(dev.DeviceID)
}
// Stop the services running for this folder and wait for them to finish
@@ -935,10 +933,11 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
for _, folder := range cm.Folders {
cfg, ok := m.cfg.Folder(folder.ID)
if !ok || !cfg.SharedWith(deviceID) {
- if m.cfg.IgnoredFolder(folder.ID) {
+ if deviceCfg.IgnoredFolder(folder.ID) {
l.Infof("Ignoring folder %s from device %s since we are configured to", folder.Description(), deviceID)
continue
}
+ m.cfg.AddOrUpdatePendingFolder(folder.ID, folder.Label, deviceID)
events.Default.Log(events.FolderRejected, map[string]string{
"folder": folder.ID,
"folderLabel": folder.Label,
@@ -1545,6 +1544,7 @@ func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco
cfg, ok := m.cfg.Device(remoteID)
if !ok {
+ m.cfg.AddOrUpdatePendingDevice(remoteID, hello.DeviceName, addr.String())
events.Default.Log(events.DeviceRejected, map[string]string{
"name": hello.DeviceName,
"device": remoteID.String(),
@@ -2701,6 +2701,11 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
continue
}
+ // Ignored folder was removed, reconnect to retrigger the prompt.
+ if len(fromCfg.IgnoredFolders) > len(toCfg.IgnoredFolders) {
+ m.close(deviceID)
+ }
+
if toCfg.Paused {
l.Infoln("Pausing", deviceID)
m.close(deviceID)