Browse Source

all: Add folder pause, make pauses permanent (fixes #3407, fixes #215, fixes #3001)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3520
Audrius Butkevicius 8 years ago
parent
commit
bab7c8ebbf

+ 33 - 37
cmd/syncthing/gui.go

@@ -82,8 +82,6 @@ type modelIntf interface {
 	Availability(folder, file string, version protocol.Vector, block protocol.BlockInfo) []model.Availability
 	Availability(folder, file string, version protocol.Vector, block protocol.BlockInfo) []model.Availability
 	GetIgnores(folder string) ([]string, []string, error)
 	GetIgnores(folder string) ([]string, []string, error)
 	SetIgnores(folder string, content []string) error
 	SetIgnores(folder string, content []string) error
-	PauseDevice(device protocol.DeviceID)
-	ResumeDevice(device protocol.DeviceID)
 	DelayScan(folder string, next time.Duration)
 	DelayScan(folder string, next time.Duration)
 	ScanFolder(folder string) error
 	ScanFolder(folder string) error
 	ScanFolders() map[string]error
 	ScanFolders() map[string]error
@@ -105,6 +103,7 @@ type configIntf interface {
 	Subscribe(c config.Committer)
 	Subscribe(c config.Committer)
 	Folders() map[string]config.FolderConfiguration
 	Folders() map[string]config.FolderConfiguration
 	Devices() map[protocol.DeviceID]config.DeviceConfiguration
 	Devices() map[protocol.DeviceID]config.DeviceConfiguration
+	SetDevice(config.DeviceConfiguration) error
 	Save() error
 	Save() error
 	ListenAddresses() []string
 	ListenAddresses() []string
 	RequiresRestart() bool
 	RequiresRestart() bool
@@ -258,21 +257,21 @@ func (s *apiService) Serve() {
 
 
 	// The POST handlers
 	// The POST handlers
 	postRestMux := http.NewServeMux()
 	postRestMux := http.NewServeMux()
-	postRestMux.HandleFunc("/rest/db/prio", s.postDBPrio)                      // folder file [perpage] [page]
-	postRestMux.HandleFunc("/rest/db/ignores", s.postDBIgnores)                // folder
-	postRestMux.HandleFunc("/rest/db/override", s.postDBOverride)              // folder
-	postRestMux.HandleFunc("/rest/db/scan", s.postDBScan)                      // folder [sub...] [delay]
-	postRestMux.HandleFunc("/rest/system/config", s.postSystemConfig)          // <body>
-	postRestMux.HandleFunc("/rest/system/error", s.postSystemError)            // <body>
-	postRestMux.HandleFunc("/rest/system/error/clear", s.postSystemErrorClear) // -
-	postRestMux.HandleFunc("/rest/system/ping", s.restPing)                    // -
-	postRestMux.HandleFunc("/rest/system/reset", s.postSystemReset)            // [folder]
-	postRestMux.HandleFunc("/rest/system/restart", s.postSystemRestart)        // -
-	postRestMux.HandleFunc("/rest/system/shutdown", s.postSystemShutdown)      // -
-	postRestMux.HandleFunc("/rest/system/upgrade", s.postSystemUpgrade)        // -
-	postRestMux.HandleFunc("/rest/system/pause", s.postSystemPause)            // device
-	postRestMux.HandleFunc("/rest/system/resume", s.postSystemResume)          // device
-	postRestMux.HandleFunc("/rest/system/debug", s.postSystemDebug)            // [enable] [disable]
+	postRestMux.HandleFunc("/rest/db/prio", s.postDBPrio)                          // folder file [perpage] [page]
+	postRestMux.HandleFunc("/rest/db/ignores", s.postDBIgnores)                    // folder
+	postRestMux.HandleFunc("/rest/db/override", s.postDBOverride)                  // folder
+	postRestMux.HandleFunc("/rest/db/scan", s.postDBScan)                          // folder [sub...] [delay]
+	postRestMux.HandleFunc("/rest/system/config", s.postSystemConfig)              // <body>
+	postRestMux.HandleFunc("/rest/system/error", s.postSystemError)                // <body>
+	postRestMux.HandleFunc("/rest/system/error/clear", s.postSystemErrorClear)     // -
+	postRestMux.HandleFunc("/rest/system/ping", s.restPing)                        // -
+	postRestMux.HandleFunc("/rest/system/reset", s.postSystemReset)                // [folder]
+	postRestMux.HandleFunc("/rest/system/restart", s.postSystemRestart)            // -
+	postRestMux.HandleFunc("/rest/system/shutdown", s.postSystemShutdown)          // -
+	postRestMux.HandleFunc("/rest/system/upgrade", s.postSystemUpgrade)            // -
+	postRestMux.HandleFunc("/rest/system/pause", s.makeDevicePauseHandler(true))   // device
+	postRestMux.HandleFunc("/rest/system/resume", s.makeDevicePauseHandler(false)) // device
+	postRestMux.HandleFunc("/rest/system/debug", s.postSystemDebug)                // [enable] [disable]
 
 
 	// Debug endpoints, not for general use
 	// Debug endpoints, not for general use
 	debugMux := http.NewServeMux()
 	debugMux := http.NewServeMux()
@@ -1103,30 +1102,27 @@ func (s *apiService) postSystemUpgrade(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 }
 }
 
 
-func (s *apiService) postSystemPause(w http.ResponseWriter, r *http.Request) {
-	var qs = r.URL.Query()
-	var deviceStr = qs.Get("device")
-
-	device, err := protocol.DeviceIDFromString(deviceStr)
-	if err != nil {
-		http.Error(w, err.Error(), 500)
-		return
-	}
+func (s *apiService) makeDevicePauseHandler(paused bool) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var qs = r.URL.Query()
+		var deviceStr = qs.Get("device")
 
 
-	s.model.PauseDevice(device)
-}
+		device, err := protocol.DeviceIDFromString(deviceStr)
+		if err != nil {
+			http.Error(w, err.Error(), 500)
+			return
+		}
 
 
-func (s *apiService) postSystemResume(w http.ResponseWriter, r *http.Request) {
-	var qs = r.URL.Query()
-	var deviceStr = qs.Get("device")
+		cfg, ok := s.cfg.Devices()[device]
+		if !ok {
+			http.Error(w, "not found", http.StatusNotFound)
+		}
 
 
-	device, err := protocol.DeviceIDFromString(deviceStr)
-	if err != nil {
-		http.Error(w, err.Error(), 500)
-		return
+		cfg.Paused = paused
+		if err := s.cfg.SetDevice(cfg); err != nil {
+			http.Error(w, err.Error(), 500)
+		}
 	}
 	}
-
-	s.model.ResumeDevice(device)
 }
 }
 
 
 func (s *apiService) postDBScan(w http.ResponseWriter, r *http.Request) {
 func (s *apiService) postDBScan(w http.ResponseWriter, r *http.Request) {

+ 23 - 5
cmd/syncthing/main.go

@@ -212,6 +212,7 @@ type RuntimeOptions struct {
 	auditEnabled   bool
 	auditEnabled   bool
 	verbose        bool
 	verbose        bool
 	paused         bool
 	paused         bool
+	unpaused       bool
 	guiAddress     string
 	guiAddress     string
 	guiAPIKey      string
 	guiAPIKey      string
 	generateDir    string
 	generateDir    string
@@ -267,7 +268,8 @@ func parseCommandLineOptions() RuntimeOptions {
 	flag.StringVar(&options.upgradeTo, "upgrade-to", options.upgradeTo, "Force upgrade directly from specified URL")
 	flag.StringVar(&options.upgradeTo, "upgrade-to", options.upgradeTo, "Force upgrade directly from specified URL")
 	flag.BoolVar(&options.auditEnabled, "audit", false, "Write events to audit file")
 	flag.BoolVar(&options.auditEnabled, "audit", false, "Write events to audit file")
 	flag.BoolVar(&options.verbose, "verbose", false, "Print verbose log output")
 	flag.BoolVar(&options.verbose, "verbose", false, "Print verbose log output")
-	flag.BoolVar(&options.paused, "paused", false, "Start with all devices paused")
+	flag.BoolVar(&options.paused, "paused", false, "Start with all devices and folders paused")
+	flag.BoolVar(&options.unpaused, "unpaused", false, "Start with all devices and folders unpaused")
 	flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (use \"-\" for stdout)")
 	flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (use \"-\" for stdout)")
 	if runtime.GOOS == "windows" {
 	if runtime.GOOS == "windows" {
 		// Allow user to hide the console window
 		// Allow user to hide the console window
@@ -701,14 +703,17 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
 		m.StartDeadlockDetector(20 * time.Minute)
 		m.StartDeadlockDetector(20 * time.Minute)
 	}
 	}
 
 
-	if runtimeOptions.paused {
-		for device := range cfg.Devices() {
-			m.PauseDevice(device)
-		}
+	if runtimeOptions.unpaused {
+		setPauseState(cfg, false)
+	} else if runtimeOptions.paused {
+		setPauseState(cfg, true)
 	}
 	}
 
 
 	// Add and start folders
 	// Add and start folders
 	for _, folderCfg := range cfg.Folders() {
 	for _, folderCfg := range cfg.Folders() {
+		if folderCfg.Paused {
+			continue
+		}
 		m.AddFolder(folderCfg)
 		m.AddFolder(folderCfg)
 		m.StartFolder(folderCfg.ID)
 		m.StartFolder(folderCfg.ID)
 	}
 	}
@@ -1203,3 +1208,16 @@ func showPaths() {
 	fmt.Printf("GUI override directory:\n\t%s\n\n", locations[locGUIAssets])
 	fmt.Printf("GUI override directory:\n\t%s\n\n", locations[locGUIAssets])
 	fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations[locDefFolder])
 	fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations[locDefFolder])
 }
 }
+
+func setPauseState(cfg *config.Wrapper, paused bool) {
+	raw := cfg.RawCopy()
+	for i := range raw.Devices {
+		raw.Devices[i].Paused = paused
+	}
+	for i := range raw.Folders {
+		raw.Folders[i].Paused = paused
+	}
+	if err := cfg.Replace(raw); err != nil {
+		l.Fatalln("Cannot adjust paused state:", err)
+	}
+}

+ 4 - 0
cmd/syncthing/mocked_config_test.go

@@ -45,6 +45,10 @@ func (c *mockedConfig) Devices() map[protocol.DeviceID]config.DeviceConfiguratio
 	return nil
 	return nil
 }
 }
 
 
+func (c *mockedConfig) SetDevice(config.DeviceConfiguration) error {
+	return nil
+}
+
 func (c *mockedConfig) Save() error {
 func (c *mockedConfig) Save() error {
 	return nil
 	return nil
 }
 }

+ 12 - 0
cmd/syncthing/verboseservice.go

@@ -166,6 +166,18 @@ func (s *verboseService) formatEvent(ev events.Event) string {
 		device := data["device"]
 		device := data["device"]
 		return fmt.Sprintf("Device %v was resumed", device)
 		return fmt.Sprintf("Device %v was resumed", device)
 
 
+	case events.FolderPaused:
+		data := ev.Data.(map[string]string)
+		id := data["id"]
+		label := data["label"]
+		return fmt.Sprintf("Folder %v (%v) was paused", id, label)
+
+	case events.FolderResumed:
+		data := ev.Data.(map[string]string)
+		id := data["id"]
+		label := data["label"]
+		return fmt.Sprintf("Folder %v (%v) was resumed", id, label)
+
 	case events.ListenAddressesChanged:
 	case events.ListenAddressesChanged:
 		data := ev.Data.(map[string]interface{})
 		data := ev.Data.(map[string]interface{})
 		address := data["address"]
 		address := data["address"]

+ 12 - 5
gui/default/index.html

@@ -271,6 +271,7 @@
                   <span class="fa fa-fw" ng-class="[folder.type == 'readonly' ? 'fa-lock' : 'fa-folder']"></span>
                   <span class="fa fa-fw" ng-class="[folder.type == 'readonly' ? 'fa-lock' : 'fa-folder']"></span>
                 </div>
                 </div>
                 <div class="panel-status pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
                 <div class="panel-status pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
+                  <span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs">&#9724;</span></span>
                   <span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs">&#9724;</span></span>
                   <span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs">&#9724;</span></span>
                   <span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs">&#9724;</span></span>
                   <span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs">&#9724;</span></span>
                   <span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs">&#9724;</span></span>
                   <span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs">&#9724;</span></span>
@@ -307,11 +308,11 @@
                         <span tooltip data-original-title="{{folder.path}}">{{folder.path}}</span>
                         <span tooltip data-original-title="{{folder.path}}">{{folder.path}}</span>
                       </td>
                       </td>
                     </tr>
                     </tr>
-                    <tr ng-if="model[folder.id].invalid || model[folder.id].error">
+                    <tr ng-if="!folder.paused && (model[folder.id].invalid || model[folder.id].error)">
                       <th><span class="fa fa-fw fa-exclamation-triangle"></span>&nbsp;<span translate>Error</span></th>
                       <th><span class="fa fa-fw fa-exclamation-triangle"></span>&nbsp;<span translate>Error</span></th>
                       <td class="text-right">{{model[folder.id].invalid || model[folder.id].error}}</td>
                       <td class="text-right">{{model[folder.id].invalid || model[folder.id].error}}</td>
                     </tr>
                     </tr>
-                    <tr>
+                    <tr ng-if="!folder.paused">
                       <th><span class="fa fa-fw fa-globe"></span>&nbsp;<span translate>Global State</span></th>
                       <th><span class="fa fa-fw fa-globe"></span>&nbsp;<span translate>Global State</span></th>
                       <td class="text-right">
                       <td class="text-right">
                         <span tooltip data-original-title="{{model[folder.id].globalFiles | alwaysNumber}} {{'files' | translate}}, {{model[folder.id].globalDirectories | alwaysNumber}} {{'directories' | translate}}, ~{{model[folder.id].globalBytes | binary}}B">
                         <span tooltip data-original-title="{{model[folder.id].globalFiles | alwaysNumber}} {{'files' | translate}}, {{model[folder.id].globalDirectories | alwaysNumber}} {{'directories' | translate}}, ~{{model[folder.id].globalBytes | binary}}B">
@@ -321,7 +322,7 @@
                         </span>
                         </span>
                       </td>
                       </td>
                     </tr>
                     </tr>
-                    <tr>
+                    <tr ng-if="!folder.paused">
                       <th><span class="fa fa-fw fa-home"></span>&nbsp;<span translate>Local State</span></th>
                       <th><span class="fa fa-fw fa-home"></span>&nbsp;<span translate>Local State</span></th>
                       <td class="text-right">
                       <td class="text-right">
                         <span tooltip data-original-title="{{model[folder.id].localFiles | alwaysNumber}} {{'files' | translate}}, {{model[folder.id].localDirectories | alwaysNumber}} {{'directories' | translate}}, ~{{model[folder.id].localBytes | binary}}B">
                         <span tooltip data-original-title="{{model[folder.id].localFiles | alwaysNumber}} {{'files' | translate}}, {{model[folder.id].localDirectories | alwaysNumber}} {{'directories' | translate}}, ~{{model[folder.id].localBytes | binary}}B">
@@ -417,6 +418,12 @@
                   <span class="fa fa-arrow-circle-up"></span>&nbsp;<span translate>Override Changes</span>
                   <span class="fa fa-arrow-circle-up"></span>&nbsp;<span translate>Override Changes</span>
                 </button>
                 </button>
                 <span class="pull-right">
                 <span class="pull-right">
+                  <button ng-if="!folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, true)">
+                    <span class="fa fa-pause"></span>&nbsp;<span translate>Pause</span>
+                  </button>
+                  <button ng-if="folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, false)">
+                    <span class="fa fa-play"></span>&nbsp;<span translate>Resume</span>
+                  </button>
                   <button type="button" class="btn btn-sm btn-default" ng-click="rescanFolder(folder.id)" ng-show="['idle', 'stopped', 'unshared'].indexOf(folderStatus(folder)) > -1">
                   <button type="button" class="btn btn-sm btn-default" ng-click="rescanFolder(folder.id)" ng-show="['idle', 'stopped', 'unshared'].indexOf(folderStatus(folder)) > -1">
                     <span class="fa fa-refresh"></span>&nbsp;<span translate>Rescan</span>
                     <span class="fa fa-refresh"></span>&nbsp;<span translate>Rescan</span>
                   </button>
                   </button>
@@ -601,10 +608,10 @@
               </div>
               </div>
               <div class="panel-footer">
               <div class="panel-footer">
                 <span class="pull-right">
                 <span class="pull-right">
-                  <button ng-if="!connections[deviceCfg.deviceID].paused" type="button" class="btn btn-sm btn-default" ng-click="pauseDevice(deviceCfg.deviceID)">
+                  <button ng-if="!deviceCfg.paused" type="button" class="btn btn-sm btn-default" ng-click="setDevicePause(deviceCfg.deviceID, true)">
                     <span class="fa fa-pause"></span>&nbsp;<span translate>Pause</span>
                     <span class="fa fa-pause"></span>&nbsp;<span translate>Pause</span>
                   </button>
                   </button>
-                  <button ng-if="connections[deviceCfg.deviceID].paused" type="button" class="btn btn-sm btn-default" ng-click="resumeDevice(deviceCfg.deviceID)">
+                  <button ng-if="deviceCfg.paused" type="button" class="btn btn-sm btn-default" ng-click="setDevicePause(deviceCfg.deviceID, false)">
                     <span class="fa fa-play"></span>&nbsp;<span translate>Resume</span>
                     <span class="fa fa-play"></span>&nbsp;<span translate>Resume</span>
                   </button>
                   </button>
                   <button type="button" class="btn btn-sm btn-default" ng-click="editDevice(deviceCfg)">
                   <button type="button" class="btn btn-sm btn-default" ng-click="editDevice(deviceCfg)">

+ 2 - 0
gui/default/syncthing/core/eventService.js

@@ -77,6 +77,8 @@ angular.module('syncthing.core')
             STATE_CHANGED:        'StateChanged',   // Emitted when a folder changes state
             STATE_CHANGED:        'StateChanged',   // Emitted when a folder changes state
             FOLDER_ERRORS:        'FolderErrors',   // Emitted when a folder has errors preventing a full sync
             FOLDER_ERRORS:        'FolderErrors',   // Emitted when a folder has errors preventing a full sync
             FOLDER_SCAN_PROGRESS: 'FolderScanProgress',   // Emitted every ScanProgressIntervalS seconds, indicating how far into the scan it is at.
             FOLDER_SCAN_PROGRESS: 'FolderScanProgress',   // Emitted every ScanProgressIntervalS seconds, indicating how far into the scan it is at.
+            FOLDER_PAUSED:        'FolderPaused',   // Emitted when a folder is paused
+            FOLDER_RESUMED:       'FolderResumed',   // Emitted when a folder is resumed
 
 
             start: function() {
             start: function() {
                 $http.get(urlbase + '/events?limit=1')
                 $http.get(urlbase + '/events?limit=1')

+ 24 - 14
gui/default/syncthing/core/syncthingController.js

@@ -235,14 +235,6 @@ angular.module('syncthing.core')
             $scope.deviceRejections[arg.data.device] = arg;
             $scope.deviceRejections[arg.data.device] = arg;
         });
         });
 
 
-        $scope.$on(Events.DEVICE_PAUSED, function (event, arg) {
-            $scope.connections[arg.data.device].paused = true;
-        });
-
-        $scope.$on(Events.DEVICE_RESUMED, function (event, arg) {
-            $scope.connections[arg.data.device].paused = false;
-        });
-
         $scope.$on(Events.FOLDER_REJECTED, function (event, arg) {
         $scope.$on(Events.FOLDER_REJECTED, function (event, arg) {
             $scope.folderRejections[arg.data.folder + "-" + arg.data.device] = arg;
             $scope.folderRejections[arg.data.folder + "-" + arg.data.device] = arg;
         });
         });
@@ -652,6 +644,10 @@ angular.module('syncthing.core')
                 return 'unknown';
                 return 'unknown';
             }
             }
 
 
+            if (folderCfg.paused) {
+                return 'paused';
+            }
+
             // after restart syncthing process state may be empty
             // after restart syncthing process state may be empty
             if (!$scope.model[folderCfg.id].state) {
             if (!$scope.model[folderCfg.id].state) {
                 return 'unknown';
                 return 'unknown';
@@ -685,6 +681,9 @@ angular.module('syncthing.core')
             if (status === 'idle') {
             if (status === 'idle') {
                 return 'success';
                 return 'success';
             }
             }
+            if (status == 'paused') {
+                return 'default';
+            }
             if (status === 'syncing' || status === 'scanning') {
             if (status === 'syncing' || status === 'scanning') {
                 return 'primary';
                 return 'primary';
             }
             }
@@ -801,7 +800,7 @@ angular.module('syncthing.core')
                 return 'unknown';
                 return 'unknown';
             }
             }
 
 
-            if ($scope.connections[deviceCfg.deviceID].paused) {
+            if (deviceCfg.paused) {
                 return 'paused';
                 return 'paused';
             }
             }
 
 
@@ -827,7 +826,7 @@ angular.module('syncthing.core')
                 return 'info';
                 return 'info';
             }
             }
 
 
-            if ($scope.connections[deviceCfg.deviceID].paused) {
+            if (deviceCfg.paused) {
                 return 'default';
                 return 'default';
             }
             }
 
 
@@ -964,12 +963,23 @@ angular.module('syncthing.core')
             return device.deviceID.substr(0, 6);
             return device.deviceID.substr(0, 6);
         };
         };
 
 
-        $scope.pauseDevice = function (device) {
-            $http.post(urlbase + "/system/pause?device=" + device);
+        $scope.setDevicePause = function (device, pause) {
+            $scope.devices.forEach(function (cfg) {
+                if (cfg.deviceID == device) {
+                    cfg.paused = pause;
+                }
+            });
+            $scope.config.devices = $scope.devices;
+            $scope.saveConfig();
         };
         };
 
 
-        $scope.resumeDevice = function (device) {
-            $http.post(urlbase + "/system/resume?device=" + device);
+        $scope.setFolderPause = function (folder, pause) {
+            var cfg = $scope.folders[folder];
+            if (cfg) {
+                cfg.paused = pause;
+                $scope.config.folders = folderList($scope.folders);
+                $scope.saveConfig();
+            }
         };
         };
 
 
         $scope.editSettings = function () {
         $scope.editSettings = function () {

+ 1 - 0
lib/config/deviceconfiguration.go

@@ -17,6 +17,7 @@ type DeviceConfiguration struct {
 	Introducer               bool                 `xml:"introducer,attr" json:"introducer"`
 	Introducer               bool                 `xml:"introducer,attr" json:"introducer"`
 	SkipIntroductionRemovals bool                 `xml:"skipIntroductionRemovals,attr" json:"skipIntroductionRemovals"`
 	SkipIntroductionRemovals bool                 `xml:"skipIntroductionRemovals,attr" json:"skipIntroductionRemovals"`
 	IntroducedBy             protocol.DeviceID    `xml:"introducedBy,attr" json:"introducedBy"`
 	IntroducedBy             protocol.DeviceID    `xml:"introducedBy,attr" json:"introducedBy"`
+	Paused                   bool                 `xml:"paused" json:"paused"`
 }
 }
 
 
 func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {
 func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {

+ 1 - 0
lib/config/folderconfiguration.go

@@ -41,6 +41,7 @@ type FolderConfiguration struct {
 	DisableTempIndexes    bool                        `xml:"disableTempIndexes" json:"disableTempIndexes"`
 	DisableTempIndexes    bool                        `xml:"disableTempIndexes" json:"disableTempIndexes"`
 	Fsync                 bool                        `xml:"fsync" json:"fsync"`
 	Fsync                 bool                        `xml:"fsync" json:"fsync"`
 	DisableWeakHash       bool                        `xml:"disableWeakHash" json:"disableWeakHash"`
 	DisableWeakHash       bool                        `xml:"disableWeakHash" json:"disableWeakHash"`
+	Paused                bool                        `xml:"paused" json:"paused"`
 
 
 	cachedPath string
 	cachedPath string
 
 

+ 12 - 0
lib/config/wrapper.go

@@ -323,6 +323,18 @@ func (w *Wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) {
 	return DeviceConfiguration{}, false
 	return DeviceConfiguration{}, false
 }
 }
 
 
+// Folder returns the configuration for the given folder and an "ok" bool.
+func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) {
+	w.mut.Lock()
+	defer w.mut.Unlock()
+	for _, folder := range w.cfg.Folders {
+		if folder.ID == id {
+			return folder, true
+		}
+	}
+	return FolderConfiguration{}, false
+}
+
 // Save writes the configuration to disk, and generates a ConfigSaved event.
 // Save writes the configuration to disk, and generates a ConfigSaved event.
 func (w *Wrapper) Save() error {
 func (w *Wrapper) Save() error {
 	fd, err := osutil.CreateAtomic(w.path)
 	fd, err := osutil.CreateAtomic(w.path)

+ 1 - 2
lib/connections/service.go

@@ -343,8 +343,7 @@ func (s *Service) connect() {
 				continue
 				continue
 			}
 			}
 
 
-			paused := s.model.IsPaused(deviceID)
-			if paused {
+			if deviceCfg.Paused {
 				continue
 				continue
 			}
 			}
 
 

+ 0 - 1
lib/connections/structs.go

@@ -116,7 +116,6 @@ type Model interface {
 	protocol.Model
 	protocol.Model
 	AddConnection(conn Connection, hello protocol.HelloResult)
 	AddConnection(conn Connection, hello protocol.HelloResult)
 	ConnectedTo(remoteID protocol.DeviceID) bool
 	ConnectedTo(remoteID protocol.DeviceID) bool
-	IsPaused(remoteID protocol.DeviceID) bool
 	OnHello(protocol.DeviceID, net.Addr, protocol.HelloResult) error
 	OnHello(protocol.DeviceID, net.Addr, protocol.HelloResult) error
 	GetHello(protocol.DeviceID) protocol.HelloIntf
 	GetHello(protocol.DeviceID) protocol.HelloIntf
 }
 }

+ 29 - 29
lib/db/structs.pb.go

@@ -982,33 +982,33 @@ var (
 )
 )
 
 
 var fileDescriptorStructs = []byte{
 var fileDescriptorStructs = []byte{
-	// 446 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x51, 0xd1, 0x8a, 0xd3, 0x40,
-	0x14, 0xed, 0x6c, 0xb3, 0xdb, 0xf6, 0xc6, 0xae, 0xee, 0x20, 0xcb, 0x50, 0x30, 0x0d, 0x05, 0x21,
-	0x08, 0xa6, 0x5a, 0xf1, 0xc5, 0xc7, 0x7d, 0x58, 0x10, 0x44, 0x24, 0x2e, 0xeb, 0x63, 0x69, 0x32,
-	0xb7, 0xd9, 0xc1, 0x64, 0x26, 0x66, 0x26, 0x85, 0xfa, 0x25, 0xbe, 0xb9, 0x9f, 0xd3, 0x47, 0xbf,
-	0x40, 0xb4, 0xfe, 0x88, 0x74, 0x92, 0xc6, 0x3c, 0xfa, 0x76, 0xce, 0xbd, 0xe7, 0xdc, 0x7b, 0x66,
-	0x2e, 0x8c, 0xb5, 0x29, 0xab, 0xc4, 0xe8, 0xb0, 0x28, 0x95, 0x51, 0xf4, 0x84, 0xc7, 0x93, 0xe7,
-	0xa9, 0x30, 0x77, 0x55, 0x1c, 0x26, 0x2a, 0x9f, 0xa7, 0x2a, 0x55, 0x73, 0xdb, 0x8a, 0xab, 0xb5,
-	0x65, 0x96, 0x58, 0x54, 0x5b, 0x26, 0xaf, 0x3b, 0x72, 0xbd, 0x95, 0x89, 0xb9, 0x13, 0x32, 0xed,
-	0xa0, 0x4c, 0xc4, 0xf5, 0x84, 0x44, 0x65, 0xf3, 0x18, 0x8b, 0xda, 0x36, 0xfb, 0x04, 0xee, 0xb5,
-	0xc8, 0xf0, 0x16, 0x4b, 0x2d, 0x94, 0xa4, 0x2f, 0x60, 0xb0, 0xa9, 0x21, 0x23, 0x3e, 0x09, 0xdc,
-	0xc5, 0xa3, 0xf0, 0x68, 0x0a, 0x6f, 0x31, 0x31, 0xaa, 0xbc, 0x72, 0x76, 0x3f, 0xa7, 0xbd, 0xe8,
-	0x28, 0xa3, 0x97, 0x70, 0xc6, 0x71, 0x23, 0x12, 0x64, 0x27, 0x3e, 0x09, 0x1e, 0x44, 0x0d, 0x9b,
-	0x5d, 0x83, 0xdb, 0x0c, 0x7d, 0x27, 0xb4, 0xa1, 0x2f, 0x61, 0xd8, 0x38, 0x34, 0x23, 0x7e, 0x3f,
-	0x70, 0x17, 0x0f, 0x43, 0x1e, 0x87, 0x9d, 0xdd, 0xcd, 0xe0, 0x56, 0xf6, 0xc6, 0xf9, 0x76, 0x3f,
-	0xed, 0xcd, 0xbe, 0xf7, 0xe1, 0xe2, 0xa0, 0x7a, 0x2b, 0xd7, 0xea, 0xa6, 0xac, 0x64, 0xb2, 0x32,
-	0xc8, 0x29, 0x05, 0x47, 0xae, 0x72, 0xb4, 0x21, 0x47, 0x91, 0xc5, 0xf4, 0x19, 0x38, 0x66, 0x5b,
-	0xd4, 0x39, 0xce, 0x17, 0x97, 0xff, 0x82, 0xb7, 0xf6, 0x6d, 0x81, 0x91, 0xd5, 0x1c, 0xfc, 0x5a,
-	0x7c, 0x45, 0xd6, 0xf7, 0x49, 0xd0, 0x8f, 0x2c, 0xa6, 0x3e, 0xb8, 0x05, 0x96, 0xb9, 0xd0, 0x75,
-	0x4a, 0xc7, 0x27, 0xc1, 0x38, 0xea, 0x96, 0xe8, 0x13, 0x80, 0x5c, 0x71, 0xb1, 0x16, 0xc8, 0x97,
-	0x9a, 0x9d, 0x5a, 0xef, 0xe8, 0x58, 0xf9, 0x48, 0x19, 0x0c, 0x38, 0x66, 0x68, 0x90, 0xb3, 0x33,
-	0x9f, 0x04, 0xc3, 0xe8, 0x48, 0x0f, 0x1d, 0x21, 0x37, 0xab, 0x4c, 0x70, 0x36, 0xa8, 0x3b, 0x0d,
-	0xa5, 0x4f, 0xe1, 0x5c, 0xaa, 0x65, 0x77, 0xef, 0xd0, 0x0a, 0xc6, 0x52, 0x7d, 0xe8, 0x6c, 0xee,
-	0xdc, 0x65, 0xf4, 0x7f, 0x77, 0x99, 0xc0, 0x50, 0xe3, 0x97, 0x0a, 0x65, 0x82, 0x0c, 0x6c, 0xd2,
-	0x96, 0xd3, 0x29, 0xb8, 0xed, 0x3b, 0xa4, 0x66, 0xae, 0x4f, 0x82, 0xd3, 0xa8, 0x7d, 0xda, 0x7b,
-	0x7d, 0x48, 0xa5, 0xb7, 0x79, 0x26, 0xe4, 0xe7, 0xa5, 0x59, 0x95, 0x29, 0x1a, 0x76, 0x61, 0x3f,
-	0x7a, 0xdc, 0x54, 0x6f, 0x6c, 0xb1, 0xbe, 0xd0, 0xd5, 0xe3, 0xdd, 0x6f, 0xaf, 0xb7, 0xdb, 0x7b,
-	0xe4, 0xc7, 0xde, 0x23, 0xbf, 0xf6, 0x5e, 0xef, 0xfe, 0x8f, 0x47, 0xe2, 0x33, 0x9b, 0xef, 0xd5,
-	0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb1, 0x2f, 0x12, 0xb6, 0xda, 0x02, 0x00, 0x00,
+	// 442 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x51, 0x5d, 0x8b, 0xd3, 0x40,
+	0x14, 0x6d, 0xb7, 0xd9, 0x7e, 0xdc, 0xd8, 0xd5, 0x1d, 0x64, 0x19, 0x0a, 0xb6, 0x4b, 0x41, 0x10,
+	0xc1, 0x54, 0x57, 0x7c, 0xf1, 0x71, 0x1f, 0x16, 0x04, 0x11, 0x19, 0x97, 0xf5, 0xb1, 0x34, 0x99,
+	0xdb, 0xec, 0x60, 0x32, 0x53, 0x33, 0x93, 0x42, 0xfd, 0x25, 0xbe, 0xb9, 0x3f, 0xa7, 0x8f, 0xfe,
+	0x02, 0xd1, 0xfa, 0x47, 0x9c, 0xce, 0xa4, 0x31, 0x8f, 0xfb, 0x10, 0xb8, 0xe7, 0x9e, 0x73, 0xee,
+	0x3d, 0x93, 0x0b, 0x43, 0x6d, 0x8a, 0x32, 0x31, 0x3a, 0x5a, 0x15, 0xca, 0x28, 0x72, 0xc4, 0xe3,
+	0xd1, 0x8b, 0x54, 0x98, 0xdb, 0x32, 0x8e, 0x12, 0x95, 0xcf, 0x52, 0x95, 0xaa, 0x99, 0xa3, 0xe2,
+	0x72, 0xe9, 0x90, 0x03, 0xae, 0xf2, 0x96, 0xd1, 0x9b, 0x86, 0x5c, 0x6f, 0x64, 0x62, 0x6e, 0x85,
+	0x4c, 0x1b, 0x55, 0x26, 0x62, 0x3f, 0x21, 0x51, 0xd9, 0x2c, 0xc6, 0x95, 0xb7, 0x4d, 0x3f, 0x43,
+	0x78, 0x25, 0x32, 0xbc, 0xc1, 0x42, 0x0b, 0x25, 0xc9, 0x4b, 0xe8, 0xad, 0x7d, 0x49, 0xdb, 0xe7,
+	0xed, 0x67, 0xe1, 0xc5, 0xa3, 0xe8, 0x60, 0x8a, 0x6e, 0x30, 0x31, 0xaa, 0xb8, 0x0c, 0xb6, 0xbf,
+	0x26, 0x2d, 0x76, 0x90, 0x91, 0x33, 0xe8, 0x72, 0x5c, 0x8b, 0x04, 0xe9, 0x91, 0x35, 0x3c, 0x60,
+	0x15, 0x9a, 0x5e, 0x41, 0x58, 0x0d, 0x7d, 0x2f, 0xb4, 0x21, 0xaf, 0xa0, 0x5f, 0x39, 0xb4, 0x9d,
+	0xdc, 0xb1, 0x93, 0x1f, 0x46, 0x3c, 0x8e, 0x1a, 0xbb, 0xab, 0xc1, 0xb5, 0xec, 0x6d, 0xf0, 0xfd,
+	0x6e, 0xd2, 0x9a, 0xfe, 0xe8, 0xc0, 0xe9, 0x5e, 0xf5, 0x4e, 0x2e, 0xd5, 0x75, 0x51, 0xca, 0x64,
+	0x61, 0x90, 0x13, 0x02, 0x81, 0x5c, 0xe4, 0xe8, 0x42, 0x0e, 0x98, 0xab, 0xc9, 0x73, 0x08, 0xcc,
+	0x66, 0xe5, 0x73, 0x9c, 0x5c, 0x9c, 0xfd, 0x0f, 0x5e, 0xdb, 0x2d, 0xcb, 0x9c, 0x66, 0xef, 0xd7,
+	0xe2, 0x1b, 0xd2, 0x8e, 0xd5, 0x76, 0x98, 0xab, 0xc9, 0x39, 0x84, 0x2b, 0x2c, 0x72, 0xa1, 0x7d,
+	0xca, 0xc0, 0x52, 0x43, 0xd6, 0x6c, 0x91, 0x27, 0x00, 0xb9, 0xe2, 0x62, 0x29, 0x90, 0xcf, 0x35,
+	0x3d, 0x76, 0xde, 0xc1, 0xa1, 0xf3, 0x89, 0x50, 0xe8, 0x71, 0xcc, 0xd0, 0xe6, 0xa3, 0x5d, 0xcb,
+	0xf5, 0xd9, 0x01, 0xee, 0x19, 0x21, 0xd7, 0x8b, 0x4c, 0x70, 0xda, 0xf3, 0x4c, 0x05, 0xc9, 0x53,
+	0x38, 0x91, 0x6a, 0xde, 0xdc, 0xdb, 0x77, 0x82, 0xa1, 0x54, 0x1f, 0x1b, 0x9b, 0x1b, 0x77, 0x19,
+	0xdc, 0xef, 0x2e, 0x23, 0xe8, 0x6b, 0xfc, 0x5a, 0xa2, 0xb4, 0x97, 0x01, 0x97, 0xb4, 0xc6, 0x64,
+	0x02, 0x61, 0xfd, 0x0e, 0xbb, 0x31, 0xb4, 0xf4, 0x31, 0xab, 0x9f, 0xf6, 0x41, 0xef, 0x53, 0xe9,
+	0x4d, 0x9e, 0x09, 0xf9, 0x65, 0x6e, 0x16, 0x45, 0x8a, 0x86, 0x9e, 0xba, 0x1f, 0x3d, 0xac, 0xba,
+	0xd7, 0xae, 0xe9, 0x2f, 0x74, 0xf9, 0x78, 0xfb, 0x67, 0xdc, 0xda, 0xee, 0xc6, 0xed, 0x9f, 0xf6,
+	0xfb, 0xbd, 0x1b, 0xb7, 0xee, 0xfe, 0x8e, 0xdb, 0x71, 0xd7, 0xe5, 0x7b, 0xfd, 0x2f, 0x00, 0x00,
+	0xff, 0xff, 0xb1, 0x2f, 0x12, 0xb6, 0xda, 0x02, 0x00, 0x00,
 }
 }

+ 16 - 17
lib/discover/local.pb.go

@@ -382,21 +382,20 @@ var (
 )
 )
 
 
 var fileDescriptorLocal = []byte{
 var fileDescriptorLocal = []byte{
-	// 241 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x8e, 0x4f, 0x4e, 0x84, 0x30,
-	0x14, 0xc6, 0x29, 0x24, 0x66, 0xa6, 0x63, 0x5c, 0x10, 0x17, 0xc4, 0x98, 0x42, 0x5c, 0xb1, 0x11,
-	0x16, 0x7a, 0x01, 0x09, 0x9b, 0x6e, 0xb9, 0x80, 0x81, 0xb6, 0x32, 0x2f, 0xc1, 0x3e, 0x43, 0x61,
-	0x12, 0x6f, 0xe3, 0x05, 0xbc, 0x07, 0x4b, 0xd7, 0x2e, 0x1a, 0xad, 0x17, 0x31, 0xe9, 0x68, 0x86,
-	0xdd, 0xf7, 0xfd, 0xf2, 0x7b, 0x7f, 0xe8, 0x6e, 0x40, 0xd1, 0x0e, 0xc5, 0xcb, 0x88, 0x13, 0xc6,
-	0x1b, 0x09, 0x46, 0xe0, 0x41, 0x8d, 0x57, 0xb7, 0x3d, 0x4c, 0xfb, 0xb9, 0x2b, 0x04, 0x3e, 0x97,
-	0x3d, 0xf6, 0x58, 0x7a, 0xa1, 0x9b, 0x9f, 0x7c, 0xf3, 0xc5, 0xa7, 0xe3, 0xe0, 0xcd, 0x3b, 0xa1,
-	0x9b, 0x07, 0xad, 0x71, 0xd6, 0x42, 0xc5, 0x0d, 0x0d, 0x41, 0x26, 0x24, 0x23, 0xf9, 0x79, 0x55,
-	0x2d, 0x36, 0x0d, 0x3e, 0x6d, 0x7a, 0xbf, 0xda, 0x67, 0x5e, 0xb5, 0x98, 0xf6, 0xa0, 0xfb, 0x55,
-	0x1a, 0xa0, 0x3b, 0x9e, 0x10, 0x38, 0x14, 0xb5, 0x3a, 0x80, 0x50, 0xbc, 0x76, 0x36, 0x0d, 0x79,
-	0xdd, 0x84, 0x20, 0xe3, 0x6b, 0xba, 0x6d, 0xa5, 0x1c, 0x95, 0x31, 0xca, 0x24, 0x61, 0x16, 0xe5,
-	0xdb, 0xe6, 0x04, 0xe2, 0x92, 0xee, 0x40, 0x9b, 0xa9, 0xd5, 0x42, 0x3d, 0x82, 0x4c, 0xa2, 0x8c,
-	0xe4, 0x51, 0x75, 0xe1, 0x6c, 0x4a, 0xf9, 0x1f, 0xe6, 0x75, 0x43, 0xff, 0x15, 0x2e, 0xab, 0xcb,
-	0xe5, 0x9b, 0x05, 0x8b, 0x63, 0xe4, 0xc3, 0x31, 0xf2, 0xe5, 0x58, 0xf0, 0xf6, 0xc3, 0x48, 0x77,
-	0xe6, 0x3f, 0xb8, 0xfb, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x46, 0x4f, 0x13, 0x14, 0x01, 0x00,
-	0x00,
+	// 235 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0xc9, 0x4f, 0x4e,
+	0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x48, 0xc9, 0x2c, 0x4e, 0xce, 0x2f, 0x4b,
+	0x2d, 0x92, 0xd2, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf,
+	0x4f, 0xcf, 0xd7, 0x07, 0x2b, 0x48, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0xa2, 0x51,
+	0x69, 0x2d, 0x23, 0x17, 0x87, 0x63, 0x5e, 0x5e, 0x7e, 0x69, 0x5e, 0x72, 0xaa, 0x50, 0x10, 0x17,
+	0x53, 0x66, 0x8a, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x8f, 0x93, 0xd3, 0x89, 0x7b, 0xf2, 0x0c, 0xb7,
+	0xee, 0xc9, 0x9b, 0x20, 0x99, 0x57, 0x5c, 0x99, 0x97, 0x5c, 0x92, 0x91, 0x99, 0x97, 0x8e, 0xc4,
+	0xca, 0xc9, 0x4c, 0x82, 0x58, 0x91, 0x9c, 0x9f, 0xa3, 0xe7, 0x92, 0x5a, 0x96, 0x99, 0x9c, 0xea,
+	0xe9, 0xf2, 0xe8, 0x9e, 0x3c, 0x93, 0xa7, 0x4b, 0x10, 0xd0, 0x34, 0x21, 0x19, 0x2e, 0xce, 0xc4,
+	0x94, 0x94, 0xa2, 0xd4, 0xe2, 0xe2, 0xd4, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0xce, 0x20, 0x84,
+	0x80, 0x90, 0x3e, 0x17, 0x77, 0x66, 0x5e, 0x71, 0x49, 0x22, 0xd0, 0xf6, 0x78, 0xa0, 0xd5, 0xcc,
+	0x40, 0xab, 0x99, 0x9d, 0xf8, 0x80, 0xda, 0xb9, 0x3c, 0xa1, 0xc2, 0x40, 0x63, 0xb8, 0x60, 0x4a,
+	0x3c, 0x53, 0x9c, 0x44, 0x4e, 0x3c, 0x94, 0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0x02, 0x10, 0x3f,
+	0x78, 0x24, 0xc7, 0xb0, 0xe0, 0xb1, 0x1c, 0x63, 0x12, 0x1b, 0xd8, 0x05, 0xc6, 0x80, 0x00, 0x00,
+	0x00, 0xff, 0xff, 0xa4, 0x46, 0x4f, 0x13, 0x14, 0x01, 0x00, 0x00,
 }
 }

+ 6 - 0
lib/events/events.go

@@ -43,6 +43,8 @@ const (
 	FolderCompletion
 	FolderCompletion
 	FolderErrors
 	FolderErrors
 	FolderScanProgress
 	FolderScanProgress
+	FolderPaused
+	FolderResumed
 	ListenAddressesChanged
 	ListenAddressesChanged
 	LoginAttempt
 	LoginAttempt
 
 
@@ -101,6 +103,10 @@ func (t EventType) String() string {
 		return "DeviceResumed"
 		return "DeviceResumed"
 	case FolderScanProgress:
 	case FolderScanProgress:
 		return "FolderScanProgress"
 		return "FolderScanProgress"
+	case FolderPaused:
+		return "FolderPaused"
+	case FolderResumed:
+		return "FolderResumed"
 	case ListenAddressesChanged:
 	case ListenAddressesChanged:
 		return "ListenAddressesChanged"
 		return "ListenAddressesChanged"
 	case LoginAttempt:
 	case LoginAttempt:

+ 130 - 79
lib/model/model.go

@@ -93,12 +93,12 @@ type Model struct {
 	folderStatRefs     map[string]*stats.FolderStatisticsReference            // folder -> statsRef
 	folderStatRefs     map[string]*stats.FolderStatisticsReference            // folder -> statsRef
 	fmut               sync.RWMutex                                           // protects the above
 	fmut               sync.RWMutex                                           // protects the above
 
 
-	conn            map[protocol.DeviceID]connections.Connection
-	closed          map[protocol.DeviceID]chan struct{}
-	helloMessages   map[protocol.DeviceID]protocol.HelloResult
-	devicePaused    map[protocol.DeviceID]bool
-	deviceDownloads map[protocol.DeviceID]*deviceDownloadState
-	pmut            sync.RWMutex // protects the above
+	conn                map[protocol.DeviceID]connections.Connection
+	closed              map[protocol.DeviceID]chan struct{}
+	helloMessages       map[protocol.DeviceID]protocol.HelloResult
+	deviceDownloads     map[protocol.DeviceID]*deviceDownloadState
+	remotePausedFolders map[protocol.DeviceID][]string // deviceID -> folders
+	pmut                sync.RWMutex                   // protects the above
 }
 }
 
 
 type folderFactory func(*Model, config.FolderConfiguration, versioner.Versioner, *fs.MtimeFS) service
 type folderFactory func(*Model, config.FolderConfiguration, versioner.Versioner, *fs.MtimeFS) service
@@ -134,33 +134,33 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
 				l.Debugln(line)
 				l.Debugln(line)
 			},
 			},
 		}),
 		}),
-		cfg:                cfg,
-		db:                 ldb,
-		finder:             db.NewBlockFinder(ldb),
-		progressEmitter:    NewProgressEmitter(cfg),
-		id:                 id,
-		shortID:            id.Short(),
-		cacheIgnoredFiles:  cfg.Options().CacheIgnoredFiles,
-		protectedFiles:     protectedFiles,
-		deviceName:         deviceName,
-		clientName:         clientName,
-		clientVersion:      clientVersion,
-		folderCfgs:         make(map[string]config.FolderConfiguration),
-		folderFiles:        make(map[string]*db.FileSet),
-		folderDevices:      make(folderDeviceSet),
-		deviceFolders:      make(map[protocol.DeviceID][]string),
-		deviceStatRefs:     make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
-		folderIgnores:      make(map[string]*ignore.Matcher),
-		folderRunners:      make(map[string]service),
-		folderRunnerTokens: make(map[string][]suture.ServiceToken),
-		folderStatRefs:     make(map[string]*stats.FolderStatisticsReference),
-		conn:               make(map[protocol.DeviceID]connections.Connection),
-		closed:             make(map[protocol.DeviceID]chan struct{}),
-		helloMessages:      make(map[protocol.DeviceID]protocol.HelloResult),
-		devicePaused:       make(map[protocol.DeviceID]bool),
-		deviceDownloads:    make(map[protocol.DeviceID]*deviceDownloadState),
-		fmut:               sync.NewRWMutex(),
-		pmut:               sync.NewRWMutex(),
+		cfg:                 cfg,
+		db:                  ldb,
+		finder:              db.NewBlockFinder(ldb),
+		progressEmitter:     NewProgressEmitter(cfg),
+		id:                  id,
+		shortID:             id.Short(),
+		cacheIgnoredFiles:   cfg.Options().CacheIgnoredFiles,
+		protectedFiles:      protectedFiles,
+		deviceName:          deviceName,
+		clientName:          clientName,
+		clientVersion:       clientVersion,
+		folderCfgs:          make(map[string]config.FolderConfiguration),
+		folderFiles:         make(map[string]*db.FileSet),
+		folderDevices:       make(folderDeviceSet),
+		deviceFolders:       make(map[protocol.DeviceID][]string),
+		deviceStatRefs:      make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
+		folderIgnores:       make(map[string]*ignore.Matcher),
+		folderRunners:       make(map[string]service),
+		folderRunnerTokens:  make(map[string][]suture.ServiceToken),
+		folderStatRefs:      make(map[string]*stats.FolderStatisticsReference),
+		conn:                make(map[protocol.DeviceID]connections.Connection),
+		closed:              make(map[protocol.DeviceID]chan struct{}),
+		helloMessages:       make(map[protocol.DeviceID]protocol.HelloResult),
+		deviceDownloads:     make(map[protocol.DeviceID]*deviceDownloadState),
+		remotePausedFolders: make(map[protocol.DeviceID][]string),
+		fmut:                sync.NewRWMutex(),
+		pmut:                sync.NewRWMutex(),
 	}
 	}
 	if cfg.Options().ProgressUpdateIntervalS > -1 {
 	if cfg.Options().ProgressUpdateIntervalS > -1 {
 		go m.progressEmitter.Serve()
 		go m.progressEmitter.Serve()
@@ -183,8 +183,10 @@ func (m *Model) StartDeadlockDetector(timeout time.Duration) {
 // StartFolder constructs the folder service and starts it.
 // StartFolder constructs the folder service and starts it.
 func (m *Model) StartFolder(folder string) {
 func (m *Model) StartFolder(folder string) {
 	m.fmut.Lock()
 	m.fmut.Lock()
+	m.pmut.Lock()
 	folderType := m.startFolderLocked(folder)
 	folderType := m.startFolderLocked(folder)
 	folderCfg := m.folderCfgs[folder]
 	folderCfg := m.folderCfgs[folder]
+	m.pmut.Unlock()
 	m.fmut.Unlock()
 	m.fmut.Unlock()
 
 
 	l.Infof("Ready to synchronize %s (%s)", folderCfg.Description(), folderType)
 	l.Infof("Ready to synchronize %s (%s)", folderCfg.Description(), folderType)
@@ -218,6 +220,11 @@ func (m *Model) startFolderLocked(folder string) config.FolderType {
 		}
 		}
 	}
 	}
 
 
+	// Close connections to affected devices
+	for _, id := range cfg.DeviceIDs() {
+		m.closeLocked(id)
+	}
+
 	v, ok := fs.Sequence(protocol.LocalDeviceID), true
 	v, ok := fs.Sequence(protocol.LocalDeviceID), true
 	indexHasFiles := ok && v > 0
 	indexHasFiles := ok && v > 0
 	if !indexHasFiles {
 	if !indexHasFiles {
@@ -366,13 +373,16 @@ func (m *Model) RestartFolder(cfg config.FolderConfiguration) {
 	m.pmut.Lock()
 	m.pmut.Lock()
 
 
 	m.tearDownFolderLocked(cfg.ID)
 	m.tearDownFolderLocked(cfg.ID)
-	m.addFolderLocked(cfg)
-	folderType := m.startFolderLocked(cfg.ID)
+	if !cfg.Paused {
+		m.addFolderLocked(cfg)
+		folderType := m.startFolderLocked(cfg.ID)
+		l.Infoln("Restarted folder", cfg.Description(), fmt.Sprintf("(%s)", folderType))
+	} else {
+		l.Infoln("Paused folder", cfg.Description())
+	}
 
 
 	m.pmut.Unlock()
 	m.pmut.Unlock()
 	m.fmut.Unlock()
 	m.fmut.Unlock()
-
-	l.Infoln("Restarted folder", cfg.ID, fmt.Sprintf("(%s)", folderType))
 }
 }
 
 
 type ConnectionInfo struct {
 type ConnectionInfo struct {
@@ -405,7 +415,7 @@ func (m *Model) ConnectionStats() map[string]interface{} {
 	res := make(map[string]interface{})
 	res := make(map[string]interface{})
 	devs := m.cfg.Devices()
 	devs := m.cfg.Devices()
 	conns := make(map[string]ConnectionInfo, len(devs))
 	conns := make(map[string]ConnectionInfo, len(devs))
-	for device := range devs {
+	for device, deviceCfg := range devs {
 		hello := m.helloMessages[device]
 		hello := m.helloMessages[device]
 		versionString := hello.ClientVersion
 		versionString := hello.ClientVersion
 		if hello.ClientName != "syncthing" {
 		if hello.ClientName != "syncthing" {
@@ -413,7 +423,7 @@ func (m *Model) ConnectionStats() map[string]interface{} {
 		}
 		}
 		ci := ConnectionInfo{
 		ci := ConnectionInfo{
 			ClientVersion: strings.TrimSpace(versionString),
 			ClientVersion: strings.TrimSpace(versionString),
-			Paused:        m.devicePaused[device],
+			Paused:        deviceCfg.Paused,
 		}
 		}
 		if conn, ok := m.conn[device]; ok {
 		if conn, ok := m.conn[device]; ok {
 			ci.Type = conn.Type()
 			ci.Type = conn.Type()
@@ -783,7 +793,17 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 	}
 	}
 
 
 	m.fmut.Lock()
 	m.fmut.Lock()
+	var paused []string
 	for _, folder := range cm.Folders {
 	for _, folder := range cm.Folders {
+		if folder.Paused {
+			paused = append(paused, folder.ID)
+			continue
+		}
+
+		if cfg, ok := m.cfg.Folder(folder.ID); ok && cfg.Paused {
+			continue
+		}
+
 		if !m.folderSharedWithLocked(folder.ID, deviceID) {
 		if !m.folderSharedWithLocked(folder.ID, deviceID) {
 			events.Default.Log(events.FolderRejected, map[string]string{
 			events.Default.Log(events.FolderRejected, map[string]string{
 				"folder":      folder.ID,
 				"folder":      folder.ID,
@@ -871,6 +891,10 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 		go sendIndexes(conn, folder.ID, fs, m.folderIgnores[folder.ID], startSequence, dbLocation, dropSymlinks)
 		go sendIndexes(conn, folder.ID, fs, m.folderIgnores[folder.ID], startSequence, dbLocation, dropSymlinks)
 	}
 	}
 
 
+	m.pmut.Lock()
+	m.remotePausedFolders[deviceID] = paused
+	m.pmut.Unlock()
+
 	// This breaks if we send multiple CM messages during the same connection.
 	// This breaks if we send multiple CM messages during the same connection.
 	if len(tempIndexFolders) > 0 {
 	if len(tempIndexFolders) > 0 {
 		m.pmut.RLock()
 		m.pmut.RLock()
@@ -1058,6 +1082,7 @@ func (m *Model) Closed(conn protocol.Connection, err error) {
 	delete(m.conn, device)
 	delete(m.conn, device)
 	delete(m.helloMessages, device)
 	delete(m.helloMessages, device)
 	delete(m.deviceDownloads, device)
 	delete(m.deviceDownloads, device)
+	delete(m.remotePausedFolders, device)
 	closed := m.closed[device]
 	closed := m.closed[device]
 	delete(m.closed, device)
 	delete(m.closed, device)
 	m.pmut.Unlock()
 	m.pmut.Unlock()
@@ -1070,6 +1095,24 @@ func (m *Model) Closed(conn protocol.Connection, err error) {
 	close(closed)
 	close(closed)
 }
 }
 
 
+// close will close the underlying connection for a given device
+func (m *Model) close(device protocol.DeviceID) {
+	m.pmut.Lock()
+	m.closeLocked(device)
+	m.pmut.Unlock()
+}
+
+// closeLocked will close the underlying connection for a given device
+func (m *Model) closeLocked(device protocol.DeviceID) {
+	conn, ok := m.conn[device]
+	if !ok {
+		// There is no connection to close
+		return
+	}
+
+	closeRawConn(conn)
+}
+
 // Request returns the specified data segment by reading it from local disk.
 // Request returns the specified data segment by reading it from local disk.
 // Implements the protocol.Model interface.
 // Implements the protocol.Model interface.
 func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
 func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
@@ -1257,16 +1300,15 @@ func (m *Model) SetIgnores(folder string, content []string) error {
 // This allows us to extract some information from the Hello message
 // This allows us to extract some information from the Hello message
 // and add it to a list of known devices ahead of any checks.
 // and add it to a list of known devices ahead of any checks.
 func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloResult) error {
 func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloResult) error {
-	if m.IsPaused(remoteID) {
-		return errDevicePaused
-	}
-
 	if m.cfg.IgnoredDevice(remoteID) {
 	if m.cfg.IgnoredDevice(remoteID) {
 		return errDeviceIgnored
 		return errDeviceIgnored
 	}
 	}
 
 
-	if _, ok := m.cfg.Device(remoteID); ok {
+	if cfg, ok := m.cfg.Device(remoteID); ok {
 		// The device exists
 		// The device exists
+		if cfg.Paused {
+			return errDevicePaused
+		}
 		return nil
 		return nil
 	}
 	}
 
 
@@ -1349,17 +1391,6 @@ func (m *Model) AddConnection(conn connections.Connection, hello protocol.HelloR
 	m.deviceWasSeen(deviceID)
 	m.deviceWasSeen(deviceID)
 }
 }
 
 
-func (m *Model) PauseDevice(device protocol.DeviceID) {
-	m.pmut.Lock()
-	m.devicePaused[device] = true
-	conn, ok := m.conn[device]
-	m.pmut.Unlock()
-	if ok {
-		closeRawConn(conn)
-	}
-	events.Default.Log(events.DevicePaused, map[string]string{"device": device.String()})
-}
-
 func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
 func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
 	if !m.folderSharedWith(folder, device) {
 	if !m.folderSharedWith(folder, device) {
 		return
 		return
@@ -1385,20 +1416,6 @@ func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, update
 	})
 	})
 }
 }
 
 
-func (m *Model) ResumeDevice(device protocol.DeviceID) {
-	m.pmut.Lock()
-	m.devicePaused[device] = false
-	m.pmut.Unlock()
-	events.Default.Log(events.DeviceResumed, map[string]string{"device": device.String()})
-}
-
-func (m *Model) IsPaused(device protocol.DeviceID) bool {
-	m.pmut.Lock()
-	paused := m.devicePaused[device]
-	m.pmut.Unlock()
-	return paused
-}
-
 func (m *Model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatisticsReference {
 func (m *Model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatisticsReference {
 	m.fmut.Lock()
 	m.fmut.Lock()
 	defer m.fmut.Unlock()
 	defer m.fmut.Unlock()
@@ -1983,6 +2000,7 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
 			IgnorePermissions:  folderCfg.IgnorePerms,
 			IgnorePermissions:  folderCfg.IgnorePerms,
 			IgnoreDelete:       folderCfg.IgnoreDelete,
 			IgnoreDelete:       folderCfg.IgnoreDelete,
 			DisableTempIndexes: folderCfg.DisableTempIndexes,
 			DisableTempIndexes: folderCfg.DisableTempIndexes,
+			Paused:             folderCfg.Paused,
 		}
 		}
 
 
 		// Devices are sorted, so we always get the same order.
 		// Devices are sorted, so we always get the same order.
@@ -2192,7 +2210,13 @@ func (m *Model) Availability(folder, file string, version protocol.Vector, block
 	}
 	}
 
 
 	var availabilities []Availability
 	var availabilities []Availability
+next:
 	for _, device := range fs.Availability(file) {
 	for _, device := range fs.Availability(file) {
+		for _, pausedFolder := range m.remotePausedFolders[device] {
+			if pausedFolder == folder {
+				continue next
+			}
+		}
 		_, ok := m.conn[device]
 		_, ok := m.conn[device]
 		if ok {
 		if ok {
 			availabilities = append(availabilities, Availability{ID: device, FromTemporary: false})
 			availabilities = append(availabilities, Availability{ID: device, FromTemporary: false})
@@ -2350,16 +2374,6 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
 			l.Debugln(m, "adding folder", folderID)
 			l.Debugln(m, "adding folder", folderID)
 			m.AddFolder(cfg)
 			m.AddFolder(cfg)
 			m.StartFolder(folderID)
 			m.StartFolder(folderID)
-
-			// Drop connections to all devices that can now share the new
-			// folder.
-			m.pmut.Lock()
-			for _, dev := range cfg.DeviceIDs() {
-				if conn, ok := m.conn[dev]; ok {
-					closeRawConn(conn)
-				}
-			}
-			m.pmut.Unlock()
 		}
 		}
 	}
 	}
 
 
@@ -2381,6 +2395,15 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
 		if !reflect.DeepEqual(fromCfgCopy, toCfgCopy) {
 		if !reflect.DeepEqual(fromCfgCopy, toCfgCopy) {
 			m.RestartFolder(toCfg)
 			m.RestartFolder(toCfg)
 		}
 		}
+
+		// Emit the folder pause/resume event
+		if fromCfg.Paused != toCfg.Paused {
+			eventType := events.FolderResumed
+			if toCfg.Paused {
+				eventType = events.FolderPaused
+			}
+			events.Default.Log(eventType, map[string]string{"id": toCfg.ID, "label": toCfg.Label})
+		}
 	}
 	}
 
 
 	// Removing a device. We actually don't need to do anything.
 	// Removing a device. We actually don't need to do anything.
@@ -2390,6 +2413,24 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
 	// At some point model.Close() will get called for that device which will
 	// At some point model.Close() will get called for that device which will
 	// clean residue device state that is not part of any folder.
 	// clean residue device state that is not part of any folder.
 
 
+	// Pausing a device, unpausing is handled by the connection service.
+	fromDevices := mapDeviceConfigs(from.Devices)
+	toDevices := mapDeviceConfigs(to.Devices)
+	for deviceID, toCfg := range toDevices {
+		fromCfg, ok := fromDevices[deviceID]
+		if !ok || fromCfg.Paused == toCfg.Paused {
+			continue
+		}
+
+		if toCfg.Paused {
+			l.Infoln("Pausing", deviceID)
+			m.close(deviceID)
+			events.Default.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
+		} else {
+			events.Default.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
+		}
+	}
+
 	// Some options don't require restart as those components handle it fine
 	// Some options don't require restart as those components handle it fine
 	// by themselves.
 	// by themselves.
 	from.Options.URAccepted = to.Options.URAccepted
 	from.Options.URAccepted = to.Options.URAccepted
@@ -2431,6 +2472,16 @@ func mapDevices(devices []protocol.DeviceID) map[protocol.DeviceID]struct{} {
 	return m
 	return m
 }
 }
 
 
+// mapDeviceConfigs returns a map of device ID to device configuration for the given
+// slice of folder configurations.
+func mapDeviceConfigs(devices []config.DeviceConfiguration) map[protocol.DeviceID]config.DeviceConfiguration {
+	m := make(map[protocol.DeviceID]config.DeviceConfiguration, len(devices))
+	for _, dev := range devices {
+		m[dev.DeviceID] = dev
+	}
+	return m
+}
+
 func symlinkInvalid(folder string, fi db.FileIntf) bool {
 func symlinkInvalid(folder string, fi db.FileIntf) bool {
 	if !symlinks.Supported && fi.IsSymlink() && !fi.IsInvalid() && !fi.IsDeleted() {
 	if !symlinks.Supported && fi.IsSymlink() && !fi.IsInvalid() && !fi.IsDeleted() {
 		symlinkWarning.Do(func() {
 		symlinkWarning.Do(func() {

+ 91 - 0
lib/model/model_test.go

@@ -2198,6 +2198,97 @@ func TestIssue3829(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestNoRequestsFromPausedDevices(t *testing.T) {
+	dbi := db.OpenMemory()
+
+	fcfg := config.NewFolderConfiguration("default", "testdata")
+	fcfg.Devices = []config.FolderDeviceConfiguration{
+		{DeviceID: device1},
+		{DeviceID: device2},
+	}
+	cfg := config.Configuration{
+		Folders: []config.FolderConfiguration{fcfg},
+		Devices: []config.DeviceConfiguration{
+			config.NewDeviceConfiguration(device1, "device1"),
+			config.NewDeviceConfiguration(device2, "device2"),
+		},
+		Options: config.OptionsConfiguration{
+			// Don't remove temporaries directly on startup
+			KeepTemporariesH: 1,
+		},
+	}
+
+	wcfg := config.Wrap("/tmp/test", cfg)
+
+	m := NewModel(wcfg, protocol.LocalDeviceID, "device", "syncthing", "dev", dbi, nil)
+	m.AddFolder(fcfg)
+	m.StartFolder(fcfg.ID)
+	m.ServeBackground()
+
+	file := testDataExpected["foo"]
+	files := m.folderFiles["default"]
+	files.Update(device1, []protocol.FileInfo{file})
+	files.Update(device2, []protocol.FileInfo{file})
+
+	avail := m.Availability("default", file.Name, file.Version, file.Blocks[0])
+	if len(avail) != 0 {
+		t.Errorf("should not be available, no connections")
+	}
+
+	addFakeConn(m, device1)
+	addFakeConn(m, device2)
+
+	// !!! This is not what I'd expect to happen, as we don't even know if the peer has the original index !!!
+
+	avail = m.Availability("default", file.Name, file.Version, file.Blocks[0])
+	if len(avail) != 2 {
+		t.Errorf("should have two available")
+	}
+
+	cc := protocol.ClusterConfig{
+		Folders: []protocol.Folder{
+			{
+				ID: "default",
+				Devices: []protocol.Device{
+					{ID: device1},
+					{ID: device2},
+				},
+			},
+		},
+	}
+
+	m.ClusterConfig(device1, cc)
+	m.ClusterConfig(device2, cc)
+
+	avail = m.Availability("default", file.Name, file.Version, file.Blocks[0])
+	if len(avail) != 2 {
+		t.Errorf("should have two available")
+	}
+
+	m.Closed(&fakeConnection{id: device1}, errDeviceUnknown)
+	m.Closed(&fakeConnection{id: device2}, errDeviceUnknown)
+
+	avail = m.Availability("default", file.Name, file.Version, file.Blocks[0])
+	if len(avail) != 0 {
+		t.Errorf("should have no available")
+	}
+
+	// Test that remote paused folders are not used.
+
+	addFakeConn(m, device1)
+	addFakeConn(m, device2)
+
+	m.ClusterConfig(device1, cc)
+	ccp := cc
+	ccp.Folders[0].Paused = true
+	m.ClusterConfig(device1, ccp)
+
+	avail = m.Availability("default", file.Name, file.Version, file.Blocks[0])
+	if len(avail) != 1 {
+		t.Errorf("should have one available")
+	}
+}
+
 func TestRootedJoinedPath(t *testing.T) {
 func TestRootedJoinedPath(t *testing.T) {
 	type testcase struct {
 	type testcase struct {
 		root   string
 		root   string

+ 140 - 108
lib/protocol/bep.pb.go

@@ -246,6 +246,7 @@ type Folder struct {
 	IgnorePermissions  bool     `protobuf:"varint,4,opt,name=ignore_permissions,json=ignorePermissions,proto3" json:"ignore_permissions,omitempty"`
 	IgnorePermissions  bool     `protobuf:"varint,4,opt,name=ignore_permissions,json=ignorePermissions,proto3" json:"ignore_permissions,omitempty"`
 	IgnoreDelete       bool     `protobuf:"varint,5,opt,name=ignore_delete,json=ignoreDelete,proto3" json:"ignore_delete,omitempty"`
 	IgnoreDelete       bool     `protobuf:"varint,5,opt,name=ignore_delete,json=ignoreDelete,proto3" json:"ignore_delete,omitempty"`
 	DisableTempIndexes bool     `protobuf:"varint,6,opt,name=disable_temp_indexes,json=disableTempIndexes,proto3" json:"disable_temp_indexes,omitempty"`
 	DisableTempIndexes bool     `protobuf:"varint,6,opt,name=disable_temp_indexes,json=disableTempIndexes,proto3" json:"disable_temp_indexes,omitempty"`
+	Paused             bool     `protobuf:"varint,7,opt,name=paused,proto3" json:"paused,omitempty"`
 	Devices            []Device `protobuf:"bytes,16,rep,name=devices" json:"devices"`
 	Devices            []Device `protobuf:"bytes,16,rep,name=devices" json:"devices"`
 }
 }
 
 
@@ -593,6 +594,16 @@ func (m *Folder) MarshalTo(data []byte) (int, error) {
 		}
 		}
 		i++
 		i++
 	}
 	}
+	if m.Paused {
+		data[i] = 0x38
+		i++
+		if m.Paused {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
 	if len(m.Devices) > 0 {
 	if len(m.Devices) > 0 {
 		for _, msg := range m.Devices {
 		for _, msg := range m.Devices {
 			data[i] = 0x82
 			data[i] = 0x82
@@ -1306,6 +1317,9 @@ func (m *Folder) ProtoSize() (n int) {
 	if m.DisableTempIndexes {
 	if m.DisableTempIndexes {
 		n += 2
 		n += 2
 	}
 	}
+	if m.Paused {
+		n += 2
+	}
 	if len(m.Devices) > 0 {
 	if len(m.Devices) > 0 {
 		for _, e := range m.Devices {
 		for _, e := range m.Devices {
 			l = e.ProtoSize()
 			l = e.ProtoSize()
@@ -2065,6 +2079,26 @@ func (m *Folder) Unmarshal(data []byte) error {
 				}
 				}
 			}
 			}
 			m.DisableTempIndexes = bool(v != 0)
 			m.DisableTempIndexes = bool(v != 0)
+		case 7:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Paused", 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.Paused = bool(v != 0)
 		case 16:
 		case 16:
 			if wireType != 2 {
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType)
 				return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType)
@@ -4140,112 +4174,110 @@ var (
 )
 )
 
 
 var fileDescriptorBep = []byte{
 var fileDescriptorBep = []byte{
-	// 1700 bytes of a gzipped FileDescriptorProto
-	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,
-	0xb2, 0x4b, 0xca, 0xae, 0x73, 0x28, 0x06, 0x24, 0x96, 0x14, 0x46, 0x20, 0x96, 0x05, 0x40, 0xd9,
-	0xea, 0x47, 0x60, 0xbf, 0x40, 0x2f, 0x9c, 0xc9, 0xb5, 0xf7, 0x7e, 0x08, 0xf7, 0x96, 0xc9, 0xb1,
-	0x07, 0x4f, 0xa3, 0x5e, 0x7a, 0xec, 0xa5, 0xf7, 0x0e, 0x76, 0x01, 0x10, 0x94, 0xe4, 0x8e, 0x0f,
-	0x3d, 0x71, 0xf7, 0xbd, 0xdf, 0xbe, 0xdd, 0xf7, 0x7b, 0xef, 0xf7, 0x08, 0xa8, 0x0c, 0xc9, 0xec,
-	0x68, 0xe6, 0xd3, 0x90, 0xa2, 0x32, 0xfb, 0x19, 0x51, 0x57, 0xf9, 0x6c, 0xe2, 0x84, 0xe7, 0xf3,
-	0xe1, 0xd1, 0x88, 0x4e, 0x3f, 0x9f, 0xd0, 0x09, 0xfd, 0x9c, 0x79, 0x86, 0xf3, 0x31, 0xdb, 0xb1,
-	0x0d, 0x5b, 0xf1, 0x83, 0xda, 0x0c, 0x0a, 0x4f, 0x89, 0xeb, 0x52, 0xb4, 0x0f, 0x55, 0x9b, 0x5c,
-	0x3a, 0x23, 0x62, 0x7a, 0xd6, 0x94, 0xc8, 0x82, 0x2a, 0x1c, 0x54, 0x30, 0x70, 0x93, 0x61, 0x4d,
-	0x49, 0x04, 0x18, 0xb9, 0x0e, 0xf1, 0x42, 0x0e, 0xc8, 0x71, 0x00, 0x37, 0x31, 0xc0, 0x23, 0xd8,
-	0x8a, 0x01, 0x97, 0xc4, 0x0f, 0x1c, 0xea, 0xc9, 0x79, 0x86, 0xd9, 0xe4, 0xd6, 0xe7, 0xdc, 0xa8,
-	0x05, 0x50, 0x7c, 0x4a, 0x2c, 0x9b, 0xf8, 0xe8, 0x53, 0x10, 0xc3, 0xab, 0x19, 0xbf, 0x6b, 0xeb,
-	0x8b, 0xfb, 0x47, 0x49, 0x0e, 0x47, 0xcf, 0x48, 0x10, 0x58, 0x13, 0x32, 0xb8, 0x9a, 0x11, 0xcc,
-	0x20, 0xe8, 0x37, 0x50, 0x1d, 0xd1, 0xe9, 0xcc, 0x27, 0x01, 0x0b, 0x9c, 0x63, 0x27, 0xf6, 0x6e,
-	0x9d, 0x68, 0xae, 0x30, 0x38, 0x7b, 0x40, 0x6b, 0xc0, 0x66, 0xd3, 0x9d, 0x07, 0x21, 0xf1, 0x9b,
-	0xd4, 0x1b, 0x3b, 0x13, 0xf4, 0x18, 0x4a, 0x63, 0xea, 0xda, 0xc4, 0x0f, 0x64, 0x41, 0xcd, 0x1f,
-	0x54, 0xbf, 0x90, 0x56, 0xc1, 0x4e, 0x98, 0xe3, 0x58, 0x7c, 0xf3, 0x76, 0x7f, 0x03, 0x27, 0x30,
-	0xed, 0x4f, 0x39, 0x28, 0x72, 0x0f, 0xda, 0x85, 0x9c, 0x63, 0x73, 0x8a, 0x8e, 0x8b, 0xd7, 0x6f,
-	0xf7, 0x73, 0xed, 0x16, 0xce, 0x39, 0x36, 0xda, 0x81, 0x82, 0x6b, 0x0d, 0x89, 0x1b, 0x93, 0xc3,
-	0x37, 0xe8, 0x01, 0x54, 0x7c, 0x62, 0xd9, 0x26, 0xf5, 0xdc, 0x2b, 0x46, 0x49, 0x19, 0x97, 0x23,
-	0x43, 0xd7, 0x73, 0xaf, 0xd0, 0x67, 0x80, 0x9c, 0x89, 0x47, 0x7d, 0x62, 0xce, 0x88, 0x3f, 0x75,
-	0xd8, 0x6b, 0x03, 0x59, 0x64, 0xa8, 0x6d, 0xee, 0xe9, 0xad, 0x1c, 0xe8, 0x21, 0x6c, 0xc6, 0x70,
-	0x9b, 0xb8, 0x24, 0x24, 0x72, 0x81, 0x21, 0x6b, 0xdc, 0xd8, 0x62, 0x36, 0xf4, 0x18, 0x76, 0x6c,
-	0x27, 0xb0, 0x86, 0x2e, 0x31, 0x43, 0x32, 0x9d, 0x99, 0x8e, 0x67, 0x93, 0xd7, 0x24, 0x90, 0x8b,
-	0x0c, 0x8b, 0x62, 0xdf, 0x80, 0x4c, 0x67, 0x6d, 0xee, 0x89, 0xd8, 0xe0, 0x95, 0x0e, 0x64, 0xe9,
-	0x26, 0x1b, 0x2d, 0xe6, 0x48, 0xd8, 0x88, 0x61, 0xda, 0xbf, 0x73, 0x50, 0xe4, 0x1e, 0xf4, 0xe3,
-	0x94, 0x8d, 0xda, 0xf1, 0x6e, 0x84, 0xfa, 0xfb, 0xdb, 0xfd, 0x32, 0xf7, 0xb5, 0x5b, 0x19, 0x76,
-	0x10, 0x88, 0x99, 0xce, 0x61, 0x6b, 0xb4, 0x07, 0x15, 0xcb, 0xb6, 0xa3, 0x2a, 0x91, 0x40, 0xce,
-	0xab, 0xf9, 0x83, 0x0a, 0x5e, 0x19, 0xd0, 0x57, 0xeb, 0x55, 0x17, 0x6f, 0xf6, 0xc9, 0xbb, 0xca,
-	0x1d, 0x51, 0x3e, 0x22, 0x7e, 0xdc, 0xa9, 0x05, 0x76, 0x5f, 0x39, 0x32, 0xb0, 0x3e, 0xfd, 0x18,
-	0x6a, 0x53, 0xeb, 0xb5, 0x19, 0x90, 0x3f, 0xcc, 0x89, 0x37, 0x22, 0x8c, 0x96, 0x3c, 0xae, 0x4e,
-	0xad, 0xd7, 0xfd, 0xd8, 0x84, 0xea, 0x00, 0x8e, 0x17, 0xfa, 0xd4, 0x9e, 0x8f, 0x88, 0x2f, 0x97,
-	0x18, 0x6f, 0x19, 0x0b, 0xfa, 0x39, 0x94, 0x19, 0xa9, 0xa6, 0x63, 0xcb, 0x65, 0x55, 0x38, 0x10,
-	0x8f, 0x95, 0x38, 0xf1, 0x12, 0xa3, 0x94, 0xe5, 0x9d, 0x2c, 0x71, 0x89, 0x61, 0xdb, 0x36, 0xfa,
-	0x15, 0x28, 0xc1, 0x85, 0x13, 0x15, 0x84, 0x47, 0x0a, 0x1d, 0xea, 0x99, 0x3e, 0x99, 0xd2, 0x4b,
-	0xcb, 0x0d, 0xe4, 0x0a, 0xbb, 0x46, 0x8e, 0x10, 0xed, 0x0c, 0x00, 0xc7, 0x7e, 0xad, 0x0b, 0x05,
-	0x16, 0x11, 0xed, 0x42, 0x91, 0x37, 0x65, 0xac, 0xd2, 0x78, 0x87, 0x8e, 0xa0, 0x30, 0x76, 0x5c,
-	0x12, 0xc8, 0x39, 0x56, 0x43, 0x94, 0xe9, 0x68, 0xc7, 0x25, 0x6d, 0x6f, 0x4c, 0xe3, 0x2a, 0x72,
-	0x98, 0x76, 0x06, 0x55, 0x16, 0xf0, 0x6c, 0x66, 0x5b, 0x21, 0xf9, 0xbf, 0x85, 0xfd, 0x5b, 0x1e,
-	0xca, 0x89, 0x27, 0x2d, 0xba, 0x90, 0x29, 0xfa, 0x61, 0xac, 0x7b, 0xae, 0xe2, 0xdd, 0xdb, 0xf1,
-	0x32, 0xc2, 0x47, 0x20, 0x06, 0xce, 0x1f, 0x09, 0xd3, 0x4d, 0x1e, 0xb3, 0x35, 0x52, 0xa1, 0x7a,
-	0x53, 0x2c, 0x9b, 0x38, 0x6b, 0x42, 0x1f, 0x01, 0x4c, 0xa9, 0xed, 0x8c, 0x1d, 0x62, 0x9b, 0x01,
-	0x6b, 0x80, 0x3c, 0xae, 0x24, 0x96, 0x3e, 0x92, 0xa3, 0x76, 0x8f, 0xa4, 0x62, 0xc7, 0x9a, 0x48,
-	0xb6, 0x91, 0xc7, 0xf1, 0x2e, 0x2d, 0xd7, 0xb1, 0xe3, 0xaa, 0x27, 0xdb, 0x68, 0xba, 0x79, 0x74,
-	0x4d, 0xa4, 0x65, 0x06, 0xd8, 0xf4, 0x68, 0x56, 0xa0, 0x8f, 0xa1, 0x94, 0x4c, 0xbf, 0xa8, 0x9e,
-	0x6b, 0x4a, 0x7a, 0x4e, 0x46, 0x21, 0x4d, 0xe7, 0x4a, 0x0c, 0x43, 0x0a, 0x94, 0xd3, 0x56, 0x04,
-	0xf6, 0xd2, 0x74, 0x1f, 0xcd, 0xdc, 0x34, 0x0f, 0x2f, 0x90, 0xab, 0xaa, 0x70, 0x50, 0xc0, 0x69,
-	0x6a, 0x46, 0x80, 0x7e, 0x06, 0xc5, 0x63, 0x97, 0x8e, 0x2e, 0x12, 0xdd, 0xde, 0x5b, 0xdd, 0xc6,
-	0xec, 0x99, 0xea, 0x14, 0x87, 0x0c, 0x18, 0x25, 0x12, 0x5c, 0x4d, 0x5d, 0xc7, 0xbb, 0x30, 0x43,
-	0xcb, 0x9f, 0x90, 0x50, 0xde, 0xe6, 0x63, 0x3a, 0xb6, 0x0e, 0x98, 0xf1, 0x97, 0xe2, 0x9f, 0xbf,
-	0xdd, 0xdf, 0xd0, 0x3c, 0xa8, 0xa4, 0x71, 0xa2, 0x06, 0xa1, 0xe3, 0x71, 0x40, 0x42, 0x56, 0xcd,
-	0x3c, 0x8e, 0x77, 0x69, 0x8d, 0x72, 0xec, 0x79, 0xbc, 0x46, 0x08, 0xc4, 0x73, 0x2b, 0x38, 0x67,
-	0x75, 0xab, 0x61, 0xb6, 0x8e, 0x54, 0xf9, 0x8a, 0x58, 0x17, 0x26, 0x73, 0xf0, 0xaa, 0x95, 0x23,
-	0xc3, 0x53, 0x2b, 0x38, 0x8f, 0xef, 0xfb, 0x35, 0x14, 0x39, 0x4b, 0xe8, 0x09, 0x94, 0x47, 0x74,
-	0xee, 0x85, 0xab, 0x09, 0xbd, 0x9d, 0x15, 0x3e, 0xf3, 0xc4, 0x99, 0xa5, 0x40, 0xed, 0x04, 0x4a,
-	0xb1, 0x0b, 0x3d, 0x4a, 0xa7, 0x92, 0x78, 0x7c, 0x3f, 0x11, 0x67, 0xff, 0x9c, 0xfa, 0xe1, 0xda,
-	0x50, 0xda, 0x81, 0xc2, 0xa5, 0xe5, 0xce, 0xf9, 0xe3, 0x45, 0xcc, 0x37, 0xda, 0x5f, 0x05, 0x28,
-	0xe1, 0xa8, 0x08, 0x41, 0x98, 0x19, 0xf6, 0x85, 0xb5, 0x61, 0xbf, 0x92, 0x4b, 0x6e, 0x4d, 0x2e,
-	0x49, 0xc7, 0xe7, 0x33, 0x1d, 0xbf, 0x62, 0x4e, 0xbc, 0x93, 0xb9, 0xc2, 0x1d, 0xcc, 0x15, 0x33,
-	0xcc, 0x3d, 0x82, 0xad, 0xb1, 0x4f, 0xa7, 0x6c, 0x9c, 0x53, 0xdf, 0xf2, 0xaf, 0xe2, 0xee, 0xdc,
-	0x8c, 0xac, 0x83, 0xc4, 0xa8, 0x99, 0x50, 0xc6, 0x24, 0x98, 0x51, 0x2f, 0x20, 0xef, 0x7c, 0x36,
-	0x02, 0xd1, 0xb6, 0x42, 0x8b, 0x3d, 0xba, 0x86, 0xd9, 0x1a, 0xfd, 0x04, 0xc4, 0x11, 0xb5, 0xf9,
-	0x93, 0xb7, 0xb2, 0x3d, 0xa4, 0xfb, 0x3e, 0xf5, 0x9b, 0xd4, 0x26, 0x98, 0x01, 0xb4, 0x19, 0x48,
-	0x2d, 0xfa, 0xca, 0x73, 0xa9, 0x65, 0xf7, 0x7c, 0x3a, 0x89, 0xc6, 0xed, 0x3b, 0xc7, 0x46, 0x0b,
-	0x4a, 0x73, 0x36, 0x58, 0x92, 0xc1, 0xf1, 0xc9, 0xba, 0xd0, 0x6f, 0x06, 0xe2, 0x53, 0x28, 0x51,
-	0x47, 0x7c, 0x54, 0xfb, 0x5e, 0x00, 0xe5, 0xdd, 0x68, 0xd4, 0x86, 0x2a, 0x47, 0x9a, 0x99, 0x2f,
-	0x89, 0x83, 0xf7, 0xb9, 0x88, 0xcd, 0x18, 0x98, 0xa7, 0xeb, 0x3b, 0xff, 0x9e, 0x32, 0x6a, 0xce,
-	0xbf, 0x9f, 0x9a, 0x1f, 0xc2, 0x26, 0xd3, 0x59, 0xfa, 0xa7, 0x2b, 0xaa, 0xf9, 0x83, 0x02, 0xae,
-	0x0d, 0xb9, 0x8a, 0x98, 0x4d, 0x2b, 0x82, 0xd8, 0x73, 0xbc, 0x89, 0xb6, 0x0f, 0x85, 0xa6, 0x4b,
-	0x59, 0xb1, 0x8a, 0x3e, 0xb1, 0x02, 0xea, 0x25, 0x1c, 0xf2, 0xdd, 0xe1, 0xf7, 0x39, 0xa8, 0x66,
-	0x3e, 0x86, 0xd0, 0x63, 0xd8, 0x6a, 0x76, 0xce, 0xfa, 0x03, 0x1d, 0x9b, 0xcd, 0xae, 0x71, 0xd2,
-	0x3e, 0x95, 0x36, 0x94, 0xbd, 0xc5, 0x52, 0x95, 0xa7, 0x2b, 0xd0, 0xfa, 0x77, 0xce, 0x3e, 0x14,
-	0xda, 0x46, 0x4b, 0xff, 0x9d, 0x24, 0x28, 0x3b, 0x8b, 0xa5, 0x2a, 0x65, 0x80, 0xfc, 0xcf, 0xe4,
-	0xa7, 0x50, 0x63, 0x00, 0xf3, 0xac, 0xd7, 0x6a, 0x0c, 0x74, 0x29, 0xa7, 0x28, 0x8b, 0xa5, 0xba,
-	0x7b, 0x13, 0x17, 0xf3, 0xfd, 0x10, 0x4a, 0x58, 0xff, 0xed, 0x99, 0xde, 0x1f, 0x48, 0x79, 0x65,
-	0x77, 0xb1, 0x54, 0x51, 0x06, 0x98, 0x28, 0xe6, 0x11, 0x94, 0xb1, 0xde, 0xef, 0x75, 0x8d, 0xbe,
-	0x2e, 0x89, 0xca, 0x87, 0x8b, 0xa5, 0x7a, 0x6f, 0x0d, 0x15, 0x77, 0xe8, 0x2f, 0x60, 0xbb, 0xd5,
-	0x7d, 0x61, 0x74, 0xba, 0x8d, 0x96, 0xd9, 0xc3, 0xdd, 0x53, 0xac, 0xf7, 0xfb, 0x52, 0x41, 0xd9,
-	0x5f, 0x2c, 0xd5, 0x07, 0x19, 0xfc, 0xad, 0x86, 0xfb, 0x08, 0xc4, 0x5e, 0xdb, 0x38, 0x95, 0x8a,
-	0xca, 0xbd, 0xc5, 0x52, 0xfd, 0x20, 0x03, 0x8d, 0x48, 0x8d, 0x32, 0x6e, 0x76, 0xba, 0x7d, 0x5d,
-	0x2a, 0xdd, 0xca, 0x98, 0x91, 0x7d, 0xf8, 0x7b, 0x40, 0xb7, 0x3f, 0x17, 0xd1, 0x27, 0x20, 0x1a,
-	0x5d, 0x43, 0x97, 0x36, 0x78, 0xfe, 0xb7, 0x11, 0x06, 0xf5, 0x08, 0xd2, 0x20, 0xdf, 0xf9, 0xe6,
-	0x4b, 0x49, 0x50, 0x7e, 0xb4, 0x58, 0xaa, 0xf7, 0x6f, 0x83, 0x3a, 0xdf, 0x7c, 0x79, 0x48, 0xa1,
-	0x9a, 0x0d, 0xac, 0x41, 0xf9, 0x99, 0x3e, 0x68, 0xb4, 0x1a, 0x83, 0x86, 0xb4, 0xc1, 0x9f, 0x94,
-	0xb8, 0x9f, 0x91, 0xd0, 0x62, 0x02, 0xdc, 0x83, 0x82, 0xa1, 0x3f, 0xd7, 0xb1, 0x24, 0x28, 0xdb,
-	0x8b, 0xa5, 0xba, 0x99, 0x00, 0x0c, 0x72, 0x49, 0x7c, 0x54, 0x87, 0x62, 0xa3, 0xf3, 0xa2, 0xf1,
-	0xb2, 0x2f, 0xe5, 0x14, 0xb4, 0x58, 0xaa, 0x5b, 0x89, 0xbb, 0xe1, 0xbe, 0xb2, 0xae, 0x82, 0xc3,
-	0xff, 0x08, 0x50, 0xcb, 0xfe, 0x75, 0xa2, 0x3a, 0x88, 0x27, 0xed, 0x8e, 0x9e, 0x5c, 0x97, 0xf5,
-	0x45, 0x6b, 0x74, 0x00, 0x95, 0x56, 0x1b, 0xeb, 0xcd, 0x41, 0x17, 0xbf, 0x4c, 0x72, 0xc9, 0x82,
-	0x5a, 0x8e, 0xcf, 0x9a, 0x3b, 0xfa, 0x3c, 0xad, 0xf5, 0x5f, 0x3e, 0xeb, 0xb4, 0x8d, 0xaf, 0x4d,
-	0x16, 0x31, 0xa7, 0x3c, 0x58, 0x2c, 0xd5, 0x0f, 0xb3, 0xe0, 0x3e, 0xff, 0xdb, 0x60, 0x81, 0xbf,
-	0x82, 0xed, 0x04, 0xbe, 0xba, 0x20, 0xaf, 0xa8, 0x8b, 0xa5, 0xba, 0x77, 0xc7, 0x99, 0xd5, 0x3d,
-	0x4f, 0xe0, 0x83, 0xe4, 0xe0, 0x99, 0xf1, 0xb5, 0xd1, 0x7d, 0x61, 0x48, 0xa2, 0x52, 0x5f, 0x2c,
-	0x55, 0xe5, 0x8e, 0x63, 0x67, 0xde, 0x85, 0x47, 0x5f, 0x79, 0x87, 0x7f, 0x11, 0xa0, 0x92, 0x4e,
-	0xa8, 0x88, 0x67, 0xa3, 0x6b, 0xea, 0x18, 0x77, 0x71, 0x92, 0x78, 0xea, 0x34, 0x28, 0x5b, 0xa2,
-	0x8f, 0xa1, 0x74, 0xaa, 0x1b, 0x3a, 0x6e, 0x37, 0x13, 0x3d, 0xa4, 0x90, 0x53, 0xe2, 0x11, 0xdf,
-	0x19, 0xa1, 0x4f, 0xa1, 0x66, 0x74, 0xcd, 0xfe, 0x59, 0xf3, 0x69, 0x92, 0x31, 0x6b, 0xe0, 0x4c,
-	0xa8, 0xfe, 0x7c, 0x74, 0xce, 0xb2, 0x3d, 0x8c, 0xa4, 0xf3, 0xbc, 0xd1, 0x69, 0xb7, 0x38, 0x34,
-	0xaf, 0xc8, 0x8b, 0xa5, 0xba, 0x93, 0x42, 0xdb, 0xfc, 0xd3, 0x21, 0xc2, 0x1e, 0xda, 0x50, 0xff,
-	0xdf, 0xb3, 0x08, 0xa9, 0x50, 0x6c, 0xf4, 0x7a, 0xba, 0xd1, 0x4a, 0x5e, 0xbf, 0xf2, 0x35, 0x66,
-	0x33, 0xe2, 0xd9, 0x11, 0xe2, 0xa4, 0x8b, 0x4f, 0xf5, 0x41, 0xf2, 0xf8, 0x15, 0xe2, 0x84, 0x46,
-	0x7f, 0xda, 0xc7, 0x7b, 0x6f, 0x7e, 0xa8, 0x6f, 0x7c, 0xf7, 0x43, 0x7d, 0xe3, 0xcd, 0x75, 0x5d,
-	0xf8, 0xee, 0xba, 0x2e, 0xfc, 0xe3, 0xba, 0xbe, 0xf1, 0xaf, 0xeb, 0xba, 0xf0, 0xed, 0x3f, 0xeb,
-	0xc2, 0xb0, 0xc8, 0x66, 0xd7, 0x93, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xee, 0xe2, 0x0f, 0x00,
-	0x38, 0x0e, 0x00, 0x00,
+	// 1670 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0x4f, 0x73, 0xdb, 0x4c,
+	0x19, 0x8f, 0x6d, 0xf9, 0xdf, 0xda, 0xc9, 0xeb, 0x6c, 0xd3, 0xbc, 0x46, 0xcd, 0x9b, 0x04, 0xbd,
+	0x6f, 0x21, 0x78, 0x68, 0x0a, 0x2d, 0xd0, 0x19, 0x06, 0x98, 0x71, 0x6c, 0x25, 0xd1, 0xd4, 0x91,
+	0x8d, 0x6c, 0xa7, 0x94, 0x03, 0x1a, 0xd9, 0x5a, 0x3b, 0x9a, 0xc8, 0x5a, 0x23, 0xc9, 0x6d, 0xc3,
+	0x47, 0xe0, 0x13, 0x70, 0x61, 0xa6, 0x33, 0x9c, 0xb8, 0xf3, 0x21, 0xca, 0xad, 0xd3, 0x23, 0x87,
+	0x0e, 0x94, 0x0b, 0x47, 0x2e, 0xdc, 0x79, 0x76, 0x57, 0x92, 0xe5, 0xfc, 0x61, 0x7a, 0xe0, 0x90,
+	0xb1, 0xf6, 0x79, 0x7e, 0xfb, 0x3c, 0xfb, 0xfc, 0xf9, 0x3d, 0xbb, 0x41, 0xe5, 0x11, 0x99, 0x1f,
+	0xce, 0x7d, 0x1a, 0x52, 0x5c, 0xe2, 0x3f, 0x63, 0xea, 0xca, 0x8f, 0xa6, 0x4e, 0x78, 0xb1, 0x18,
+	0x1d, 0x8e, 0xe9, 0xec, 0xf1, 0x94, 0x4e, 0xe9, 0x63, 0xae, 0x19, 0x2d, 0x26, 0x7c, 0xc5, 0x17,
+	0xfc, 0x4b, 0x6c, 0x54, 0xe6, 0x28, 0x7f, 0x4a, 0x5c, 0x97, 0xe2, 0x3d, 0x54, 0xb1, 0xc9, 0x2b,
+	0x67, 0x4c, 0x4c, 0xcf, 0x9a, 0x91, 0x7a, 0x66, 0x3f, 0x73, 0x50, 0x36, 0x90, 0x10, 0xe9, 0x20,
+	0x61, 0x80, 0xb1, 0xeb, 0x10, 0x2f, 0x14, 0x80, 0xac, 0x00, 0x08, 0x11, 0x07, 0x3c, 0x44, 0x1b,
+	0x11, 0xe0, 0x15, 0xf1, 0x03, 0x87, 0x7a, 0xf5, 0x1c, 0xc7, 0xac, 0x0b, 0xe9, 0xb9, 0x10, 0x2a,
+	0x01, 0x2a, 0x9c, 0x12, 0xcb, 0x26, 0x3e, 0xfe, 0x1e, 0x92, 0xc2, 0xab, 0xb9, 0xf0, 0xb5, 0xf1,
+	0xe4, 0xfe, 0x61, 0x1c, 0xc3, 0xe1, 0x19, 0x09, 0x02, 0x6b, 0x4a, 0x06, 0xa0, 0x34, 0x38, 0x04,
+	0xff, 0x02, 0x9c, 0xd3, 0xd9, 0xdc, 0x07, 0x05, 0x33, 0x9c, 0xe5, 0x3b, 0x76, 0x6e, 0xec, 0x68,
+	0x2d, 0x31, 0x46, 0x7a, 0x83, 0xd2, 0x44, 0xeb, 0x2d, 0x77, 0x11, 0x84, 0xc4, 0x6f, 0x51, 0x6f,
+	0xe2, 0x4c, 0xf1, 0x0f, 0x50, 0x71, 0x42, 0x5d, 0x38, 0x45, 0x00, 0xee, 0x73, 0x07, 0x95, 0x27,
+	0xb5, 0xa5, 0xb1, 0x63, 0xae, 0x38, 0x92, 0xde, 0x7d, 0xdc, 0x5b, 0x33, 0x62, 0x98, 0xf2, 0xa7,
+	0x2c, 0x2a, 0x08, 0x0d, 0xde, 0x46, 0x59, 0xc7, 0x16, 0x29, 0x3a, 0x2a, 0x7c, 0xfa, 0xb8, 0x97,
+	0xd5, 0xda, 0x06, 0x48, 0xf0, 0x16, 0xca, 0xbb, 0xd6, 0x88, 0xb8, 0x51, 0x72, 0xc4, 0x02, 0x3f,
+	0x40, 0x65, 0x1f, 0x02, 0x36, 0xa9, 0xe7, 0x5e, 0xf1, 0x94, 0x94, 0x8c, 0x12, 0x13, 0x74, 0x61,
+	0x8d, 0x1f, 0x21, 0xec, 0x4c, 0x3d, 0xea, 0x13, 0x73, 0x4e, 0xfc, 0x99, 0xc3, 0x4f, 0x1b, 0xd4,
+	0x25, 0x8e, 0xda, 0x14, 0x9a, 0xde, 0x52, 0x81, 0xbf, 0x46, 0xeb, 0x11, 0xdc, 0x26, 0x2e, 0x09,
+	0x49, 0x3d, 0xcf, 0x91, 0x55, 0x21, 0x6c, 0x73, 0x19, 0xc4, 0xb6, 0x65, 0x3b, 0x81, 0x35, 0x72,
+	0x89, 0x19, 0x92, 0xd9, 0xdc, 0x74, 0x3c, 0x9b, 0xbc, 0x21, 0x41, 0xbd, 0xc0, 0xb1, 0x38, 0xd2,
+	0x0d, 0x40, 0xa5, 0x09, 0x0d, 0x04, 0x54, 0x98, 0x5b, 0x8b, 0x80, 0xd8, 0xf5, 0x22, 0xc7, 0x44,
+	0x2b, 0x96, 0x25, 0xd1, 0x01, 0x41, 0xbd, 0x76, 0x3d, 0x4b, 0x6d, 0xae, 0x88, 0xb3, 0x14, 0xc1,
+	0x94, 0x7f, 0x43, 0x96, 0x84, 0x06, 0x7f, 0x27, 0xc9, 0x52, 0xf5, 0x68, 0x9b, 0xa1, 0xfe, 0xf6,
+	0x71, 0xaf, 0x24, 0x74, 0x5a, 0x3b, 0x95, 0x35, 0x8c, 0xa4, 0x54, 0x47, 0xf1, 0x6f, 0xbc, 0x83,
+	0xca, 0x96, 0x6d, 0xb3, 0xea, 0x81, 0xeb, 0x1c, 0xb8, 0x2e, 0x1b, 0x4b, 0x01, 0x7e, 0xb6, 0xda,
+	0x0d, 0xd2, 0xf5, 0xfe, 0xb9, 0xab, 0x0d, 0x58, 0x29, 0xc6, 0xc4, 0x8f, 0x3a, 0x38, 0xcf, 0xfd,
+	0x95, 0x98, 0x80, 0xf7, 0xef, 0xb7, 0x51, 0x75, 0x66, 0xbd, 0x31, 0x03, 0xf2, 0xdb, 0x05, 0xf1,
+	0xc6, 0x84, 0xa7, 0x2b, 0x67, 0x54, 0x40, 0xd6, 0x8f, 0x44, 0x78, 0x17, 0x21, 0xc7, 0x0b, 0x7d,
+	0x6a, 0x2f, 0x60, 0x57, 0x94, 0xab, 0x94, 0x04, 0xff, 0x18, 0x95, 0x78, 0xb2, 0x4d, 0x08, 0xbc,
+	0x04, 0x5a, 0xe9, 0x48, 0x8e, 0x02, 0x2f, 0xf2, 0x54, 0xf3, 0xb8, 0xe3, 0x4f, 0xa3, 0xc8, 0xb1,
+	0x9a, 0x8d, 0x7f, 0x86, 0xe4, 0xe0, 0xd2, 0x61, 0x85, 0x12, 0x96, 0x42, 0x38, 0xab, 0xe9, 0x93,
+	0x19, 0x7d, 0x65, 0xb9, 0x41, 0xbd, 0xcc, 0xdd, 0xd4, 0x19, 0x42, 0x4b, 0x01, 0x8c, 0x48, 0xaf,
+	0x74, 0x51, 0x9e, 0x5b, 0x64, 0x55, 0x14, 0xcd, 0x1a, 0xb1, 0x37, 0x5a, 0xe1, 0x43, 0x94, 0x9f,
+	0x38, 0x2e, 0x24, 0x32, 0xcb, 0x6b, 0x88, 0x53, 0x9d, 0x0e, 0x62, 0xcd, 0x9b, 0xd0, 0xa8, 0x8a,
+	0x02, 0xa6, 0x0c, 0x51, 0x85, 0x1b, 0x1c, 0xce, 0x6d, 0x0b, 0xda, 0xe9, 0xff, 0x65, 0xf6, 0xaf,
+	0x39, 0x54, 0x8a, 0x35, 0x49, 0xd1, 0x33, 0xa9, 0xa2, 0x37, 0xa2, 0x79, 0x20, 0xd8, 0xbd, 0x7d,
+	0xd3, 0x5e, 0x6a, 0x20, 0xc0, 0xfe, 0xc0, 0xf9, 0x1d, 0xe1, 0x7c, 0xca, 0x19, 0xfc, 0x1b, 0xef,
+	0xa3, 0xca, 0x75, 0x12, 0xad, 0x1b, 0x69, 0x11, 0xfe, 0x0a, 0xa1, 0x19, 0xb5, 0x9d, 0x89, 0x43,
+	0x6c, 0x33, 0xe0, 0x0d, 0x90, 0x33, 0xca, 0xb1, 0xa4, 0x8f, 0xeb, 0xac, 0xdd, 0x19, 0x85, 0xec,
+	0x88, 0x2b, 0xf1, 0x92, 0x69, 0x1c, 0x0f, 0xb2, 0xed, 0xc4, 0x0c, 0x89, 0x97, 0x6c, 0xea, 0x79,
+	0x74, 0x85, 0xbc, 0x25, 0x0e, 0x58, 0xf7, 0x68, 0x9a, 0xb8, 0xc0, 0xa4, 0x78, 0x2a, 0xb2, 0x7a,
+	0xae, 0x30, 0xe9, 0x9c, 0x8c, 0x43, 0x9a, 0xcc, 0x9b, 0x08, 0x86, 0x65, 0x54, 0x4a, 0x5a, 0x11,
+	0xf1, 0x93, 0x26, 0x6b, 0x36, 0x8b, 0x93, 0x38, 0xc0, 0x63, 0x05, 0xd4, 0x79, 0x23, 0x09, 0x4d,
+	0x0f, 0xf0, 0x0f, 0x51, 0xe1, 0xc8, 0xa5, 0xe3, 0xcb, 0x98, 0xb7, 0xf7, 0x96, 0xde, 0xb8, 0x3c,
+	0x55, 0x9d, 0xc2, 0x88, 0x03, 0x59, 0x20, 0xc1, 0xd5, 0xcc, 0x75, 0xbc, 0x4b, 0x33, 0xb4, 0xfc,
+	0x29, 0x09, 0xeb, 0x9b, 0x62, 0x7c, 0x47, 0xd2, 0x01, 0x17, 0xfe, 0x54, 0xfa, 0xc3, 0xdb, 0xbd,
+	0x35, 0xc5, 0x43, 0xe5, 0xc4, 0x0e, 0x6b, 0x10, 0x3a, 0x99, 0x04, 0xb0, 0x23, 0xc3, 0xcf, 0x19,
+	0xad, 0x92, 0x1a, 0x65, 0xf9, 0xf1, 0x44, 0x8d, 0x40, 0x76, 0x61, 0x05, 0x17, 0xbc, 0x6e, 0x55,
+	0x83, 0x7f, 0x33, 0x56, 0xbe, 0x26, 0xd6, 0xa5, 0xc9, 0x15, 0xa2, 0x6a, 0x25, 0x26, 0x38, 0x85,
+	0x75, 0xe4, 0xef, 0xe7, 0xa8, 0x20, 0xb2, 0x84, 0x9f, 0xa2, 0xd2, 0x98, 0x2e, 0xbc, 0x70, 0x39,
+	0xb9, 0x37, 0xd3, 0xc4, 0xe7, 0x9a, 0x28, 0xb2, 0x04, 0xa8, 0x1c, 0xa3, 0x62, 0xa4, 0x82, 0x30,
+	0xe3, 0xa9, 0x24, 0x1d, 0xdd, 0x8f, 0xc9, 0xd9, 0xbf, 0xa0, 0x7e, 0xb8, 0x32, 0x94, 0x60, 0x94,
+	0x43, 0x7d, 0x17, 0xe2, 0xf0, 0x92, 0x21, 0x16, 0xca, 0x5f, 0x32, 0xa8, 0x68, 0xb0, 0x22, 0x04,
+	0x61, 0xea, 0x12, 0xc8, 0xaf, 0x5c, 0x02, 0x4b, 0xba, 0x64, 0x57, 0xe8, 0x12, 0x77, 0x7c, 0x2e,
+	0xd5, 0xf1, 0xcb, 0xcc, 0x49, 0xb7, 0x66, 0x2e, 0x7f, 0x4b, 0xe6, 0x0a, 0xa9, 0xcc, 0x41, 0xcd,
+	0x26, 0x3e, 0x9d, 0xf1, 0x31, 0x4f, 0x7d, 0xcb, 0xbf, 0x8a, 0xba, 0x73, 0x9d, 0x49, 0x07, 0xb1,
+	0x50, 0x31, 0x51, 0xc9, 0x20, 0xc1, 0x1c, 0xfa, 0x90, 0xdc, 0x79, 0x6c, 0x30, 0x0f, 0x6c, 0xb7,
+	0xf8, 0xa1, 0xc1, 0x3c, 0xfb, 0xc6, 0xdf, 0x45, 0xd2, 0x98, 0xda, 0xe2, 0xc8, 0x1b, 0xe9, 0x1e,
+	0x52, 0x7d, 0x9f, 0xc2, 0x4d, 0x6a, 0x03, 0x1b, 0x19, 0x00, 0x5e, 0x11, 0xb5, 0x36, 0x7d, 0xed,
+	0xb9, 0xd4, 0xb2, 0x7b, 0x3e, 0x9d, 0xb2, 0x71, 0x7b, 0xe7, 0xd8, 0x68, 0xa3, 0xe2, 0x82, 0x0f,
+	0x96, 0x78, 0x70, 0x7c, 0xb3, 0x4a, 0xf4, 0xeb, 0x86, 0xc4, 0x14, 0x8a, 0xd9, 0x11, 0x6d, 0x55,
+	0x3e, 0x64, 0x90, 0x7c, 0x37, 0x1a, 0x6b, 0xa8, 0x22, 0x90, 0x66, 0xea, 0x85, 0x71, 0xf0, 0x39,
+	0x8e, 0xf8, 0x8c, 0x41, 0x8b, 0xe4, 0xfb, 0xd6, 0xeb, 0x29, 0xc5, 0xe6, 0xdc, 0xe7, 0xb1, 0x19,
+	0x2e, 0x6e, 0xce, 0xb3, 0xe4, 0x32, 0x96, 0x20, 0xf6, 0xbc, 0x51, 0x1d, 0x09, 0x16, 0x71, 0x99,
+	0x52, 0x40, 0x52, 0xcf, 0xf1, 0xa6, 0xca, 0x1e, 0xca, 0xb7, 0x5c, 0xca, 0x8b, 0x55, 0x80, 0x97,
+	0x42, 0x00, 0x6e, 0xa2, 0x1c, 0x8a, 0x55, 0xe3, 0x43, 0x16, 0x55, 0x52, 0x8f, 0x24, 0x38, 0xcf,
+	0x46, 0xab, 0x33, 0xec, 0x0f, 0x54, 0xc3, 0x6c, 0x75, 0xf5, 0x63, 0xed, 0xa4, 0xb6, 0x26, 0xef,
+	0xfc, 0xfe, 0x8f, 0xfb, 0xf5, 0xd9, 0x12, 0xb4, 0xfa, 0xfe, 0x01, 0x17, 0x9a, 0xde, 0x56, 0x7f,
+	0x55, 0xcb, 0xc8, 0x5b, 0x00, 0xac, 0xa5, 0x80, 0xe2, 0x32, 0xf9, 0x3e, 0xaa, 0x72, 0x80, 0x39,
+	0xec, 0xb5, 0x9b, 0x03, 0xb5, 0x96, 0x95, 0x65, 0xc0, 0x6d, 0x5f, 0xc7, 0x45, 0xf9, 0xfe, 0x1a,
+	0x78, 0xa1, 0xfe, 0x72, 0xa8, 0xf6, 0x07, 0xb5, 0x9c, 0xbc, 0x0d, 0x40, 0x9c, 0x02, 0xc6, 0x8c,
+	0x79, 0x08, 0x6d, 0xa8, 0xf6, 0x7b, 0x5d, 0xbd, 0xaf, 0xd6, 0x24, 0xf9, 0x4b, 0x40, 0xdd, 0x5b,
+	0x41, 0x45, 0x1d, 0xfa, 0x13, 0xb4, 0xd9, 0xee, 0xbe, 0xd0, 0x3b, 0xdd, 0x66, 0xdb, 0xec, 0x19,
+	0xdd, 0x13, 0xd8, 0xd3, 0xaf, 0xe5, 0xe5, 0x3d, 0xc0, 0x3f, 0x48, 0xe1, 0x6f, 0x34, 0xdc, 0x57,
+	0x90, 0x3d, 0x4d, 0x3f, 0xa9, 0x15, 0xe4, 0x7b, 0x00, 0xfd, 0x22, 0x05, 0x65, 0x49, 0x65, 0x11,
+	0xb7, 0x3a, 0x5d, 0x70, 0x5d, 0xbc, 0x11, 0x31, 0x4f, 0x76, 0xe3, 0x37, 0x08, 0xdf, 0x7c, 0x46,
+	0xe2, 0x6f, 0x90, 0xa4, 0x77, 0x75, 0x15, 0x12, 0xca, 0xe3, 0xbf, 0x89, 0xd0, 0xa9, 0x47, 0xb0,
+	0x82, 0x72, 0x9d, 0x5f, 0xff, 0x08, 0x92, 0xf9, 0x2d, 0x00, 0xdd, 0xbf, 0x09, 0x02, 0x65, 0x83,
+	0xa2, 0x4a, 0xda, 0xb0, 0x82, 0x4a, 0x67, 0xea, 0xa0, 0x09, 0xc9, 0x6d, 0x82, 0x71, 0x7e, 0xa4,
+	0x58, 0x7d, 0x46, 0x42, 0x8b, 0x13, 0x70, 0x07, 0xe5, 0x75, 0xf5, 0x5c, 0x35, 0xc0, 0xf0, 0x26,
+	0x00, 0xd6, 0x63, 0x80, 0x4e, 0xa0, 0xaf, 0xe0, 0x35, 0x52, 0x68, 0x76, 0x5e, 0x34, 0x5f, 0xf6,
+	0xa1, 0x38, 0x18, 0xd4, 0x1b, 0xb1, 0xba, 0xe9, 0xbe, 0xb6, 0xae, 0x82, 0xc6, 0x7f, 0x32, 0xa8,
+	0x9a, 0xbe, 0x3a, 0x61, 0x83, 0x74, 0xac, 0x75, 0xd4, 0xd8, 0x5d, 0x5a, 0xc7, 0xbe, 0xf1, 0x01,
+	0x2a, 0xb7, 0x35, 0x43, 0x6d, 0x0d, 0xba, 0xc6, 0xcb, 0x38, 0x96, 0x34, 0xa8, 0xed, 0xf8, 0xbc,
+	0xb9, 0xd9, 0xb3, 0xb5, 0xda, 0x7f, 0x79, 0xd6, 0xd1, 0xf4, 0xe7, 0x26, 0xb7, 0x98, 0x95, 0x1f,
+	0x00, 0xf8, 0xcb, 0x34, 0xb8, 0x2f, 0xae, 0x0d, 0x6e, 0xf8, 0x19, 0xda, 0x8c, 0xe1, 0x4b, 0x07,
+	0x39, 0x79, 0x1f, 0xf6, 0xec, 0xdc, 0xb2, 0x67, 0xe9, 0xe7, 0x29, 0xfa, 0x22, 0xde, 0x38, 0xd4,
+	0x9f, 0xeb, 0xd0, 0x16, 0xd0, 0x39, 0xbb, 0xb0, 0x4d, 0xbe, 0x65, 0xdb, 0xd0, 0xbb, 0xf4, 0xa0,
+	0x29, 0x1a, 0x7f, 0xce, 0xa0, 0x72, 0x32, 0xa1, 0x58, 0x9e, 0xf5, 0xae, 0xa9, 0x1a, 0x46, 0xd7,
+	0x88, 0x03, 0x4f, 0x94, 0x3a, 0xe5, 0x9f, 0xf0, 0xf4, 0x2b, 0x9e, 0xa8, 0xba, 0x6a, 0x68, 0xad,
+	0x98, 0x0f, 0x09, 0xe4, 0x84, 0x78, 0xc4, 0x77, 0xc6, 0xf0, 0xcf, 0x4a, 0x15, 0xcc, 0xf4, 0x87,
+	0xad, 0xd3, 0x38, 0x62, 0xde, 0xc0, 0x29, 0x53, 0xfd, 0xc5, 0xf8, 0x82, 0x47, 0xdb, 0x60, 0xd4,
+	0x39, 0x6f, 0x76, 0xb4, 0xb6, 0x80, 0xe6, 0xe4, 0x3a, 0x40, 0xb7, 0x12, 0xa8, 0x26, 0x9e, 0x0e,
+	0x0c, 0xdb, 0xb0, 0xd1, 0xee, 0xff, 0x9e, 0x45, 0xf0, 0xaa, 0x29, 0x34, 0x7b, 0x3d, 0x55, 0x6f,
+	0xc7, 0xa7, 0x5f, 0xea, 0x9a, 0xf3, 0x39, 0xf1, 0x6c, 0x86, 0x38, 0xee, 0x1a, 0x27, 0xea, 0x20,
+	0x3e, 0xfc, 0x12, 0x71, 0x4c, 0xd9, 0xa5, 0x7d, 0xb4, 0xf3, 0xee, 0x1f, 0xbb, 0x6b, 0xef, 0xe1,
+	0xef, 0xdd, 0xa7, 0xdd, 0xcc, 0x7b, 0xf8, 0xfb, 0xfb, 0xa7, 0xdd, 0xb5, 0x7f, 0xc1, 0xef, 0xdb,
+	0x7f, 0xee, 0x66, 0x46, 0x05, 0x3e, 0xbb, 0x9e, 0xfe, 0x37, 0x00, 0x00, 0xff, 0xff, 0x93, 0x35,
+	0x80, 0x49, 0x50, 0x0e, 0x00, 0x00,
 }
 }

+ 1 - 0
lib/protocol/bep.proto

@@ -58,6 +58,7 @@ message Folder {
     bool   ignore_permissions   = 4;
     bool   ignore_permissions   = 4;
     bool   ignore_delete        = 5;
     bool   ignore_delete        = 5;
     bool   disable_temp_indexes = 6;
     bool   disable_temp_indexes = 6;
+    bool   paused               = 7;
 
 
     repeated Device devices = 16 [(gogoproto.nullable) = false];
     repeated Device devices = 16 [(gogoproto.nullable) = false];
 }
 }

+ 8 - 8
lib/protocol/deviceid_test.pb.go

@@ -427,16 +427,16 @@ var (
 )
 )
 
 
 var fileDescriptorDeviceidTest = []byte{
 var fileDescriptorDeviceidTest = []byte{
-	// 176 bytes of a gzipped FileDescriptorProto
+	// 171 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x49, 0x2d, 0xcb,
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x49, 0x2d, 0xcb,
 	0x4c, 0x4e, 0xcd, 0x4c, 0x89, 0x2f, 0x49, 0x2d, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
 	0x4c, 0x4e, 0xcd, 0x4c, 0x89, 0x2f, 0x49, 0x2d, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
 	0xe2, 0x00, 0x53, 0xc9, 0xf9, 0x39, 0x52, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9,
 	0xe2, 0x00, 0x53, 0xc9, 0xf9, 0x39, 0x52, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9,
 	0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0xfa, 0x60, 0x99, 0xa4, 0xd2, 0x34, 0x30, 0x0f, 0xcc,
 	0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0xfa, 0x60, 0x99, 0xa4, 0xd2, 0x34, 0x30, 0x0f, 0xcc,
-	0x01, 0xb3, 0x20, 0x1a, 0x95, 0x54, 0xb9, 0xf8, 0x43, 0x52, 0x8b, 0x4b, 0xfc, 0x73, 0x52, 0x5c,
-	0xc0, 0xc6, 0x7a, 0xba, 0x08, 0x09, 0x71, 0xb1, 0x80, 0x4c, 0x96, 0x60, 0x54, 0x60, 0xd4, 0xe0,
-	0x09, 0x02, 0xb3, 0x95, 0xcc, 0x21, 0xca, 0xfc, 0x52, 0xcb, 0xe1, 0xca, 0x54, 0x90, 0x95, 0x39,
-	0x09, 0x9c, 0xb8, 0x27, 0xcf, 0x70, 0xeb, 0x9e, 0x3c, 0x07, 0x4c, 0x1e, 0xa2, 0xd1, 0x49, 0xe6,
-	0xc4, 0x43, 0x39, 0x86, 0x0b, 0x0f, 0xe5, 0x18, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e,
-	0xf1, 0xc1, 0x23, 0x39, 0x86, 0x17, 0x8f, 0xe4, 0x18, 0x16, 0x3c, 0x96, 0x63, 0x4c, 0x62, 0x03,
-	0x3b, 0xc2, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x35, 0x9c, 0x00, 0x78, 0xd4, 0x00, 0x00, 0x00,
+	0x01, 0xb3, 0x20, 0x1a, 0x95, 0x54, 0xb9, 0xf8, 0x43, 0x80, 0xc6, 0xf8, 0xe7, 0xa4, 0xb8, 0x80,
+	0x8d, 0xf5, 0x74, 0x11, 0x12, 0xe2, 0x62, 0x01, 0x99, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13,
+	0x04, 0x66, 0x2b, 0x99, 0x43, 0x94, 0xf9, 0xa5, 0x96, 0xc3, 0x95, 0xa9, 0x20, 0x2b, 0x73, 0x12,
+	0x38, 0x71, 0x4f, 0x9e, 0xe1, 0xd6, 0x3d, 0x79, 0x0e, 0x98, 0x3c, 0x44, 0xa3, 0x93, 0xcc, 0x89,
+	0x87, 0x72, 0x0c, 0x17, 0x80, 0xf8, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x40, 0xfc, 0xe0, 0x91, 0x1c,
+	0xc3, 0x0b, 0x20, 0x5e, 0xf0, 0x58, 0x8e, 0x31, 0x89, 0x0d, 0xec, 0x08, 0x63, 0x40, 0x00, 0x00,
+	0x00, 0xff, 0xff, 0x35, 0x9c, 0x00, 0x78, 0xd4, 0x00, 0x00, 0x00,
 }
 }