Sfoglia il codice sorgente

Refactor config types into separate files

Jakob Borg 10 anni fa
parent
commit
83154569b1

+ 0 - 344
lib/config/config.go

@@ -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
-}

+ 39 - 0
lib/config/deviceconfiguration.go

@@ -0,0 +1,39 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package config
+
+import "github.com/syncthing/syncthing/lib/protocol"
+
+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 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)
+}

+ 123 - 0
lib/config/folderconfiguration.go

@@ -0,0 +1,123 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package config
+
+import (
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+
+	"github.com/syncthing/syncthing/lib/osutil"
+	"github.com/syncthing/syncthing/lib/protocol"
+)
+
+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
+}
+
+type FolderDeviceConfiguration struct {
+	DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
+}
+
+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 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)
+}

+ 82 - 0
lib/config/guiconfiguration.go

@@ -0,0 +1,82 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package config
+
+import (
+	"net/url"
+	"os"
+	"strings"
+)
+
+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
+}

+ 53 - 0
lib/config/optionsconfiguration.go

@@ -0,0 +1,53 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package config
+
+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
+}

+ 61 - 0
lib/config/pulorder.go

@@ -0,0 +1,61 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package config
+
+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
+}

+ 50 - 0
lib/config/versioningconfiguration.go

@@ -0,0 +1,50 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package config
+
+import "encoding/xml"
+
+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
+}