|
@@ -12,7 +12,6 @@ import (
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"math/rand"
|
|
|
- "net/url"
|
|
|
"os"
|
|
|
"path/filepath"
|
|
|
"reflect"
|
|
@@ -21,7 +20,6 @@ import (
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
|
|
|
- "github.com/syncthing/syncthing/lib/osutil"
|
|
|
"github.com/syncthing/syncthing/lib/protocol"
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
)
|
|
@@ -94,270 +92,6 @@ func (cfg Configuration) Copy() Configuration {
|
|
|
return newCfg
|
|
|
}
|
|
|
|
|
|
-type FolderConfiguration struct {
|
|
|
- ID string `xml:"id,attr" json:"id"`
|
|
|
- RawPath string `xml:"path,attr" json:"path"`
|
|
|
- Devices []FolderDeviceConfiguration `xml:"device" json:"devices"`
|
|
|
- ReadOnly bool `xml:"ro,attr" json:"readOnly"`
|
|
|
- RescanIntervalS int `xml:"rescanIntervalS,attr" json:"rescanIntervalS"`
|
|
|
- IgnorePerms bool `xml:"ignorePerms,attr" json:"ignorePerms"`
|
|
|
- AutoNormalize bool `xml:"autoNormalize,attr" json:"autoNormalize"`
|
|
|
- MinDiskFreePct float64 `xml:"minDiskFreePct" json:"minDiskFreePct"`
|
|
|
- Versioning VersioningConfiguration `xml:"versioning" json:"versioning"`
|
|
|
- Copiers int `xml:"copiers" json:"copiers"` // This defines how many files are handled concurrently.
|
|
|
- Pullers int `xml:"pullers" json:"pullers"` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
|
|
|
- Hashers int `xml:"hashers" json:"hashers"` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
|
|
|
- Order PullOrder `xml:"order" json:"order"`
|
|
|
- IgnoreDelete bool `xml:"ignoreDelete" json:"ignoreDelete"`
|
|
|
- ScanProgressIntervalS int `xml:"scanProgressIntervalS" json:"scanProgressIntervalS"` // Set to a negative value to disable. Value of 0 will get replaced with value of 2 (default value)
|
|
|
- PullerSleepS int `xml:"pullerSleepS" json:"pullerSleepS"`
|
|
|
- PullerPauseS int `xml:"pullerPauseS" json:"pullerPauseS"`
|
|
|
- MaxConflicts int `xml:"maxConflicts" json:"maxConflicts"`
|
|
|
-
|
|
|
- Invalid string `xml:"-" json:"invalid"` // Set at runtime when there is an error, not saved
|
|
|
-}
|
|
|
-
|
|
|
-func (f FolderConfiguration) Copy() FolderConfiguration {
|
|
|
- c := f
|
|
|
- c.Devices = make([]FolderDeviceConfiguration, len(f.Devices))
|
|
|
- copy(c.Devices, f.Devices)
|
|
|
- return c
|
|
|
-}
|
|
|
-
|
|
|
-func (f FolderConfiguration) Path() string {
|
|
|
- // This is intentionally not a pointer method, because things like
|
|
|
- // cfg.Folders["default"].Path() should be valid.
|
|
|
-
|
|
|
- // Attempt tilde expansion; leave unchanged in case of error
|
|
|
- if path, err := osutil.ExpandTilde(f.RawPath); err == nil {
|
|
|
- f.RawPath = path
|
|
|
- }
|
|
|
-
|
|
|
- // Attempt absolutification; leave unchanged in case of error
|
|
|
- if !filepath.IsAbs(f.RawPath) {
|
|
|
- // Abs() looks like a fairly expensive syscall on Windows, while
|
|
|
- // IsAbs() is a whole bunch of string mangling. I think IsAbs() may be
|
|
|
- // somewhat faster in the general case, hence the outer if...
|
|
|
- if path, err := filepath.Abs(f.RawPath); err == nil {
|
|
|
- f.RawPath = path
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Attempt to enable long filename support on Windows. We may still not
|
|
|
- // have an absolute path here if the previous steps failed.
|
|
|
- if runtime.GOOS == "windows" && filepath.IsAbs(f.RawPath) && !strings.HasPrefix(f.RawPath, `\\`) {
|
|
|
- return `\\?\` + f.RawPath
|
|
|
- }
|
|
|
-
|
|
|
- return f.RawPath
|
|
|
-}
|
|
|
-
|
|
|
-func (f *FolderConfiguration) CreateMarker() error {
|
|
|
- if !f.HasMarker() {
|
|
|
- marker := filepath.Join(f.Path(), ".stfolder")
|
|
|
- fd, err := os.Create(marker)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- fd.Close()
|
|
|
- osutil.HideFile(marker)
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (f *FolderConfiguration) HasMarker() bool {
|
|
|
- _, err := os.Stat(filepath.Join(f.Path(), ".stfolder"))
|
|
|
- if err != nil {
|
|
|
- return false
|
|
|
- }
|
|
|
- return true
|
|
|
-}
|
|
|
-
|
|
|
-func (f *FolderConfiguration) DeviceIDs() []protocol.DeviceID {
|
|
|
- deviceIDs := make([]protocol.DeviceID, len(f.Devices))
|
|
|
- for i, n := range f.Devices {
|
|
|
- deviceIDs[i] = n.DeviceID
|
|
|
- }
|
|
|
- return deviceIDs
|
|
|
-}
|
|
|
-
|
|
|
-type VersioningConfiguration struct {
|
|
|
- Type string `xml:"type,attr" json:"type"`
|
|
|
- Params map[string]string `json:"params"`
|
|
|
-}
|
|
|
-
|
|
|
-type InternalVersioningConfiguration struct {
|
|
|
- Type string `xml:"type,attr,omitempty"`
|
|
|
- Params []InternalParam `xml:"param"`
|
|
|
-}
|
|
|
-
|
|
|
-type InternalParam struct {
|
|
|
- Key string `xml:"key,attr"`
|
|
|
- Val string `xml:"val,attr"`
|
|
|
-}
|
|
|
-
|
|
|
-func (c *VersioningConfiguration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|
|
- var tmp InternalVersioningConfiguration
|
|
|
- tmp.Type = c.Type
|
|
|
- for k, v := range c.Params {
|
|
|
- tmp.Params = append(tmp.Params, InternalParam{k, v})
|
|
|
- }
|
|
|
-
|
|
|
- return e.EncodeElement(tmp, start)
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
|
- var tmp InternalVersioningConfiguration
|
|
|
- err := d.DecodeElement(&tmp, &start)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- c.Type = tmp.Type
|
|
|
- c.Params = make(map[string]string, len(tmp.Params))
|
|
|
- for _, p := range tmp.Params {
|
|
|
- c.Params[p.Key] = p.Val
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-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"`
|
|
|
-}
|
|
|
-
|
|
|
-func (orig DeviceConfiguration) Copy() DeviceConfiguration {
|
|
|
- c := orig
|
|
|
- c.Addresses = make([]string, len(orig.Addresses))
|
|
|
- copy(c.Addresses, orig.Addresses)
|
|
|
- return c
|
|
|
-}
|
|
|
-
|
|
|
-type FolderDeviceConfiguration struct {
|
|
|
- DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
|
|
|
-}
|
|
|
-
|
|
|
-type OptionsConfiguration struct {
|
|
|
- ListenAddress []string `xml:"listenAddress" json:"listenAddress" default:"tcp://0.0.0.0:22000"`
|
|
|
- GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" json:"globalAnnounceServer" default:"default"`
|
|
|
- GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" json:"globalAnnounceEnabled" default:"true"`
|
|
|
- LocalAnnEnabled bool `xml:"localAnnounceEnabled" json:"localAnnounceEnabled" default:"true"`
|
|
|
- LocalAnnPort int `xml:"localAnnouncePort" json:"localAnnouncePort" default:"21027"`
|
|
|
- LocalAnnMCAddr string `xml:"localAnnounceMCAddr" json:"localAnnounceMCAddr" default:"[ff12::8384]:21027"`
|
|
|
- RelayServers []string `xml:"relayServer" json:"relayServers" default:"dynamic+https://relays.syncthing.net/endpoint"`
|
|
|
- MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"`
|
|
|
- MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"`
|
|
|
- ReconnectIntervalS int `xml:"reconnectionIntervalS" json:"reconnectionIntervalS" default:"60"`
|
|
|
- RelaysEnabled bool `xml:"relaysEnabled" json:"relaysEnabled" default:"true"`
|
|
|
- RelayReconnectIntervalM int `xml:"relayReconnectIntervalM" json:"relayReconnectIntervalM" default:"10"`
|
|
|
- RelayWithoutGlobalAnn bool `xml:"relayWithoutGlobalAnn" json:"relayWithoutGlobalAnn" default:"false"`
|
|
|
- StartBrowser bool `xml:"startBrowser" json:"startBrowser" default:"true"`
|
|
|
- UPnPEnabled bool `xml:"upnpEnabled" json:"upnpEnabled" default:"true"`
|
|
|
- UPnPLeaseM int `xml:"upnpLeaseMinutes" json:"upnpLeaseMinutes" default:"60"`
|
|
|
- UPnPRenewalM int `xml:"upnpRenewalMinutes" json:"upnpRenewalMinutes" default:"30"`
|
|
|
- UPnPTimeoutS int `xml:"upnpTimeoutSeconds" json:"upnpTimeoutSeconds" default:"10"`
|
|
|
- URAccepted int `xml:"urAccepted" json:"urAccepted"` // Accepted usage reporting version; 0 for off (undecided), -1 for off (permanently)
|
|
|
- URUniqueID string `xml:"urUniqueID" json:"urUniqueId"` // Unique ID for reporting purposes, regenerated when UR is turned on.
|
|
|
- URURL string `xml:"urURL" json:"urURL" default:"https://data.syncthing.net/newdata"`
|
|
|
- URPostInsecurely bool `xml:"urPostInsecurely" json:"urPostInsecurely" default:"false"` // For testing
|
|
|
- URInitialDelayS int `xml:"urInitialDelayS" json:"urInitialDelayS" default:"1800"`
|
|
|
- RestartOnWakeup bool `xml:"restartOnWakeup" json:"restartOnWakeup" default:"true"`
|
|
|
- AutoUpgradeIntervalH int `xml:"autoUpgradeIntervalH" json:"autoUpgradeIntervalH" default:"12"` // 0 for off
|
|
|
- KeepTemporariesH int `xml:"keepTemporariesH" json:"keepTemporariesH" default:"24"` // 0 for off
|
|
|
- CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" json:"cacheIgnoredFiles" default:"true"`
|
|
|
- ProgressUpdateIntervalS int `xml:"progressUpdateIntervalS" json:"progressUpdateIntervalS" default:"5"`
|
|
|
- SymlinksEnabled bool `xml:"symlinksEnabled" json:"symlinksEnabled" default:"true"`
|
|
|
- LimitBandwidthInLan bool `xml:"limitBandwidthInLan" json:"limitBandwidthInLan" default:"false"`
|
|
|
- DatabaseBlockCacheMiB int `xml:"databaseBlockCacheMiB" json:"databaseBlockCacheMiB" default:"0"`
|
|
|
- MinHomeDiskFreePct float64 `xml:"minHomeDiskFreePct" json:"minHomeDiskFreePct" default:"1"`
|
|
|
- ReleasesURL string `xml:"releasesURL" json:"releasesURL" default:"https://api.github.com/repos/syncthing/syncthing/releases?per_page=30"`
|
|
|
- AlwaysLocalNets []string `xml:"alwaysLocalNet" json:"alwaysLocalNets"`
|
|
|
-}
|
|
|
-
|
|
|
-func (orig OptionsConfiguration) Copy() OptionsConfiguration {
|
|
|
- c := orig
|
|
|
- c.ListenAddress = make([]string, len(orig.ListenAddress))
|
|
|
- copy(c.ListenAddress, orig.ListenAddress)
|
|
|
- c.GlobalAnnServers = make([]string, len(orig.GlobalAnnServers))
|
|
|
- copy(c.GlobalAnnServers, orig.GlobalAnnServers)
|
|
|
- return c
|
|
|
-}
|
|
|
-
|
|
|
-type GUIConfiguration struct {
|
|
|
- Enabled bool `xml:"enabled,attr" json:"enabled" default:"true"`
|
|
|
- RawAddress string `xml:"address" json:"address" default:"127.0.0.1:8384"`
|
|
|
- User string `xml:"user,omitempty" json:"user"`
|
|
|
- Password string `xml:"password,omitempty" json:"password"`
|
|
|
- RawUseTLS bool `xml:"tls,attr" json:"useTLS"`
|
|
|
- RawAPIKey string `xml:"apikey,omitempty" json:"apiKey"`
|
|
|
-}
|
|
|
-
|
|
|
-func (c GUIConfiguration) Address() string {
|
|
|
- if override := os.Getenv("STGUIADDRESS"); override != "" {
|
|
|
- // This value may be of the form "scheme://address:port" or just
|
|
|
- // "address:port". We need to chop off the scheme. We try to parse it as
|
|
|
- // an URL if it contains a slash. If that fails, return it as is and let
|
|
|
- // some other error handling handle it.
|
|
|
-
|
|
|
- if strings.Contains(override, "/") {
|
|
|
- url, err := url.Parse(override)
|
|
|
- if err != nil {
|
|
|
- return override
|
|
|
- }
|
|
|
- return url.Host
|
|
|
- }
|
|
|
-
|
|
|
- return override
|
|
|
- }
|
|
|
-
|
|
|
- return c.RawAddress
|
|
|
-}
|
|
|
-
|
|
|
-func (c GUIConfiguration) UseTLS() bool {
|
|
|
- if override := os.Getenv("STGUIADDRESS"); override != "" {
|
|
|
- return strings.HasPrefix(override, "https:")
|
|
|
- }
|
|
|
- return c.RawUseTLS
|
|
|
-}
|
|
|
-
|
|
|
-func (c GUIConfiguration) URL() string {
|
|
|
- u := url.URL{
|
|
|
- Scheme: "http",
|
|
|
- Host: c.Address(),
|
|
|
- Path: "/",
|
|
|
- }
|
|
|
-
|
|
|
- if c.UseTLS() {
|
|
|
- u.Scheme = "https"
|
|
|
- }
|
|
|
-
|
|
|
- if strings.HasPrefix(u.Host, ":") {
|
|
|
- // Empty host, i.e. ":port", use IPv4 localhost
|
|
|
- u.Host = "127.0.0.1" + u.Host
|
|
|
- } else if strings.HasPrefix(u.Host, "0.0.0.0:") {
|
|
|
- // IPv4 all zeroes host, convert to IPv4 localhost
|
|
|
- u.Host = "127.0.0.1" + u.Host[7:]
|
|
|
- } else if strings.HasPrefix(u.Host, "[::]:") {
|
|
|
- // IPv6 all zeroes host, convert to IPv6 localhost
|
|
|
- u.Host = "[::1]" + u.Host[4:]
|
|
|
- }
|
|
|
-
|
|
|
- return u.String()
|
|
|
-}
|
|
|
-
|
|
|
-func (c GUIConfiguration) APIKey() string {
|
|
|
- if override := os.Getenv("STGUIAPIKEY"); override != "" {
|
|
|
- return override
|
|
|
- }
|
|
|
- return c.RawAPIKey
|
|
|
-}
|
|
|
-
|
|
|
func New(myID protocol.DeviceID) Configuration {
|
|
|
var cfg Configuration
|
|
|
cfg.Version = CurrentVersion
|
|
@@ -811,30 +545,6 @@ loop:
|
|
|
return devices[0:count]
|
|
|
}
|
|
|
|
|
|
-type DeviceConfigurationList []DeviceConfiguration
|
|
|
-
|
|
|
-func (l DeviceConfigurationList) Less(a, b int) bool {
|
|
|
- return l[a].DeviceID.Compare(l[b].DeviceID) == -1
|
|
|
-}
|
|
|
-func (l DeviceConfigurationList) Swap(a, b int) {
|
|
|
- l[a], l[b] = l[b], l[a]
|
|
|
-}
|
|
|
-func (l DeviceConfigurationList) Len() int {
|
|
|
- return len(l)
|
|
|
-}
|
|
|
-
|
|
|
-type FolderDeviceConfigurationList []FolderDeviceConfiguration
|
|
|
-
|
|
|
-func (l FolderDeviceConfigurationList) Less(a, b int) bool {
|
|
|
- return l[a].DeviceID.Compare(l[b].DeviceID) == -1
|
|
|
-}
|
|
|
-func (l FolderDeviceConfigurationList) Swap(a, b int) {
|
|
|
- l[a], l[b] = l[b], l[a]
|
|
|
-}
|
|
|
-func (l FolderDeviceConfigurationList) Len() int {
|
|
|
- return len(l)
|
|
|
-}
|
|
|
-
|
|
|
// randomCharset contains the characters that can make up a randomString().
|
|
|
const randomCharset = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
|
|
|
|
@@ -847,57 +557,3 @@ func randomString(l int) string {
|
|
|
}
|
|
|
return string(bs)
|
|
|
}
|
|
|
-
|
|
|
-type PullOrder int
|
|
|
-
|
|
|
-const (
|
|
|
- OrderRandom PullOrder = iota // default is random
|
|
|
- OrderAlphabetic
|
|
|
- OrderSmallestFirst
|
|
|
- OrderLargestFirst
|
|
|
- OrderOldestFirst
|
|
|
- OrderNewestFirst
|
|
|
-)
|
|
|
-
|
|
|
-func (o PullOrder) String() string {
|
|
|
- switch o {
|
|
|
- case OrderRandom:
|
|
|
- return "random"
|
|
|
- case OrderAlphabetic:
|
|
|
- return "alphabetic"
|
|
|
- case OrderSmallestFirst:
|
|
|
- return "smallestFirst"
|
|
|
- case OrderLargestFirst:
|
|
|
- return "largestFirst"
|
|
|
- case OrderOldestFirst:
|
|
|
- return "oldestFirst"
|
|
|
- case OrderNewestFirst:
|
|
|
- return "newestFirst"
|
|
|
- default:
|
|
|
- return "unknown"
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-func (o PullOrder) MarshalText() ([]byte, error) {
|
|
|
- return []byte(o.String()), nil
|
|
|
-}
|
|
|
-
|
|
|
-func (o *PullOrder) UnmarshalText(bs []byte) error {
|
|
|
- switch string(bs) {
|
|
|
- case "random":
|
|
|
- *o = OrderRandom
|
|
|
- case "alphabetic":
|
|
|
- *o = OrderAlphabetic
|
|
|
- case "smallestFirst":
|
|
|
- *o = OrderSmallestFirst
|
|
|
- case "largestFirst":
|
|
|
- *o = OrderLargestFirst
|
|
|
- case "oldestFirst":
|
|
|
- *o = OrderOldestFirst
|
|
|
- case "newestFirst":
|
|
|
- *o = OrderNewestFirst
|
|
|
- default:
|
|
|
- *o = OrderRandom
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|