浏览代码

Revert "lib/model: Introducer can remove stuff it introduced (fixes #1015)"

This reverts commit 0b88cf1d03f8b96caa4d606eeb78c833cc496ee4.
Jakob Borg 9 年之前
父节点
当前提交
59f3d1445f
共有 6 个文件被更改,包括 191 次插入725 次删除
  1. 6 8
      lib/config/deviceconfiguration.go
  2. 1 2
      lib/config/folderconfiguration.go
  3. 64 188
      lib/model/model.go
  4. 1 371
      lib/model/model_test.go
  5. 111 147
      lib/protocol/bep.pb.go
  6. 8 9
      lib/protocol/bep.proto

+ 6 - 8
lib/config/deviceconfiguration.go

@@ -9,14 +9,12 @@ package config
 import "github.com/syncthing/syncthing/lib/protocol"
 import "github.com/syncthing/syncthing/lib/protocol"
 
 
 type DeviceConfiguration struct {
 type DeviceConfiguration struct {
-	DeviceID                 protocol.DeviceID    `xml:"id,attr" json:"deviceID"`
-	Name                     string               `xml:"name,attr,omitempty" json:"name"`
-	Addresses                []string             `xml:"address,omitempty" json:"addresses"`
-	Compression              protocol.Compression `xml:"compression,attr" json:"compression"`
-	CertName                 string               `xml:"certName,attr,omitempty" json:"certName"`
-	Introducer               bool                 `xml:"introducer,attr" json:"introducer"`
-	SkipIntroductionRemovals bool                 `xml:"skipIntroductionRemovals,attr" json:"skipIntroductionRemovals"`
-	IntroducedBy             protocol.DeviceID    `xml:"introducedBy,attr" json:"introducedBy"`
+	DeviceID    protocol.DeviceID    `xml:"id,attr" json:"deviceID"`
+	Name        string               `xml:"name,attr,omitempty" json:"name"`
+	Addresses   []string             `xml:"address,omitempty" json:"addresses"`
+	Compression protocol.Compression `xml:"compression,attr" json:"compression"`
+	CertName    string               `xml:"certName,attr,omitempty" json:"certName"`
+	Introducer  bool                 `xml:"introducer,attr" json:"introducer"`
 }
 }
 
 
 func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {
 func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {

+ 1 - 2
lib/config/folderconfiguration.go

@@ -45,8 +45,7 @@ type FolderConfiguration struct {
 }
 }
 
 
 type FolderDeviceConfiguration struct {
 type FolderDeviceConfiguration struct {
-	DeviceID     protocol.DeviceID `xml:"id,attr" json:"deviceID"`
-	IntroducedBy protocol.DeviceID `xml:"introducedBy,attr" json:"introducedBy"`
+	DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
 }
 }
 
 
 func NewFolderConfiguration(id, path string) FolderConfiguration {
 func NewFolderConfiguration(id, path string) FolderConfiguration {

+ 64 - 188
lib/model/model.go

@@ -83,7 +83,7 @@ type Model struct {
 
 
 	folderCfgs         map[string]config.FolderConfiguration                  // folder -> cfg
 	folderCfgs         map[string]config.FolderConfiguration                  // folder -> cfg
 	folderFiles        map[string]*db.FileSet                                 // folder -> files
 	folderFiles        map[string]*db.FileSet                                 // folder -> files
-	folderDevices      folderDeviceSet                                        // folder -> deviceIDs
+	folderDevices      map[string][]protocol.DeviceID                         // folder -> deviceIDs
 	deviceFolders      map[protocol.DeviceID][]string                         // deviceID -> folders
 	deviceFolders      map[protocol.DeviceID][]string                         // deviceID -> folders
 	deviceStatRefs     map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef
 	deviceStatRefs     map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef
 	folderIgnores      map[string]*ignore.Matcher                             // folder -> matcher object
 	folderIgnores      map[string]*ignore.Matcher                             // folder -> matcher object
@@ -144,7 +144,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
 		clientVersion:      clientVersion,
 		clientVersion:      clientVersion,
 		folderCfgs:         make(map[string]config.FolderConfiguration),
 		folderCfgs:         make(map[string]config.FolderConfiguration),
 		folderFiles:        make(map[string]*db.FileSet),
 		folderFiles:        make(map[string]*db.FileSet),
-		folderDevices:      make(folderDeviceSet),
+		folderDevices:      make(map[string][]protocol.DeviceID),
 		deviceFolders:      make(map[protocol.DeviceID][]string),
 		deviceFolders:      make(map[protocol.DeviceID][]string),
 		deviceStatRefs:     make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
 		deviceStatRefs:     make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
 		folderIgnores:      make(map[string]*ignore.Matcher),
 		folderIgnores:      make(map[string]*ignore.Matcher),
@@ -303,8 +303,9 @@ func (m *Model) addFolderLocked(cfg config.FolderConfiguration) {
 	m.folderCfgs[cfg.ID] = cfg
 	m.folderCfgs[cfg.ID] = cfg
 	m.folderFiles[cfg.ID] = db.NewFileSet(cfg.ID, m.db)
 	m.folderFiles[cfg.ID] = db.NewFileSet(cfg.ID, m.db)
 
 
-	for _, device := range cfg.Devices {
-		m.folderDevices.set(device.DeviceID, cfg.ID)
+	m.folderDevices[cfg.ID] = make([]protocol.DeviceID, len(cfg.Devices))
+	for i, device := range cfg.Devices {
+		m.folderDevices[cfg.ID][i] = device.DeviceID
 		m.deviceFolders[device.DeviceID] = append(m.deviceFolders[device.DeviceID], cfg.ID)
 		m.deviceFolders[device.DeviceID] = append(m.deviceFolders[device.DeviceID], cfg.ID)
 	}
 	}
 
 
@@ -334,7 +335,7 @@ func (m *Model) tearDownFolderLocked(folder string) {
 	}
 	}
 
 
 	// Close connections to affected devices
 	// Close connections to affected devices
-	for dev := range m.folderDevices[folder] {
+	for _, dev := range m.folderDevices[folder] {
 		if conn, ok := m.conn[dev]; ok {
 		if conn, ok := m.conn[dev]; ok {
 			closeRawConn(conn)
 			closeRawConn(conn)
 		}
 		}
@@ -871,175 +872,78 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 		}
 		}
 	}
 	}
 
 
-	var changed = false
-	if deviceCfg := m.cfg.Devices()[deviceID]; deviceCfg.Introducer {
-		foldersDevices, introduced := m.handleIntroductions(deviceCfg, cm)
-		if introduced {
-			changed = true
-		}
-		// If permitted, check if the introducer has unshare devices/folders with
-		// some of the devices/folders that we know were introduced to us by him.
-		if !deviceCfg.SkipIntroductionRemovals && m.handleDeintroductions(deviceCfg, cm, foldersDevices) {
-			changed = true
-		}
-	}
-
-	if changed {
-		if err := m.cfg.Save(); err != nil {
-			l.Warnln("Failed to save config", err)
-		}
-	}
-}
+	var changed bool
 
 
-// handleIntroductions handles adding devices/shares that are shared by an introducer device
-func (m *Model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (folderDeviceSet, bool) {
-	// This device is an introducer. Go through the announced lists of folders
-	// and devices and add what we are missing, remove what we have extra that
-	// has been introducer by the introducer.
-	changed := false
+	if m.cfg.Devices()[deviceID].Introducer {
+		// This device is an introducer. Go through the announced lists of folders
+		// and devices and add what we are missing.
 
 
-	foldersDevices := make(folderDeviceSet)
-
-	for _, folder := range cm.Folders {
-		// We don't have this folder, skip.
-		if _, ok := m.folderDevices[folder.ID]; !ok {
-			continue
-		}
-
-		// Adds devices which we do not have, but the introducer has
-		// for the folders that we have in common. Also, shares folders
-		// with devices that we have in common, yet are currently not sharing
-		// the folder.
-	nextDevice:
-		for _, device := range folder.Devices {
-			foldersDevices.set(device.ID, folder.ID)
-
-			if _, ok := m.cfg.Devices()[device.ID]; !ok {
-				// The device is currently unknown. Add it to the config.
-				m.introduceDevice(device, introducerCfg)
-				changed = true
+		for _, folder := range cm.Folders {
+			if _, ok := m.folderDevices[folder.ID]; !ok {
+				continue
 			}
 			}
 
 
-			for _, er := range m.deviceFolders[device.ID] {
-				if er == folder.ID {
-					// We already share the folder with this device, so
-					// nothing to do.
-					continue nextDevice
-				}
-			}
+		nextDevice:
+			for _, device := range folder.Devices {
+				if _, ok := m.cfg.Devices()[device.ID]; !ok {
+					// The device is currently unknown. Add it to the config.
 
 
-			// We don't yet share this folder with this device. Add the device
-			// to sharing list of the folder.
-			m.introduceDeviceToFolder(device, folder, introducerCfg)
-			changed = true
-		}
-	}
+					addresses := []string{"dynamic"}
+					for _, addr := range device.Addresses {
+						if addr != "dynamic" {
+							addresses = append(addresses, addr)
+						}
+					}
 
 
-	return foldersDevices, changed
-}
+					l.Infof("Adding device %v to config (vouched for by introducer %v)", device.ID, deviceID)
+					newDeviceCfg := config.DeviceConfiguration{
+						DeviceID:    device.ID,
+						Name:        device.Name,
+						Compression: m.cfg.Devices()[deviceID].Compression,
+						Addresses:   addresses,
+						CertName:    device.CertName,
+					}
 
 
-// handleIntroductions handles removals of devices/shares that are removed by an introducer device
-func (m *Model) handleDeintroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, foldersDevices folderDeviceSet) bool {
-	changed := false
-	foldersIntroducedByOthers := make(folderDeviceSet)
+					// The introducers' introducers are also our introducers.
+					if device.Introducer {
+						l.Infof("Device %v is now also an introducer", device.ID)
+						newDeviceCfg.Introducer = true
+					}
 
 
-	// Check if we should unshare some folders, if the introducer has unshared them.
-	for _, folderCfg := range m.cfg.Folders() {
-		folderChanged := false
-		for i := 0; i < len(folderCfg.Devices); i++ {
-			if folderCfg.Devices[i].IntroducedBy == introducerCfg.DeviceID {
-				if !foldersDevices.has(folderCfg.Devices[i].DeviceID, folderCfg.ID) {
-					// We could not find that folder shared on the introducer with the device that was introduced to us.
-					// We should follow and unshare aswell.
-					l.Infof("Unsharing folder %q with %v as introducer %v no longer shares the folder with that device", folderCfg.ID, folderCfg.Devices[i].DeviceID, folderCfg.Devices[i].IntroducedBy)
-					folderCfg.Devices = append(folderCfg.Devices[:i], folderCfg.Devices[i+1:]...)
-					i--
-					folderChanged = true
+					m.cfg.SetDevice(newDeviceCfg)
+					changed = true
 				}
 				}
-			} else {
-				foldersIntroducedByOthers.set(folderCfg.Devices[i].DeviceID, folderCfg.ID)
-			}
-		}
-
-		// We've modified the folder, hence update it.
-		if folderChanged {
-			m.cfg.SetFolder(folderCfg)
-			changed = true
-		}
-	}
 
 
-	// Check if we should remove some devices, if the introducer no longer shares any folder with them.
-	// Yet do not remove if we share other folders that haven't been introduced by the introducer.
-	raw := m.cfg.Raw()
-	deviceChanged := false
-	for i := 0; i < len(raw.Devices); i++ {
-		if raw.Devices[i].IntroducedBy == introducerCfg.DeviceID {
-			if !foldersDevices.hasDevice(raw.Devices[i].DeviceID) {
-				if foldersIntroducedByOthers.hasDevice(raw.Devices[i].DeviceID) {
-					l.Infof("Would have removed %v as %v no longer shares any folders, yet there are other folders that are shared with this device that haven't been introduced by this introducer.", raw.Devices[i].DeviceID, raw.Devices[i].IntroducedBy)
-					continue
+				for _, er := range m.deviceFolders[device.ID] {
+					if er == folder.ID {
+						// We already share the folder with this device, so
+						// nothing to do.
+						continue nextDevice
+					}
 				}
 				}
-				// The introducer no longer shares any folder with the device, remove the device.
-				l.Infof("Removing device %v as introducer %v no longer shares any folders with that device", raw.Devices[i].DeviceID, raw.Devices[i].IntroducedBy)
-				raw.Devices = append(raw.Devices[:i], raw.Devices[i+1:]...)
-				i--
-				deviceChanged = true
-			}
-		}
-	}
 
 
-	// We've removed a device, replace the config.
-	if deviceChanged {
-		if err := m.cfg.Replace(raw); err != nil {
-			l.Warnln("Failed to save config", err)
-		}
-		changed = true
-	}
+				// We don't yet share this folder with this device. Add the device
+				// to sharing list of the folder.
 
 
-	return changed
+				l.Infof("Adding device %v to share %q (vouched for by introducer %v)", device.ID, folder.ID, deviceID)
 
 
-}
+				m.deviceFolders[device.ID] = append(m.deviceFolders[device.ID], folder.ID)
+				m.folderDevices[folder.ID] = append(m.folderDevices[folder.ID], device.ID)
 
 
-func (m *Model) introduceDevice(device protocol.Device, introducerCfg config.DeviceConfiguration) {
-	addresses := []string{"dynamic"}
-	for _, addr := range device.Addresses {
-		if addr != "dynamic" {
-			addresses = append(addresses, addr)
-		}
-	}
+				folderCfg := m.cfg.Folders()[folder.ID]
+				folderCfg.Devices = append(folderCfg.Devices, config.FolderDeviceConfiguration{
+					DeviceID: device.ID,
+				})
+				m.cfg.SetFolder(folderCfg)
 
 
-	l.Infof("Adding device %v to config (vouched for by introducer %v)", device.ID, introducerCfg.DeviceID)
-	newDeviceCfg := config.DeviceConfiguration{
-		DeviceID:     device.ID,
-		Name:         device.Name,
-		Compression:  introducerCfg.Compression,
-		Addresses:    addresses,
-		CertName:     device.CertName,
-		IntroducedBy: introducerCfg.DeviceID,
+				changed = true
+			}
+		}
 	}
 	}
 
 
-	// The introducers' introducers are also our introducers.
-	if device.Introducer {
-		l.Infof("Device %v is now also an introducer", device.ID)
-		newDeviceCfg.Introducer = true
-		newDeviceCfg.SkipIntroductionRemovals = device.SkipIntroductionRemovals
+	if changed {
+		m.cfg.Save()
 	}
 	}
-
-	m.cfg.SetDevice(newDeviceCfg)
-}
-
-func (m *Model) introduceDeviceToFolder(device protocol.Device, folder protocol.Folder, introducerCfg config.DeviceConfiguration) {
-	l.Infof("Sharing folder %q with %v (vouched for by introducer %v)", folder.ID, device.ID, introducerCfg.DeviceID)
-
-	m.deviceFolders[device.ID] = append(m.deviceFolders[device.ID], folder.ID)
-	m.folderDevices.set(device.ID, folder.ID)
-
-	folderCfg := m.cfg.Folders()[folder.ID]
-	folderCfg.Devices = append(folderCfg.Devices, config.FolderDeviceConfiguration{
-		DeviceID:     device.ID,
-		IntroducedBy: introducerCfg.DeviceID,
-	})
-	m.cfg.SetFolder(folderCfg)
 }
 }
 
 
 // Closed is called when a connection has been closed
 // Closed is called when a connection has been closed
@@ -1565,6 +1469,7 @@ func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo)
 	m.fmut.RLock()
 	m.fmut.RLock()
 	folderCfg := m.folderCfgs[folder]
 	folderCfg := m.folderCfgs[folder]
 	m.fmut.RUnlock()
 	m.fmut.RUnlock()
+
 	// Fire the LocalChangeDetected event to notify listeners about local updates.
 	// Fire the LocalChangeDetected event to notify listeners about local updates.
 	m.localChangeDetected(folderCfg, fs)
 	m.localChangeDetected(folderCfg, fs)
 }
 }
@@ -1971,7 +1876,7 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
 			DisableTempIndexes: folderCfg.DisableTempIndexes,
 			DisableTempIndexes: folderCfg.DisableTempIndexes,
 		}
 		}
 
 
-		for device := range m.folderDevices[folder] {
+		for _, device := range m.folderDevices[folder] {
 			// DeviceID is a value type, but with an underlying array. Copy it
 			// DeviceID is a value type, but with an underlying array. Copy it
 			// so we don't grab aliases to the same array later on in device[:]
 			// so we don't grab aliases to the same array later on in device[:]
 			device := device
 			device := device
@@ -2094,8 +1999,8 @@ func (m *Model) RemoteSequence(folder string) (int64, bool) {
 	}
 	}
 
 
 	var ver int64
 	var ver int64
-	for device := range m.folderDevices[folder] {
-		ver += fs.Sequence(device)
+	for _, n := range m.folderDevices[folder] {
+		ver += fs.Sequence(n)
 	}
 	}
 
 
 	return ver, true
 	return ver, true
@@ -2189,7 +2094,7 @@ func (m *Model) Availability(folder, file string, version protocol.Vector, block
 		}
 		}
 	}
 	}
 
 
-	for device := range devices {
+	for _, device := range devices {
 		if m.deviceDownloads[device].Has(folder, file, version, int32(block.Offset/protocol.BlockSize)) {
 		if m.deviceDownloads[device].Has(folder, file, version, int32(block.Offset/protocol.BlockSize)) {
 			availabilities = append(availabilities, Availability{ID: device, FromTemporary: true})
 			availabilities = append(availabilities, Availability{ID: device, FromTemporary: true})
 		}
 		}
@@ -2575,32 +2480,3 @@ func shouldIgnore(file db.FileIntf, matcher *ignore.Matcher, ignoreDelete bool)
 
 
 	return false
 	return false
 }
 }
-
-// folderDeviceSet is a set of (folder, deviceID) pairs
-type folderDeviceSet map[string]map[protocol.DeviceID]struct{}
-
-// set adds the (dev, folder) pair to the set
-func (s folderDeviceSet) set(dev protocol.DeviceID, folder string) {
-	devs, ok := s[folder]
-	if !ok {
-		devs = make(map[protocol.DeviceID]struct{})
-		s[folder] = devs
-	}
-	devs[dev] = struct{}{}
-}
-
-// has returns true if the (dev, folder) pair is in the set
-func (s folderDeviceSet) has(dev protocol.DeviceID, folder string) bool {
-	_, ok := s[folder][dev]
-	return ok
-}
-
-// hasDevice returns true if the device is set on any folder
-func (s folderDeviceSet) hasDevice(dev protocol.DeviceID) bool {
-	for _, devices := range s {
-		if _, ok := devices[dev]; ok {
-			return true
-		}
-	}
-	return false
-}

+ 1 - 371
lib/model/model_test.go

@@ -478,376 +478,6 @@ func TestClusterConfig(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestIntroducer(t *testing.T) {
-	var introducedByAnyone protocol.DeviceID
-
-	// LocalDeviceID is a magic value meaning don't check introducer
-	contains := func(cfg config.FolderConfiguration, id, introducedBy protocol.DeviceID) bool {
-		for _, dev := range cfg.Devices {
-			if dev.DeviceID.Equals(id) {
-				if introducedBy.Equals(introducedByAnyone) {
-					return true
-				}
-				return introducedBy.Equals(introducedBy)
-			}
-		}
-		return false
-	}
-
-	newState := func(cfg config.Configuration) (*config.Wrapper, *Model) {
-		db := db.OpenMemory()
-
-		wcfg := config.Wrap("/tmp/test", cfg)
-
-		m := NewModel(wcfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
-		for _, folder := range cfg.Folders {
-			m.AddFolder(folder)
-		}
-		m.ServeBackground()
-		m.AddConnection(connections.Connection{
-			IntermediateConnection: connections.IntermediateConnection{
-				Conn: tls.Client(&fakeConn{}, nil),
-			},
-			Connection: &FakeConnection{
-				id: device1,
-			},
-		}, protocol.HelloResult{})
-		return wcfg, m
-	}
-
-	wcfg, m := newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:   device1,
-				Introducer: true,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{
-		Folders: []protocol.Folder{
-			{
-				ID: "folder1",
-				Devices: []protocol.Device{
-					{
-						ID:                       device2,
-						Introducer:               true,
-						SkipIntroductionRemovals: true,
-					},
-				},
-			},
-		},
-	})
-
-	if newDev, ok := wcfg.Device(device2); !ok || !newDev.Introducer || !newDev.SkipIntroductionRemovals {
-		t.Error("devie 2 missing or wrong flags")
-	}
-
-	if !contains(wcfg.Folders()["folder1"], device2, device1) {
-		t.Error("expected folder 1 to have device2 introduced by device 1")
-	}
-
-	wcfg, m = newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:   device1,
-				Introducer: true,
-			},
-			{
-				DeviceID:     device2,
-				IntroducedBy: device1,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{
-		Folders: []protocol.Folder{
-			{
-				ID: "folder2",
-				Devices: []protocol.Device{
-					{
-						ID:                       device2,
-						Introducer:               true,
-						SkipIntroductionRemovals: true,
-					},
-				},
-			},
-		},
-	})
-
-	// Should not get introducer, as it's already unset, and it's an existing device.
-	if newDev, ok := wcfg.Device(device2); !ok || newDev.Introducer || newDev.SkipIntroductionRemovals {
-		t.Error("device 2 missing or changed flags")
-	}
-
-	if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
-		t.Error("expected device 2 to be removed from folder 1")
-	}
-
-	if !contains(wcfg.Folders()["folder2"], device2, device1) {
-		t.Error("expected device 2 to be added to folder 2")
-	}
-
-	wcfg, m = newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:   device1,
-				Introducer: true,
-			},
-			{
-				DeviceID:     device2,
-				IntroducedBy: device1,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{})
-
-	if _, ok := wcfg.Device(device2); ok {
-		t.Error("device 2 should have been removed")
-	}
-
-	if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
-		t.Error("expected device 2 to be removed from folder 1")
-	}
-
-	if contains(wcfg.Folders()["folder2"], device2, introducedByAnyone) {
-		t.Error("expected device 2 to be removed from folder 2")
-	}
-
-	// Two cases when removals should not happen
-	// 1. Introducer flag no longer set on device
-
-	wcfg, m = newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:   device1,
-				Introducer: false,
-			},
-			{
-				DeviceID:     device2,
-				IntroducedBy: device1,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{})
-
-	if _, ok := wcfg.Device(device2); !ok {
-		t.Error("device 2 should not have been removed")
-	}
-
-	if !contains(wcfg.Folders()["folder1"], device2, device1) {
-		t.Error("expected device 2 not to be removed from folder 1")
-	}
-
-	if !contains(wcfg.Folders()["folder2"], device2, device1) {
-		t.Error("expected device 2 not to be removed from folder 2")
-	}
-
-	// 2. SkipIntroductionRemovals is set
-
-	wcfg, m = newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:                 device1,
-				Introducer:               true,
-				SkipIntroductionRemovals: true,
-			},
-			{
-				DeviceID:     device2,
-				IntroducedBy: device1,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{
-		Folders: []protocol.Folder{
-			{
-				ID: "folder2",
-				Devices: []protocol.Device{
-					{
-						ID:                       device2,
-						Introducer:               true,
-						SkipIntroductionRemovals: true,
-					},
-				},
-			},
-		},
-	})
-
-	if _, ok := wcfg.Device(device2); !ok {
-		t.Error("device 2 should not have been removed")
-	}
-
-	if !contains(wcfg.Folders()["folder1"], device2, device1) {
-		t.Error("expected device 2 not to be removed from folder 1")
-	}
-
-	if !contains(wcfg.Folders()["folder2"], device2, device1) {
-		t.Error("expected device 2 not to be added to folder 2")
-	}
-
-	// Test device not being removed as it's shared without an introducer.
-
-	wcfg, m = newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:   device1,
-				Introducer: true,
-			},
-			{
-				DeviceID:     device2,
-				IntroducedBy: device1,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{})
-
-	if _, ok := wcfg.Device(device2); !ok {
-		t.Error("device 2 should not have been removed")
-	}
-
-	if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
-		t.Error("expected device 2 to be removed from folder 1")
-	}
-
-	if !contains(wcfg.Folders()["folder2"], device2, introducedByAnyone) {
-		t.Error("expected device 2 not to be removed from folder 2")
-	}
-
-	// Test device not being removed as it's shared by a different introducer.
-
-	wcfg, m = newState(config.Configuration{
-		Devices: []config.DeviceConfiguration{
-			{
-				DeviceID:   device1,
-				Introducer: true,
-			},
-			{
-				DeviceID:     device2,
-				IntroducedBy: device1,
-			},
-		},
-		Folders: []config.FolderConfiguration{
-			{
-				ID: "folder1",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: device1},
-				},
-			},
-			{
-				ID: "folder2",
-				Devices: []config.FolderDeviceConfiguration{
-					{DeviceID: device1},
-					{DeviceID: device2, IntroducedBy: protocol.LocalDeviceID},
-				},
-			},
-		},
-	})
-	m.ClusterConfig(device1, protocol.ClusterConfig{})
-
-	if _, ok := wcfg.Device(device2); !ok {
-		t.Error("device 2 should not have been removed")
-	}
-
-	if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
-		t.Error("expected device 2 to be removed from folder 1")
-	}
-
-	if !contains(wcfg.Folders()["folder2"], device2, introducedByAnyone) {
-		t.Error("expected device 2 not to be removed from folder 2")
-	}
-}
-
 func TestIgnores(t *testing.T) {
 func TestIgnores(t *testing.T) {
 	arrEqual := func(a, b []string) bool {
 	arrEqual := func(a, b []string) bool {
 		if len(a) != len(b) {
 		if len(a) != len(b) {
@@ -1993,7 +1623,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
 		t.Error("folder missing?")
 		t.Error("folder missing?")
 	}
 	}
 
 
-	for id := range fdevs {
+	for _, id := range fdevs {
 		if id == device2 {
 		if id == device2 {
 			t.Error("still there")
 			t.Error("still there")
 		}
 		}

+ 111 - 147
lib/protocol/bep.pb.go

@@ -255,15 +255,14 @@ func (*Folder) ProtoMessage()               {}
 func (*Folder) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{3} }
 func (*Folder) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{3} }
 
 
 type Device struct {
 type Device struct {
-	ID                       DeviceID    `protobuf:"bytes,1,opt,name=id,proto3,customtype=DeviceID" json:"id"`
-	Name                     string      `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
-	Addresses                []string    `protobuf:"bytes,3,rep,name=addresses" json:"addresses,omitempty"`
-	Compression              Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression,omitempty"`
-	CertName                 string      `protobuf:"bytes,5,opt,name=cert_name,json=certName,proto3" json:"cert_name,omitempty"`
-	MaxSequence              int64       `protobuf:"varint,6,opt,name=max_sequence,json=maxSequence,proto3" json:"max_sequence,omitempty"`
-	Introducer               bool        `protobuf:"varint,7,opt,name=introducer,proto3" json:"introducer,omitempty"`
-	IndexID                  IndexID     `protobuf:"varint,8,opt,name=index_id,json=indexId,proto3,customtype=IndexID" json:"index_id"`
-	SkipIntroductionRemovals bool        `protobuf:"varint,9,opt,name=skip_introduction_removals,json=skipIntroductionRemovals,proto3" json:"skip_introduction_removals,omitempty"`
+	ID          DeviceID    `protobuf:"bytes,1,opt,name=id,proto3,customtype=DeviceID" json:"id"`
+	Name        string      `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Addresses   []string    `protobuf:"bytes,3,rep,name=addresses" json:"addresses,omitempty"`
+	Compression Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression,omitempty"`
+	CertName    string      `protobuf:"bytes,5,opt,name=cert_name,json=certName,proto3" json:"cert_name,omitempty"`
+	MaxSequence int64       `protobuf:"varint,6,opt,name=max_sequence,json=maxSequence,proto3" json:"max_sequence,omitempty"`
+	Introducer  bool        `protobuf:"varint,7,opt,name=introducer,proto3" json:"introducer,omitempty"`
+	IndexID     IndexID     `protobuf:"varint,8,opt,name=index_id,json=indexId,proto3,customtype=IndexID" json:"index_id"`
 }
 }
 
 
 func (m *Device) Reset()                    { *m = Device{} }
 func (m *Device) Reset()                    { *m = Device{} }
@@ -682,16 +681,6 @@ func (m *Device) MarshalTo(data []byte) (int, error) {
 		i++
 		i++
 		i = encodeVarintBep(data, i, uint64(m.IndexID))
 		i = encodeVarintBep(data, i, uint64(m.IndexID))
 	}
 	}
-	if m.SkipIntroductionRemovals {
-		data[i] = 0x48
-		i++
-		if m.SkipIntroductionRemovals {
-			data[i] = 1
-		} else {
-			data[i] = 0
-		}
-		i++
-	}
 	return i, nil
 	return i, nil
 }
 }
 
 
@@ -1314,9 +1303,6 @@ func (m *Device) ProtoSize() (n int) {
 	if m.IndexID != 0 {
 	if m.IndexID != 0 {
 		n += 1 + sovBep(uint64(m.IndexID))
 		n += 1 + sovBep(uint64(m.IndexID))
 	}
 	}
-	if m.SkipIntroductionRemovals {
-		n += 2
-	}
 	return n
 	return n
 }
 }
 
 
@@ -2296,26 +2282,6 @@ func (m *Device) Unmarshal(data []byte) error {
 					break
 					break
 				}
 				}
 			}
 			}
-		case 9:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SkipIntroductionRemovals", wireType)
-			}
-			var v int
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowBep
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := data[iNdEx]
-				iNdEx++
-				v |= (int(b) & 0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			m.SkipIntroductionRemovals = bool(v != 0)
 		default:
 		default:
 			iNdEx = preIndex
 			iNdEx = preIndex
 			skippy, err := skipBep(data[iNdEx:])
 			skippy, err := skipBep(data[iNdEx:])
@@ -3987,110 +3953,108 @@ var (
 )
 )
 
 
 var fileDescriptorBep = []byte{
 var fileDescriptorBep = []byte{
-	// 1665 bytes of a gzipped FileDescriptorProto
+	// 1635 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x73, 0xdb, 0xc6,
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x73, 0xdb, 0xc6,
-	0x15, 0x17, 0x48, 0xf0, 0xdf, 0x23, 0xa5, 0x40, 0x6b, 0x59, 0x41, 0x61, 0x85, 0x42, 0xe0, 0xb8,
-	0x55, 0x34, 0x8d, 0xe2, 0xc6, 0x69, 0x33, 0xd3, 0x69, 0x3b, 0x43, 0x91, 0x90, 0xcc, 0x09, 0x0d,
-	0x32, 0x4b, 0xca, 0xae, 0x73, 0x28, 0x06, 0x24, 0x96, 0x14, 0xc6, 0x20, 0x96, 0x05, 0x40, 0xd9,
-	0xea, 0x47, 0x60, 0xbf, 0x40, 0x2f, 0x9c, 0xc9, 0xf4, 0xd6, 0x7b, 0x3f, 0x84, 0x8f, 0x99, 0x1c,
-	0x7b, 0xf0, 0x34, 0xea, 0xa5, 0xc7, 0x5e, 0x7a, 0xef, 0x60, 0x17, 0x00, 0x41, 0xfd, 0xe9, 0xe4,
-	0x90, 0x13, 0x77, 0xdf, 0xfb, 0xed, 0xdb, 0x7d, 0xbf, 0xf7, 0x7e, 0x8f, 0x80, 0xca, 0x90, 0xcc,
-	0x8e, 0x66, 0x3e, 0x0d, 0x29, 0x2a, 0xb3, 0x9f, 0x11, 0x75, 0x95, 0x4f, 0x26, 0x4e, 0x78, 0x3e,
-	0x1f, 0x1e, 0x8d, 0xe8, 0xf4, 0xd3, 0x09, 0x9d, 0xd0, 0x4f, 0x99, 0x67, 0x38, 0x1f, 0xb3, 0x1d,
-	0xdb, 0xb0, 0x15, 0x3f, 0xa8, 0xcd, 0xa0, 0xf0, 0x94, 0xb8, 0x2e, 0x45, 0xfb, 0x50, 0xb5, 0xc9,
-	0x85, 0x33, 0x22, 0xa6, 0x67, 0x4d, 0x89, 0x2c, 0xa8, 0xc2, 0x41, 0x05, 0x03, 0x37, 0x19, 0xd6,
-	0x94, 0x44, 0x80, 0x91, 0xeb, 0x10, 0x2f, 0xe4, 0x80, 0x1c, 0x07, 0x70, 0x13, 0x03, 0x3c, 0x82,
-	0xad, 0x18, 0x70, 0x41, 0xfc, 0xc0, 0xa1, 0x9e, 0x9c, 0x67, 0x98, 0x4d, 0x6e, 0x7d, 0xce, 0x8d,
-	0x5a, 0x00, 0xc5, 0xa7, 0xc4, 0xb2, 0x89, 0x8f, 0x3e, 0x06, 0x31, 0xbc, 0x9c, 0xf1, 0xbb, 0xb6,
-	0x3e, 0xbb, 0x7f, 0x94, 0xe4, 0x70, 0xf4, 0x8c, 0x04, 0x81, 0x35, 0x21, 0x83, 0xcb, 0x19, 0xc1,
-	0x0c, 0x82, 0x7e, 0x07, 0xd5, 0x11, 0x9d, 0xce, 0x7c, 0x12, 0xb0, 0xc0, 0x39, 0x76, 0x62, 0xef,
-	0xc6, 0x89, 0xe6, 0x0a, 0x83, 0xb3, 0x07, 0xb4, 0x06, 0x6c, 0x36, 0xdd, 0x79, 0x10, 0x12, 0xbf,
-	0x49, 0xbd, 0xb1, 0x33, 0x41, 0x8f, 0xa1, 0x34, 0xa6, 0xae, 0x4d, 0xfc, 0x40, 0x16, 0xd4, 0xfc,
-	0x41, 0xf5, 0x33, 0x69, 0x15, 0xec, 0x84, 0x39, 0x8e, 0xc5, 0xb7, 0xef, 0xf6, 0x37, 0x70, 0x02,
-	0xd3, 0xfe, 0x9c, 0x83, 0x22, 0xf7, 0xa0, 0x5d, 0xc8, 0x39, 0x36, 0xa7, 0xe8, 0xb8, 0x78, 0xf5,
-	0x6e, 0x3f, 0xd7, 0x6e, 0xe1, 0x9c, 0x63, 0xa3, 0x1d, 0x28, 0xb8, 0xd6, 0x90, 0xb8, 0x31, 0x39,
-	0x7c, 0x83, 0x1e, 0x40, 0xc5, 0x27, 0x96, 0x6d, 0x52, 0xcf, 0xbd, 0x64, 0x94, 0x94, 0x71, 0x39,
-	0x32, 0x74, 0x3d, 0xf7, 0x12, 0x7d, 0x02, 0xc8, 0x99, 0x78, 0xd4, 0x27, 0xe6, 0x8c, 0xf8, 0x53,
-	0x87, 0xbd, 0x36, 0x90, 0x45, 0x86, 0xda, 0xe6, 0x9e, 0xde, 0xca, 0x81, 0x1e, 0xc2, 0x66, 0x0c,
-	0xb7, 0x89, 0x4b, 0x42, 0x22, 0x17, 0x18, 0xb2, 0xc6, 0x8d, 0x2d, 0x66, 0x43, 0x8f, 0x61, 0xc7,
-	0x76, 0x02, 0x6b, 0xe8, 0x12, 0x33, 0x24, 0xd3, 0x99, 0xe9, 0x78, 0x36, 0x79, 0x43, 0x02, 0xb9,
-	0xc8, 0xb0, 0x28, 0xf6, 0x0d, 0xc8, 0x74, 0xd6, 0xe6, 0x9e, 0x88, 0x0d, 0x5e, 0xe9, 0x40, 0x96,
-	0xae, 0xb3, 0xd1, 0x62, 0x8e, 0x84, 0x8d, 0x18, 0xa6, 0xfd, 0x27, 0x07, 0x45, 0xee, 0x41, 0x3f,
-	0x4d, 0xd9, 0xa8, 0x1d, 0xef, 0x46, 0xa8, 0x7f, 0xbc, 0xdb, 0x2f, 0x73, 0x5f, 0xbb, 0x95, 0x61,
-	0x07, 0x81, 0x98, 0xe9, 0x1c, 0xb6, 0x46, 0x7b, 0x50, 0xb1, 0x6c, 0x3b, 0xaa, 0x12, 0x09, 0xe4,
-	0xbc, 0x9a, 0x3f, 0xa8, 0xe0, 0x95, 0x01, 0x7d, 0xb1, 0x5e, 0x75, 0xf1, 0x7a, 0x9f, 0xdc, 0x55,
-	0xee, 0x88, 0xf2, 0x11, 0xf1, 0xe3, 0x4e, 0x2d, 0xb0, 0xfb, 0xca, 0x91, 0x81, 0xf5, 0xe9, 0x87,
-	0x50, 0x9b, 0x5a, 0x6f, 0xcc, 0x80, 0xfc, 0x71, 0x4e, 0xbc, 0x11, 0x61, 0xb4, 0xe4, 0x71, 0x75,
-	0x6a, 0xbd, 0xe9, 0xc7, 0x26, 0x54, 0x07, 0x70, 0xbc, 0xd0, 0xa7, 0xf6, 0x7c, 0x44, 0x7c, 0xb9,
-	0xc4, 0x78, 0xcb, 0x58, 0xd0, 0x2f, 0xa1, 0xcc, 0x48, 0x35, 0x1d, 0x5b, 0x2e, 0xab, 0xc2, 0x81,
-	0x78, 0xac, 0xc4, 0x89, 0x97, 0x18, 0xa5, 0x2c, 0xef, 0x64, 0x89, 0x4b, 0x0c, 0xdb, 0xb6, 0xd1,
-	0x6f, 0x40, 0x09, 0x5e, 0x39, 0x51, 0x41, 0x78, 0xa4, 0xd0, 0xa1, 0x9e, 0xe9, 0x93, 0x29, 0xbd,
-	0xb0, 0xdc, 0x40, 0xae, 0xb0, 0x6b, 0xe4, 0x08, 0xd1, 0xce, 0x00, 0x70, 0xec, 0xd7, 0xba, 0x50,
-	0x60, 0x11, 0xd1, 0x2e, 0x14, 0x79, 0x53, 0xc6, 0x2a, 0x8d, 0x77, 0xe8, 0x08, 0x0a, 0x63, 0xc7,
-	0x25, 0x81, 0x9c, 0x63, 0x35, 0x44, 0x99, 0x8e, 0x76, 0x5c, 0xd2, 0xf6, 0xc6, 0x34, 0xae, 0x22,
-	0x87, 0x69, 0x67, 0x50, 0x65, 0x01, 0xcf, 0x66, 0xb6, 0x15, 0x92, 0x1f, 0x2d, 0xec, 0x5f, 0xf3,
-	0x50, 0x4e, 0x3c, 0x69, 0xd1, 0x85, 0x4c, 0xd1, 0x0f, 0x63, 0xdd, 0x73, 0x15, 0xef, 0xde, 0x8c,
-	0x97, 0x11, 0x3e, 0x02, 0x31, 0x70, 0xfe, 0x44, 0x98, 0x6e, 0xf2, 0x98, 0xad, 0x91, 0x0a, 0xd5,
-	0xeb, 0x62, 0xd9, 0xc4, 0x59, 0x13, 0xfa, 0x00, 0x60, 0x4a, 0x6d, 0x67, 0xec, 0x10, 0xdb, 0x0c,
-	0x58, 0x03, 0xe4, 0x71, 0x25, 0xb1, 0xf4, 0x91, 0x1c, 0xb5, 0x7b, 0x24, 0x15, 0x3b, 0xd6, 0x44,
-	0xb2, 0x8d, 0x3c, 0x8e, 0x77, 0x61, 0xb9, 0x8e, 0x1d, 0x57, 0x3d, 0xd9, 0x46, 0xd3, 0xcd, 0xa3,
-	0x6b, 0x22, 0x2d, 0x33, 0xc0, 0xa6, 0x47, 0xb3, 0x02, 0x7d, 0x0c, 0xa5, 0x64, 0xfa, 0x45, 0xf5,
-	0x5c, 0x53, 0xd2, 0x73, 0x32, 0x0a, 0x69, 0x3a, 0x57, 0x62, 0x18, 0x52, 0xa0, 0x9c, 0xb6, 0x22,
-	0xb0, 0x97, 0xa6, 0xfb, 0x68, 0xe6, 0xa6, 0x79, 0x78, 0x81, 0x5c, 0x55, 0x85, 0x83, 0x02, 0x4e,
-	0x53, 0x33, 0x02, 0xf4, 0x0b, 0x28, 0x1e, 0xbb, 0x74, 0xf4, 0x2a, 0xd1, 0xed, 0xbd, 0xd5, 0x6d,
-	0xcc, 0x9e, 0xa9, 0x4e, 0x71, 0xc8, 0x80, 0xbf, 0x16, 0xff, 0xf2, 0xcd, 0xfe, 0x86, 0xf6, 0x15,
-	0x54, 0x52, 0x40, 0x54, 0x79, 0x3a, 0x1e, 0x07, 0x24, 0x64, 0x65, 0xca, 0xe3, 0x78, 0x97, 0x92,
-	0x9f, 0x63, 0xf7, 0x72, 0xf2, 0x11, 0x88, 0xe7, 0x56, 0x70, 0xce, 0x0a, 0x52, 0xc3, 0x6c, 0x1d,
-	0x87, 0xfc, 0x2d, 0x14, 0x79, 0x86, 0xe8, 0x09, 0x94, 0x47, 0x74, 0xee, 0x85, 0xab, 0xe9, 0xba,
-	0x9d, 0x15, 0x2d, 0xf3, 0xc4, 0xaf, 0x4a, 0x81, 0xda, 0x09, 0x94, 0x62, 0x17, 0x7a, 0x94, 0x4e,
-	0x14, 0xf1, 0xf8, 0x7e, 0x22, 0xac, 0xfe, 0x39, 0xf5, 0xc3, 0xb5, 0x81, 0xb2, 0x03, 0x85, 0x0b,
-	0xcb, 0x9d, 0xf3, 0xf7, 0x89, 0x98, 0x6f, 0xb4, 0xbf, 0x0b, 0x50, 0xc2, 0x11, 0x81, 0x41, 0x98,
-	0x19, 0xd4, 0x85, 0xb5, 0x41, 0xbd, 0x6a, 0xf5, 0xdc, 0x5a, 0xab, 0x27, 0xdd, 0x9a, 0xcf, 0x74,
-	0xeb, 0x8a, 0x1c, 0xf1, 0x56, 0x72, 0x0a, 0xb7, 0x90, 0x53, 0x5c, 0x91, 0x13, 0x35, 0xce, 0xd8,
-	0xa7, 0x53, 0x36, 0x8a, 0xa9, 0x6f, 0xf9, 0x97, 0x71, 0x67, 0x6d, 0x46, 0xd6, 0x41, 0x62, 0xd4,
-	0x4c, 0x28, 0x63, 0x12, 0xcc, 0xa8, 0x17, 0x90, 0x3b, 0x9f, 0x8d, 0x40, 0xb4, 0xad, 0xd0, 0x62,
-	0x8f, 0xae, 0x61, 0xb6, 0x46, 0x3f, 0x03, 0x71, 0x44, 0x6d, 0xfe, 0xe4, 0xad, 0x6c, 0xfd, 0x75,
-	0xdf, 0xa7, 0x7e, 0x93, 0xda, 0x04, 0x33, 0x80, 0x36, 0x03, 0xa9, 0x45, 0x5f, 0x7b, 0x2e, 0xb5,
-	0xec, 0x9e, 0x4f, 0x27, 0xd1, 0xa8, 0xbc, 0x53, 0xf2, 0x2d, 0x28, 0xcd, 0xd9, 0x50, 0x48, 0x44,
-	0xff, 0xd1, 0xba, 0x48, 0xaf, 0x07, 0xe2, 0x13, 0x24, 0xe9, 0xec, 0xf8, 0xa8, 0xf6, 0x9d, 0x00,
-	0xca, 0xdd, 0x68, 0xd4, 0x86, 0x2a, 0x47, 0x9a, 0x99, 0xaf, 0x80, 0x83, 0x1f, 0x72, 0x11, 0x9b,
-	0x0f, 0x30, 0x4f, 0xd7, 0xb7, 0xfe, 0xb5, 0x64, 0x94, 0x98, 0xff, 0x61, 0x4a, 0x7c, 0x08, 0x9b,
-	0x4c, 0x23, 0xe9, 0x1f, 0xa6, 0xa8, 0xe6, 0x0f, 0x0a, 0xb8, 0x36, 0xe4, 0x42, 0x61, 0x36, 0xad,
-	0x08, 0x62, 0xcf, 0xf1, 0x26, 0xda, 0x3e, 0x14, 0x9a, 0x2e, 0x65, 0xc5, 0x2a, 0xfa, 0xc4, 0x0a,
-	0xa8, 0x97, 0x70, 0xc8, 0x77, 0x87, 0xdf, 0xe5, 0xa0, 0x9a, 0xf9, 0x90, 0x41, 0x8f, 0x61, 0xab,
-	0xd9, 0x39, 0xeb, 0x0f, 0x74, 0x6c, 0x36, 0xbb, 0xc6, 0x49, 0xfb, 0x54, 0xda, 0x50, 0xf6, 0x16,
-	0x4b, 0x55, 0x9e, 0xae, 0x40, 0xeb, 0xdf, 0x28, 0xfb, 0x50, 0x68, 0x1b, 0x2d, 0xfd, 0xf7, 0x92,
-	0xa0, 0xec, 0x2c, 0x96, 0xaa, 0x94, 0x01, 0xf2, 0x3f, 0x82, 0x9f, 0x43, 0x8d, 0x01, 0xcc, 0xb3,
-	0x5e, 0xab, 0x31, 0xd0, 0xa5, 0x9c, 0xa2, 0x2c, 0x96, 0xea, 0xee, 0x75, 0x5c, 0xcc, 0xf7, 0x43,
-	0x28, 0x61, 0xfd, 0xab, 0x33, 0xbd, 0x3f, 0x90, 0xf2, 0xca, 0xee, 0x62, 0xa9, 0xa2, 0x0c, 0x30,
-	0x51, 0xcc, 0x23, 0x28, 0x63, 0xbd, 0xdf, 0xeb, 0x1a, 0x7d, 0x5d, 0x12, 0x95, 0xf7, 0x17, 0x4b,
-	0xf5, 0xde, 0x1a, 0x2a, 0xee, 0xd0, 0x5f, 0xc1, 0x76, 0xab, 0xfb, 0xc2, 0xe8, 0x74, 0x1b, 0x2d,
-	0xb3, 0x87, 0xbb, 0xa7, 0x58, 0xef, 0xf7, 0xa5, 0x82, 0xb2, 0xbf, 0x58, 0xaa, 0x0f, 0x32, 0xf8,
-	0x1b, 0x0d, 0xf7, 0x01, 0x88, 0xbd, 0xb6, 0x71, 0x2a, 0x15, 0x95, 0x7b, 0x8b, 0xa5, 0xfa, 0x5e,
-	0x06, 0x1a, 0x91, 0x1a, 0x65, 0xdc, 0xec, 0x74, 0xfb, 0xba, 0x54, 0xba, 0x91, 0x31, 0x23, 0xfb,
-	0xf0, 0x0f, 0x80, 0x6e, 0x7e, 0xea, 0xa1, 0x8f, 0x40, 0x34, 0xba, 0x86, 0x2e, 0x6d, 0xf0, 0xfc,
-	0x6f, 0x22, 0x0c, 0xea, 0x11, 0xa4, 0x41, 0xbe, 0xf3, 0xf5, 0xe7, 0x92, 0xa0, 0xfc, 0x64, 0xb1,
-	0x54, 0xef, 0xdf, 0x04, 0x75, 0xbe, 0xfe, 0xfc, 0x90, 0x42, 0x35, 0x1b, 0x58, 0x83, 0xf2, 0x33,
-	0x7d, 0xd0, 0x68, 0x35, 0x06, 0x0d, 0x69, 0x83, 0x3f, 0x29, 0x71, 0x3f, 0x23, 0xa1, 0xc5, 0x04,
-	0xb8, 0x07, 0x05, 0x43, 0x7f, 0xae, 0x63, 0x49, 0x50, 0xb6, 0x17, 0x4b, 0x75, 0x33, 0x01, 0x18,
-	0xe4, 0x82, 0xf8, 0xa8, 0x0e, 0xc5, 0x46, 0xe7, 0x45, 0xe3, 0x65, 0x5f, 0xca, 0x29, 0x68, 0xb1,
-	0x54, 0xb7, 0x12, 0x77, 0xc3, 0x7d, 0x6d, 0x5d, 0x06, 0x87, 0xff, 0x15, 0xa0, 0x96, 0xfd, 0xdb,
-	0x43, 0x75, 0x10, 0x4f, 0xda, 0x1d, 0x3d, 0xb9, 0x2e, 0xeb, 0x8b, 0xd6, 0xe8, 0x00, 0x2a, 0xad,
-	0x36, 0xd6, 0x9b, 0x83, 0x2e, 0x7e, 0x99, 0xe4, 0x92, 0x05, 0xb5, 0x1c, 0x9f, 0x35, 0x77, 0xf4,
-	0x69, 0x59, 0xeb, 0xbf, 0x7c, 0xd6, 0x69, 0x1b, 0x5f, 0x9a, 0x2c, 0x62, 0x4e, 0x79, 0xb0, 0x58,
-	0xaa, 0xef, 0x67, 0xc1, 0xfd, 0xcb, 0xa9, 0xeb, 0x78, 0xaf, 0x58, 0xe0, 0x2f, 0x60, 0x3b, 0x81,
-	0xaf, 0x2e, 0xc8, 0x2b, 0xea, 0x62, 0xa9, 0xee, 0xdd, 0x72, 0x66, 0x75, 0xcf, 0x13, 0x78, 0x2f,
-	0x39, 0x78, 0x66, 0x7c, 0x69, 0x74, 0x5f, 0x18, 0x92, 0xa8, 0xd4, 0x17, 0x4b, 0x55, 0xb9, 0xe5,
-	0xd8, 0x99, 0xf7, 0xca, 0xa3, 0xaf, 0xbd, 0xc3, 0xbf, 0x09, 0x50, 0x49, 0x27, 0x54, 0xc4, 0xb3,
-	0xd1, 0x35, 0x75, 0x8c, 0xbb, 0x38, 0x49, 0x3c, 0x75, 0x1a, 0x94, 0x2d, 0xd1, 0x87, 0x50, 0x3a,
-	0xd5, 0x0d, 0x1d, 0xb7, 0x9b, 0x89, 0x1e, 0x52, 0xc8, 0x29, 0xf1, 0x88, 0xef, 0x8c, 0xd0, 0xc7,
-	0x50, 0x33, 0xba, 0x66, 0xff, 0xac, 0xf9, 0x34, 0xc9, 0x98, 0x35, 0x70, 0x26, 0x54, 0x7f, 0x3e,
-	0x3a, 0x67, 0xd9, 0x1e, 0x46, 0xd2, 0x79, 0xde, 0xe8, 0xb4, 0x5b, 0x1c, 0x9a, 0x57, 0xe4, 0xc5,
-	0x52, 0xdd, 0x49, 0xa1, 0x6d, 0xfe, 0xb7, 0x1f, 0x61, 0x0f, 0x6d, 0xa8, 0xff, 0xff, 0x59, 0x84,
-	0x54, 0x28, 0x36, 0x7a, 0x3d, 0xdd, 0x68, 0x25, 0xaf, 0x5f, 0xf9, 0x1a, 0xb3, 0x19, 0xf1, 0xec,
-	0x08, 0x71, 0xd2, 0xc5, 0xa7, 0xfa, 0x20, 0x79, 0xfc, 0x0a, 0x71, 0x42, 0xfd, 0x09, 0x09, 0x8f,
-	0xf7, 0xde, 0x7e, 0x5f, 0xdf, 0xf8, 0xf6, 0xfb, 0xfa, 0xc6, 0xdb, 0xab, 0xba, 0xf0, 0xed, 0x55,
-	0x5d, 0xf8, 0xe7, 0x55, 0x7d, 0xe3, 0xdf, 0x57, 0x75, 0xe1, 0x9b, 0x7f, 0xd5, 0x85, 0x61, 0x91,
-	0xcd, 0xae, 0x27, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x64, 0x17, 0x1e, 0x19, 0xf4, 0x0d, 0x00,
-	0x00,
+	0x15, 0x17, 0x48, 0x10, 0x24, 0x1f, 0x29, 0x05, 0x5a, 0xcb, 0x0a, 0x0b, 0x2b, 0x14, 0x02, 0xc7,
+	0xad, 0xa2, 0x69, 0x14, 0x37, 0x4e, 0x9b, 0x99, 0xce, 0xb4, 0x33, 0x14, 0x09, 0xc9, 0x9c, 0xd0,
+	0x20, 0xb3, 0xa4, 0xec, 0x3a, 0x87, 0x62, 0x40, 0x62, 0x49, 0x61, 0x0c, 0x62, 0x59, 0x00, 0x94,
+	0xad, 0x7e, 0x04, 0xf6, 0x0b, 0xf4, 0xc2, 0x99, 0x4c, 0x6f, 0xbd, 0x76, 0xfa, 0x21, 0x7c, 0xcc,
+	0xe4, 0xd8, 0x83, 0xa7, 0x51, 0x2f, 0xfd, 0x02, 0xbd, 0x77, 0xb0, 0x0b, 0x80, 0xa0, 0xfe, 0x74,
+	0x7c, 0xe8, 0x89, 0xbb, 0xef, 0xfd, 0xf6, 0xed, 0xbe, 0xdf, 0xfb, 0xbd, 0x07, 0x42, 0x79, 0x48,
+	0x66, 0x47, 0x33, 0x9f, 0x86, 0x14, 0x95, 0xd8, 0xcf, 0x88, 0xba, 0xca, 0x67, 0x13, 0x27, 0x3c,
+	0x9f, 0x0f, 0x8f, 0x46, 0x74, 0xfa, 0xf9, 0x84, 0x4e, 0xe8, 0xe7, 0xcc, 0x33, 0x9c, 0x8f, 0xd9,
+	0x8e, 0x6d, 0xd8, 0x8a, 0x1f, 0xd4, 0x66, 0x50, 0x78, 0x4a, 0x5c, 0x97, 0xa2, 0x7d, 0xa8, 0xd8,
+	0xe4, 0xc2, 0x19, 0x11, 0xd3, 0xb3, 0xa6, 0xa4, 0x26, 0xa8, 0xc2, 0x41, 0x19, 0x03, 0x37, 0x19,
+	0xd6, 0x94, 0x44, 0x80, 0x91, 0xeb, 0x10, 0x2f, 0xe4, 0x80, 0x1c, 0x07, 0x70, 0x13, 0x03, 0x3c,
+	0x82, 0xad, 0x18, 0x70, 0x41, 0xfc, 0xc0, 0xa1, 0x5e, 0x2d, 0xcf, 0x30, 0x9b, 0xdc, 0xfa, 0x9c,
+	0x1b, 0xb5, 0x00, 0xa4, 0xa7, 0xc4, 0xb2, 0x89, 0x8f, 0x3e, 0x05, 0x31, 0xbc, 0x9c, 0xf1, 0xbb,
+	0xb6, 0xbe, 0xb8, 0x7f, 0x94, 0xe4, 0x70, 0xf4, 0x8c, 0x04, 0x81, 0x35, 0x21, 0x83, 0xcb, 0x19,
+	0xc1, 0x0c, 0x82, 0x7e, 0x0b, 0x95, 0x11, 0x9d, 0xce, 0x7c, 0x12, 0xb0, 0xc0, 0x39, 0x76, 0x62,
+	0xef, 0xc6, 0x89, 0xe6, 0x0a, 0x83, 0xb3, 0x07, 0xb4, 0x06, 0x6c, 0x36, 0xdd, 0x79, 0x10, 0x12,
+	0xbf, 0x49, 0xbd, 0xb1, 0x33, 0x41, 0x8f, 0xa1, 0x38, 0xa6, 0xae, 0x4d, 0xfc, 0xa0, 0x26, 0xa8,
+	0xf9, 0x83, 0xca, 0x17, 0xf2, 0x2a, 0xd8, 0x09, 0x73, 0x1c, 0x8b, 0x6f, 0xdf, 0xed, 0x6f, 0xe0,
+	0x04, 0xa6, 0xfd, 0x29, 0x07, 0x12, 0xf7, 0xa0, 0x5d, 0xc8, 0x39, 0x36, 0xa7, 0xe8, 0x58, 0xba,
+	0x7a, 0xb7, 0x9f, 0x6b, 0xb7, 0x70, 0xce, 0xb1, 0xd1, 0x0e, 0x14, 0x5c, 0x6b, 0x48, 0xdc, 0x98,
+	0x1c, 0xbe, 0x41, 0x0f, 0xa0, 0xec, 0x13, 0xcb, 0x36, 0xa9, 0xe7, 0x5e, 0x32, 0x4a, 0x4a, 0xb8,
+	0x14, 0x19, 0xba, 0x9e, 0x7b, 0x89, 0x3e, 0x03, 0xe4, 0x4c, 0x3c, 0xea, 0x13, 0x73, 0x46, 0xfc,
+	0xa9, 0xc3, 0x5e, 0x1b, 0xd4, 0x44, 0x86, 0xda, 0xe6, 0x9e, 0xde, 0xca, 0x81, 0x1e, 0xc2, 0x66,
+	0x0c, 0xb7, 0x89, 0x4b, 0x42, 0x52, 0x2b, 0x30, 0x64, 0x95, 0x1b, 0x5b, 0xcc, 0x86, 0x1e, 0xc3,
+	0x8e, 0xed, 0x04, 0xd6, 0xd0, 0x25, 0x66, 0x48, 0xa6, 0x33, 0xd3, 0xf1, 0x6c, 0xf2, 0x86, 0x04,
+	0x35, 0x89, 0x61, 0x51, 0xec, 0x1b, 0x90, 0xe9, 0xac, 0xcd, 0x3d, 0x11, 0x1b, 0xbc, 0xd2, 0x41,
+	0x4d, 0xbe, 0xce, 0x46, 0x8b, 0x39, 0x12, 0x36, 0x62, 0x98, 0xf6, 0xb7, 0x1c, 0x48, 0xdc, 0x83,
+	0x7e, 0x9a, 0xb2, 0x51, 0x3d, 0xde, 0x8d, 0x50, 0xff, 0x78, 0xb7, 0x5f, 0xe2, 0xbe, 0x76, 0x2b,
+	0xc3, 0x0e, 0x02, 0x31, 0xa3, 0x1c, 0xb6, 0x46, 0x7b, 0x50, 0xb6, 0x6c, 0x3b, 0xaa, 0x12, 0x09,
+	0x6a, 0x79, 0x35, 0x7f, 0x50, 0xc6, 0x2b, 0x03, 0xfa, 0x6a, 0xbd, 0xea, 0xe2, 0x75, 0x9d, 0xdc,
+	0x55, 0xee, 0x88, 0xf2, 0x11, 0xf1, 0x63, 0xa5, 0x16, 0xd8, 0x7d, 0xa5, 0xc8, 0xc0, 0x74, 0xfa,
+	0x31, 0x54, 0xa7, 0xd6, 0x1b, 0x33, 0x20, 0x7f, 0x98, 0x13, 0x6f, 0x44, 0x18, 0x2d, 0x79, 0x5c,
+	0x99, 0x5a, 0x6f, 0xfa, 0xb1, 0x09, 0xd5, 0x01, 0x1c, 0x2f, 0xf4, 0xa9, 0x3d, 0x1f, 0x11, 0xbf,
+	0x56, 0x64, 0xbc, 0x65, 0x2c, 0xe8, 0x97, 0x50, 0x62, 0xa4, 0x9a, 0x8e, 0x5d, 0x2b, 0xa9, 0xc2,
+	0x81, 0x78, 0xac, 0xc4, 0x89, 0x17, 0x19, 0xa5, 0x2c, 0xef, 0x64, 0x89, 0x8b, 0x0c, 0xdb, 0xb6,
+	0xb5, 0x2e, 0x14, 0x98, 0x0d, 0xed, 0x82, 0xc4, 0x65, 0x15, 0xf7, 0x59, 0xbc, 0x43, 0x47, 0x50,
+	0x18, 0x3b, 0x2e, 0x09, 0x6a, 0x39, 0x56, 0x05, 0x94, 0xd1, 0xa4, 0xe3, 0x92, 0xb6, 0x37, 0xa6,
+	0x71, 0x1d, 0x38, 0x4c, 0x3b, 0x83, 0x0a, 0x0b, 0x78, 0x36, 0xb3, 0xad, 0x90, 0xfc, 0xdf, 0xc2,
+	0xfe, 0x25, 0x0f, 0xa5, 0xc4, 0x93, 0x96, 0x4d, 0xc8, 0x94, 0xed, 0x30, 0xee, 0x5c, 0xde, 0x87,
+	0xbb, 0x37, 0xe3, 0x65, 0x5a, 0x17, 0x81, 0x18, 0x38, 0x7f, 0x24, 0x4c, 0xf9, 0x79, 0xcc, 0xd6,
+	0x48, 0x85, 0xca, 0x75, 0xb9, 0x6f, 0xe2, 0xac, 0x09, 0x7d, 0x04, 0x30, 0xa5, 0xb6, 0x33, 0x76,
+	0x88, 0x6d, 0x06, 0xac, 0x84, 0x79, 0x5c, 0x4e, 0x2c, 0x7d, 0x54, 0x8b, 0x04, 0x1b, 0x89, 0xdd,
+	0x8e, 0x55, 0x9d, 0x6c, 0x23, 0x8f, 0xe3, 0x5d, 0x58, 0xae, 0x63, 0xc7, 0x75, 0x4b, 0xb6, 0xd1,
+	0x7c, 0xf2, 0xe8, 0x5a, 0x9b, 0x95, 0x18, 0x60, 0xd3, 0xa3, 0xd9, 0x16, 0x7b, 0x0c, 0xc5, 0x64,
+	0x7e, 0x95, 0x55, 0x61, 0xbd, 0x17, 0x9e, 0x93, 0x51, 0x48, 0xd3, 0xc9, 0x10, 0xc3, 0x90, 0x02,
+	0xa5, 0x54, 0x4c, 0xc0, 0x5e, 0x9a, 0xee, 0xa3, 0xa9, 0x99, 0xe6, 0xe1, 0x05, 0xb5, 0x8a, 0x2a,
+	0x1c, 0x14, 0x70, 0x9a, 0x9a, 0x11, 0xa0, 0x5f, 0x80, 0x74, 0xec, 0xd2, 0xd1, 0xab, 0xa4, 0xf3,
+	0xee, 0xad, 0x6e, 0x63, 0xf6, 0x4c, 0x75, 0xa4, 0x21, 0x03, 0xfe, 0x5a, 0xfc, 0xf3, 0x77, 0xfb,
+	0x1b, 0xda, 0x37, 0x50, 0x4e, 0x01, 0x51, 0xe5, 0xe9, 0x78, 0x1c, 0x90, 0x90, 0x95, 0x29, 0x8f,
+	0xe3, 0x5d, 0x4a, 0x7e, 0x8e, 0xdd, 0xcb, 0xc9, 0x47, 0x20, 0x9e, 0x5b, 0xc1, 0x39, 0x2b, 0x48,
+	0x15, 0xb3, 0x75, 0x1c, 0xf2, 0x37, 0x20, 0xf1, 0x0c, 0xd1, 0x13, 0x28, 0x8d, 0xe8, 0xdc, 0x0b,
+	0x57, 0xf3, 0x71, 0x3b, 0xdb, 0x76, 0xcc, 0x13, 0xbf, 0x2a, 0x05, 0x6a, 0x27, 0x50, 0x8c, 0x5d,
+	0xe8, 0x51, 0x3a, 0x13, 0xc4, 0xe3, 0xfb, 0x49, 0x6b, 0xf4, 0xcf, 0xa9, 0x1f, 0xae, 0x8d, 0x84,
+	0x1d, 0x28, 0x5c, 0x58, 0xee, 0x9c, 0xbf, 0x4f, 0xc4, 0x7c, 0xa3, 0xfd, 0x5d, 0x80, 0x22, 0x8e,
+	0x08, 0x0c, 0xc2, 0xcc, 0xa8, 0x2d, 0xac, 0x8d, 0xda, 0x95, 0xd4, 0x73, 0x6b, 0x52, 0x4f, 0xd4,
+	0x9a, 0xcf, 0xa8, 0x75, 0x45, 0x8e, 0x78, 0x2b, 0x39, 0x85, 0x5b, 0xc8, 0x91, 0x56, 0xe4, 0x44,
+	0xc2, 0x19, 0xfb, 0x74, 0xca, 0x86, 0x29, 0xf5, 0x2d, 0xff, 0x32, 0x56, 0xd6, 0x66, 0x64, 0x1d,
+	0x24, 0x46, 0xcd, 0x84, 0x12, 0x26, 0xc1, 0x8c, 0x7a, 0x01, 0xb9, 0xf3, 0xd9, 0x08, 0x44, 0xdb,
+	0x0a, 0x2d, 0xf6, 0xe8, 0x2a, 0x66, 0x6b, 0xf4, 0x33, 0x10, 0x47, 0xd4, 0xe6, 0x4f, 0xde, 0xca,
+	0xd6, 0x5f, 0xf7, 0x7d, 0xea, 0x37, 0xa9, 0x4d, 0x30, 0x03, 0x68, 0x33, 0x90, 0x5b, 0xf4, 0xb5,
+	0xe7, 0x52, 0xcb, 0xee, 0xf9, 0x74, 0x12, 0x0d, 0xbb, 0x3b, 0x5b, 0xbe, 0x05, 0xc5, 0x39, 0x1b,
+	0x0a, 0x49, 0xd3, 0x7f, 0xb2, 0xde, 0xa4, 0xd7, 0x03, 0xf1, 0x09, 0x92, 0x28, 0x3b, 0x3e, 0xaa,
+	0xfd, 0x20, 0x80, 0x72, 0x37, 0x1a, 0xb5, 0xa1, 0xc2, 0x91, 0x66, 0xe6, 0x3b, 0x7e, 0xf0, 0x3e,
+	0x17, 0xb1, 0xf9, 0x00, 0xf3, 0x74, 0x7d, 0xeb, 0xc7, 0x21, 0xd3, 0x89, 0xf9, 0xf7, 0xeb, 0xc4,
+	0x87, 0xb0, 0xc9, 0x7a, 0x24, 0xfd, 0xe4, 0x89, 0x6a, 0xfe, 0xa0, 0x80, 0xab, 0x43, 0xde, 0x28,
+	0xcc, 0xa6, 0x49, 0x20, 0xf6, 0x1c, 0x6f, 0xa2, 0xed, 0x43, 0xa1, 0xe9, 0x52, 0x56, 0x2c, 0xc9,
+	0x27, 0x56, 0x40, 0xbd, 0x84, 0x43, 0xbe, 0x3b, 0xfc, 0x21, 0x07, 0x95, 0xcc, 0x5f, 0x11, 0xf4,
+	0x18, 0xb6, 0x9a, 0x9d, 0xb3, 0xfe, 0x40, 0xc7, 0x66, 0xb3, 0x6b, 0x9c, 0xb4, 0x4f, 0xe5, 0x0d,
+	0x65, 0x6f, 0xb1, 0x54, 0x6b, 0xd3, 0x15, 0x68, 0xfd, 0x5f, 0xc6, 0x3e, 0x14, 0xda, 0x46, 0x4b,
+	0xff, 0x9d, 0x2c, 0x28, 0x3b, 0x8b, 0xa5, 0x2a, 0x67, 0x80, 0xfc, 0x43, 0xf0, 0x73, 0xa8, 0x32,
+	0x80, 0x79, 0xd6, 0x6b, 0x35, 0x06, 0xba, 0x9c, 0x53, 0x94, 0xc5, 0x52, 0xdd, 0xbd, 0x8e, 0x8b,
+	0xf9, 0x7e, 0x08, 0x45, 0xac, 0x7f, 0x73, 0xa6, 0xf7, 0x07, 0x72, 0x5e, 0xd9, 0x5d, 0x2c, 0x55,
+	0x94, 0x01, 0x26, 0x1d, 0xf3, 0x08, 0x4a, 0x58, 0xef, 0xf7, 0xba, 0x46, 0x5f, 0x97, 0x45, 0xe5,
+	0xc3, 0xc5, 0x52, 0xbd, 0xb7, 0x86, 0x8a, 0x15, 0xfa, 0x2b, 0xd8, 0x6e, 0x75, 0x5f, 0x18, 0x9d,
+	0x6e, 0xa3, 0x65, 0xf6, 0x70, 0xf7, 0x14, 0xeb, 0xfd, 0xbe, 0x5c, 0x50, 0xf6, 0x17, 0x4b, 0xf5,
+	0x41, 0x06, 0x7f, 0x43, 0x70, 0x1f, 0x81, 0xd8, 0x6b, 0x1b, 0xa7, 0xb2, 0xa4, 0xdc, 0x5b, 0x2c,
+	0xd5, 0x0f, 0x32, 0xd0, 0x88, 0xd4, 0x28, 0xe3, 0x66, 0xa7, 0xdb, 0xd7, 0xe5, 0xe2, 0x8d, 0x8c,
+	0x19, 0xd9, 0x87, 0xbf, 0x07, 0x74, 0xf3, 0xcf, 0x1a, 0xfa, 0x04, 0x44, 0xa3, 0x6b, 0xe8, 0xf2,
+	0x06, 0xcf, 0xff, 0x26, 0xc2, 0xa0, 0x1e, 0x41, 0x1a, 0xe4, 0x3b, 0xdf, 0x7e, 0x29, 0x0b, 0xca,
+	0x4f, 0x16, 0x4b, 0xf5, 0xfe, 0x4d, 0x50, 0xe7, 0xdb, 0x2f, 0x0f, 0x29, 0x54, 0xb2, 0x81, 0x35,
+	0x28, 0x3d, 0xd3, 0x07, 0x8d, 0x56, 0x63, 0xd0, 0x90, 0x37, 0xf8, 0x93, 0x12, 0xf7, 0x33, 0x12,
+	0x5a, 0xac, 0x01, 0xf7, 0xa0, 0x60, 0xe8, 0xcf, 0x75, 0x2c, 0x0b, 0xca, 0xf6, 0x62, 0xa9, 0x6e,
+	0x26, 0x00, 0x83, 0x5c, 0x10, 0x1f, 0xd5, 0x41, 0x6a, 0x74, 0x5e, 0x34, 0x5e, 0xf6, 0xe5, 0x9c,
+	0x82, 0x16, 0x4b, 0x75, 0x2b, 0x71, 0x37, 0xdc, 0xd7, 0xd6, 0x65, 0x70, 0xf8, 0x1f, 0x01, 0xaa,
+	0xd9, 0xcf, 0x1e, 0xaa, 0x83, 0x78, 0xd2, 0xee, 0xe8, 0xc9, 0x75, 0x59, 0x5f, 0xb4, 0x46, 0x07,
+	0x50, 0x6e, 0xb5, 0xb1, 0xde, 0x1c, 0x74, 0xf1, 0xcb, 0x24, 0x97, 0x2c, 0xa8, 0xe5, 0xf8, 0x4c,
+	0xdc, 0xd1, 0x9f, 0xc3, 0x6a, 0xff, 0xe5, 0xb3, 0x4e, 0xdb, 0xf8, 0xda, 0x64, 0x11, 0x73, 0xca,
+	0x83, 0xc5, 0x52, 0xfd, 0x30, 0x0b, 0xee, 0x5f, 0x4e, 0x5d, 0xc7, 0x7b, 0xc5, 0x02, 0x7f, 0x05,
+	0xdb, 0x09, 0x7c, 0x75, 0x41, 0x5e, 0x51, 0x17, 0x4b, 0x75, 0xef, 0x96, 0x33, 0xab, 0x7b, 0x9e,
+	0xc0, 0x07, 0xc9, 0xc1, 0x33, 0xe3, 0x6b, 0xa3, 0xfb, 0xc2, 0x90, 0x45, 0xa5, 0xbe, 0x58, 0xaa,
+	0xca, 0x2d, 0xc7, 0xce, 0xbc, 0x57, 0x1e, 0x7d, 0xed, 0x1d, 0xfe, 0x55, 0x80, 0x72, 0x3a, 0xa1,
+	0x22, 0x9e, 0x8d, 0xae, 0xa9, 0x63, 0xdc, 0xc5, 0x49, 0xe2, 0xa9, 0xd3, 0xa0, 0x6c, 0x89, 0x3e,
+	0x86, 0xe2, 0xa9, 0x6e, 0xe8, 0xb8, 0xdd, 0x4c, 0xfa, 0x21, 0x85, 0x9c, 0x12, 0x8f, 0xf8, 0xce,
+	0x08, 0x7d, 0x0a, 0x55, 0xa3, 0x6b, 0xf6, 0xcf, 0x9a, 0x4f, 0x93, 0x8c, 0x99, 0x80, 0x33, 0xa1,
+	0xfa, 0xf3, 0xd1, 0x39, 0xcb, 0xf6, 0x30, 0x6a, 0x9d, 0xe7, 0x8d, 0x4e, 0xbb, 0xc5, 0xa1, 0x79,
+	0xa5, 0xb6, 0x58, 0xaa, 0x3b, 0x29, 0xb4, 0xcd, 0x3f, 0xfb, 0x11, 0xf6, 0xd0, 0x86, 0xfa, 0xff,
+	0x9e, 0x45, 0x48, 0x05, 0xa9, 0xd1, 0xeb, 0xe9, 0x46, 0x2b, 0x79, 0xfd, 0xca, 0xd7, 0x98, 0xcd,
+	0x88, 0x67, 0x47, 0x88, 0x93, 0x2e, 0x3e, 0xd5, 0x07, 0xc9, 0xe3, 0x57, 0x88, 0x13, 0xea, 0x4f,
+	0x48, 0x78, 0xbc, 0xf7, 0xf6, 0xc7, 0xfa, 0xc6, 0xf7, 0x3f, 0xd6, 0x37, 0xde, 0x5e, 0xd5, 0x85,
+	0xef, 0xaf, 0xea, 0xc2, 0x3f, 0xaf, 0xea, 0x1b, 0xff, 0xbe, 0xaa, 0x0b, 0xdf, 0xfd, 0xab, 0x2e,
+	0x0c, 0x25, 0x36, 0xbb, 0x9e, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x96, 0x0b, 0xf7, 0x15, 0xb6,
+	0x0d, 0x00, 0x00,
 }
 }

+ 8 - 9
lib/protocol/bep.proto

@@ -63,15 +63,14 @@ message Folder {
 }
 }
 
 
 message Device {
 message Device {
-    bytes           id                         = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "DeviceID", (gogoproto.nullable) = false];
-    string          name                       = 2;
-    repeated string addresses                  = 3;
-    Compression     compression                = 4;
-    string          cert_name                  = 5;
-    int64           max_sequence               = 6;
-    bool            introducer                 = 7;
-    uint64          index_id                   = 8 [(gogoproto.customname) = "IndexID", (gogoproto.customtype) = "IndexID", (gogoproto.nullable) = false];
-    bool            skip_introduction_removals = 9;
+    bytes           id           = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "DeviceID", (gogoproto.nullable) = false];
+    string          name         = 2;
+    repeated string addresses    = 3;
+    Compression     compression  = 4;
+    string          cert_name    = 5;
+    int64           max_sequence = 6;
+    bool            introducer   = 7;
+    uint64          index_id     = 8 [(gogoproto.customname) = "IndexID", (gogoproto.customtype) = "IndexID", (gogoproto.nullable) = false];
 }
 }
 
 
 enum Compression {
 enum Compression {