config.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at http://mozilla.org/MPL/2.0/.
  6. // Package config implements reading and writing of the syncthing configuration file.
  7. package config
  8. import (
  9. "encoding/json"
  10. "encoding/xml"
  11. "io"
  12. "math/rand"
  13. "net/url"
  14. "os"
  15. "reflect"
  16. "sort"
  17. "strconv"
  18. "strings"
  19. "github.com/syncthing/syncthing/lib/protocol"
  20. )
  21. const (
  22. OldestHandledVersion = 10
  23. CurrentVersion = 12
  24. MaxRescanIntervalS = 365 * 24 * 60 * 60
  25. )
  26. var (
  27. // DefaultDiscoveryServers should be substituted when the configuration
  28. // contains <globalAnnounceServer>default</globalAnnounceServer>. This is
  29. // done by the "consumer" of the configuration, as we don't want these
  30. // saved to the config.
  31. DefaultDiscoveryServers = []string{
  32. "https://discovery-v4-1.syncthing.net/?id=SR7AARM-TCBUZ5O-VFAXY4D-CECGSDE-3Q6IZ4G-XG7AH75-OBIXJQV-QJ6NLQA", // 194.126.249.5, Sweden
  33. "https://discovery-v4-2.syncthing.net/?id=DVU36WY-H3LVZHW-E6LLFRE-YAFN5EL-HILWRYP-OC2M47J-Z4PE62Y-ADIBDQC", // 45.55.230.38, USA
  34. "https://discovery-v4-3.syncthing.net/?id=7WT2BVR-FX62ZOW-TNVVW25-6AHFJGD-XEXQSBW-VO3MPL2-JBTLL4T-P4572Q4", // 128.199.95.124, Singapore
  35. "https://discovery-v6-1.syncthing.net/?id=SR7AARM-TCBUZ5O-VFAXY4D-CECGSDE-3Q6IZ4G-XG7AH75-OBIXJQV-QJ6NLQA", // 2001:470:28:4d6::5, Sweden
  36. "https://discovery-v6-2.syncthing.net/?id=DVU36WY-H3LVZHW-E6LLFRE-YAFN5EL-HILWRYP-OC2M47J-Z4PE62Y-ADIBDQC", // 2604:a880:800:10::182:a001, USA
  37. "https://discovery-v6-3.syncthing.net/?id=7WT2BVR-FX62ZOW-TNVVW25-6AHFJGD-XEXQSBW-VO3MPL2-JBTLL4T-P4572Q4", // 2400:6180:0:d0::d9:d001, Singapore
  38. }
  39. // DefaultDiscoveryServersIP is used by the usage reporting.
  40. // XXX: Detect Android, and use this is we still don't have working DNS?
  41. DefaultDiscoveryServersIP = []string{
  42. "https://194.126.249.5/?id=SR7AARM-TCBUZ5O-VFAXY4D-CECGSDE-3Q6IZ4G-XG7AH75-OBIXJQV-QJ6NLQA",
  43. "https://45.55.230.38/?id=AQEHEO2-XOS7QRA-X2COH5K-PO6OPVA-EWOSEGO-KZFMD32-XJ4ZV46-CUUVKAS",
  44. "https://128.199.95.124/?id=7WT2BVR-FX62ZOW-TNVVW25-6AHFJGD-XEXQSBW-VO3MPL2-JBTLL4T-P4572Q4",
  45. "https://[2001:470:28:4d6::5]/?id=SR7AARM-TCBUZ5O-VFAXY4D-CECGSDE-3Q6IZ4G-XG7AH75-OBIXJQV-QJ6NLQA",
  46. "https://[2604:a880:800:10::182:a001]/?id=AQEHEO2-XOS7QRA-X2COH5K-PO6OPVA-EWOSEGO-KZFMD32-XJ4ZV46-CUUVKAS",
  47. "https://[2400:6180:0:d0::d9:d001]/?id=7WT2BVR-FX62ZOW-TNVVW25-6AHFJGD-XEXQSBW-VO3MPL2-JBTLL4T-P4572Q4",
  48. }
  49. )
  50. func New(myID protocol.DeviceID) Configuration {
  51. var cfg Configuration
  52. cfg.Version = CurrentVersion
  53. cfg.OriginalVersion = CurrentVersion
  54. setDefaults(&cfg)
  55. setDefaults(&cfg.Options)
  56. setDefaults(&cfg.GUI)
  57. cfg.prepare(myID)
  58. return cfg
  59. }
  60. func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
  61. var cfg Configuration
  62. setDefaults(&cfg)
  63. setDefaults(&cfg.Options)
  64. setDefaults(&cfg.GUI)
  65. err := xml.NewDecoder(r).Decode(&cfg)
  66. cfg.OriginalVersion = cfg.Version
  67. cfg.prepare(myID)
  68. return cfg, err
  69. }
  70. func ReadJSON(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
  71. var cfg Configuration
  72. setDefaults(&cfg)
  73. setDefaults(&cfg.Options)
  74. setDefaults(&cfg.GUI)
  75. err := json.NewDecoder(r).Decode(&cfg)
  76. cfg.OriginalVersion = cfg.Version
  77. cfg.prepare(myID)
  78. return cfg, err
  79. }
  80. type Configuration struct {
  81. Version int `xml:"version,attr" json:"version"`
  82. Folders []FolderConfiguration `xml:"folder" json:"folders"`
  83. Devices []DeviceConfiguration `xml:"device" json:"devices"`
  84. GUI GUIConfiguration `xml:"gui" json:"gui"`
  85. Options OptionsConfiguration `xml:"options" json:"options"`
  86. IgnoredDevices []protocol.DeviceID `xml:"ignoredDevice" json:"ignoredDevices"`
  87. XMLName xml.Name `xml:"configuration" json:"-"`
  88. OriginalVersion int `xml:"-" json:"-"` // The version we read from disk, before any conversion
  89. }
  90. func (cfg Configuration) Copy() Configuration {
  91. newCfg := cfg
  92. // Deep copy FolderConfigurations
  93. newCfg.Folders = make([]FolderConfiguration, len(cfg.Folders))
  94. for i := range newCfg.Folders {
  95. newCfg.Folders[i] = cfg.Folders[i].Copy()
  96. }
  97. // Deep copy DeviceConfigurations
  98. newCfg.Devices = make([]DeviceConfiguration, len(cfg.Devices))
  99. for i := range newCfg.Devices {
  100. newCfg.Devices[i] = cfg.Devices[i].Copy()
  101. }
  102. newCfg.Options = cfg.Options.Copy()
  103. // DeviceIDs are values
  104. newCfg.IgnoredDevices = make([]protocol.DeviceID, len(cfg.IgnoredDevices))
  105. copy(newCfg.IgnoredDevices, cfg.IgnoredDevices)
  106. return newCfg
  107. }
  108. func (cfg *Configuration) WriteXML(w io.Writer) error {
  109. e := xml.NewEncoder(w)
  110. e.Indent("", " ")
  111. err := e.Encode(cfg)
  112. if err != nil {
  113. return err
  114. }
  115. _, err = w.Write([]byte("\n"))
  116. return err
  117. }
  118. func (cfg *Configuration) prepare(myID protocol.DeviceID) {
  119. fillNilSlices(&cfg.Options)
  120. // Initialize any empty slices
  121. if cfg.Folders == nil {
  122. cfg.Folders = []FolderConfiguration{}
  123. }
  124. if cfg.IgnoredDevices == nil {
  125. cfg.IgnoredDevices = []protocol.DeviceID{}
  126. }
  127. if cfg.Options.AlwaysLocalNets == nil {
  128. cfg.Options.AlwaysLocalNets = []string{}
  129. }
  130. // Check for missing, bad or duplicate folder ID:s
  131. var seenFolders = map[string]*FolderConfiguration{}
  132. for i := range cfg.Folders {
  133. folder := &cfg.Folders[i]
  134. folder.prepare()
  135. if seen, ok := seenFolders[folder.ID]; ok {
  136. l.Warnf("Multiple folders with ID %q; disabling", folder.ID)
  137. seen.Invalid = "duplicate folder ID"
  138. folder.Invalid = "duplicate folder ID"
  139. } else {
  140. seenFolders[folder.ID] = folder
  141. }
  142. }
  143. cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
  144. cfg.Options.GlobalAnnServers = uniqueStrings(cfg.Options.GlobalAnnServers)
  145. if cfg.Version > 0 && cfg.Version < OldestHandledVersion {
  146. l.Warnf("Configuration version %d is deprecated. Attempting best effort conversion, but please verify manually.", cfg.Version)
  147. }
  148. // Upgrade configuration versions as appropriate
  149. if cfg.Version <= 10 {
  150. convertV10V11(cfg)
  151. }
  152. if cfg.Version == 11 {
  153. convertV11V12(cfg)
  154. }
  155. // Build a list of available devices
  156. existingDevices := make(map[protocol.DeviceID]bool)
  157. for _, device := range cfg.Devices {
  158. existingDevices[device.DeviceID] = true
  159. }
  160. // Ensure this device is present in the config
  161. if !existingDevices[myID] {
  162. myName, _ := os.Hostname()
  163. cfg.Devices = append(cfg.Devices, DeviceConfiguration{
  164. DeviceID: myID,
  165. Name: myName,
  166. })
  167. existingDevices[myID] = true
  168. }
  169. sort.Sort(DeviceConfigurationList(cfg.Devices))
  170. // Ensure that any loose devices are not present in the wrong places
  171. // Ensure that there are no duplicate devices
  172. // Ensure that puller settings are sane
  173. for i := range cfg.Folders {
  174. cfg.Folders[i].Devices = ensureDevicePresent(cfg.Folders[i].Devices, myID)
  175. cfg.Folders[i].Devices = ensureExistingDevices(cfg.Folders[i].Devices, existingDevices)
  176. cfg.Folders[i].Devices = ensureNoDuplicates(cfg.Folders[i].Devices)
  177. sort.Sort(FolderDeviceConfigurationList(cfg.Folders[i].Devices))
  178. }
  179. // An empty address list is equivalent to a single "dynamic" entry
  180. for i := range cfg.Devices {
  181. n := &cfg.Devices[i]
  182. if len(n.Addresses) == 0 || len(n.Addresses) == 1 && n.Addresses[0] == "" {
  183. n.Addresses = []string{"dynamic"}
  184. }
  185. }
  186. // Very short reconnection intervals are annoying
  187. if cfg.Options.ReconnectIntervalS < 5 {
  188. cfg.Options.ReconnectIntervalS = 5
  189. }
  190. if cfg.GUI.RawAPIKey == "" {
  191. cfg.GUI.RawAPIKey = randomString(32)
  192. }
  193. }
  194. func convertV11V12(cfg *Configuration) {
  195. // Change listen address schema
  196. for i, addr := range cfg.Options.ListenAddress {
  197. if len(addr) > 0 && !strings.HasPrefix(addr, "tcp://") {
  198. cfg.Options.ListenAddress[i] = tcpAddr(addr)
  199. }
  200. }
  201. for i, device := range cfg.Devices {
  202. for j, addr := range device.Addresses {
  203. if addr != "dynamic" && addr != "" {
  204. cfg.Devices[i].Addresses[j] = tcpAddr(addr)
  205. }
  206. }
  207. }
  208. // Use new discovery server
  209. var newDiscoServers []string
  210. var useDefault bool
  211. for _, addr := range cfg.Options.GlobalAnnServers {
  212. if addr == "udp4://announce.syncthing.net:22026" {
  213. useDefault = true
  214. } else if addr == "udp6://announce-v6.syncthing.net:22026" {
  215. useDefault = true
  216. } else {
  217. newDiscoServers = append(newDiscoServers, addr)
  218. }
  219. }
  220. if useDefault {
  221. newDiscoServers = append(newDiscoServers, "default")
  222. }
  223. cfg.Options.GlobalAnnServers = newDiscoServers
  224. // Use new multicast group
  225. if cfg.Options.LocalAnnMCAddr == "[ff32::5222]:21026" {
  226. cfg.Options.LocalAnnMCAddr = "[ff12::8384]:21027"
  227. }
  228. // Use new local discovery port
  229. if cfg.Options.LocalAnnPort == 21025 {
  230. cfg.Options.LocalAnnPort = 21027
  231. }
  232. // Set MaxConflicts to unlimited
  233. for i := range cfg.Folders {
  234. cfg.Folders[i].MaxConflicts = -1
  235. }
  236. cfg.Version = 12
  237. }
  238. func convertV10V11(cfg *Configuration) {
  239. // Set minimum disk free of existing folders to 1%
  240. for i := range cfg.Folders {
  241. cfg.Folders[i].MinDiskFreePct = 1
  242. }
  243. cfg.Version = 11
  244. }
  245. func setDefaults(data interface{}) error {
  246. s := reflect.ValueOf(data).Elem()
  247. t := s.Type()
  248. for i := 0; i < s.NumField(); i++ {
  249. f := s.Field(i)
  250. tag := t.Field(i).Tag
  251. v := tag.Get("default")
  252. if len(v) > 0 {
  253. switch f.Interface().(type) {
  254. case string:
  255. f.SetString(v)
  256. case int:
  257. i, err := strconv.ParseInt(v, 10, 64)
  258. if err != nil {
  259. return err
  260. }
  261. f.SetInt(i)
  262. case float64:
  263. i, err := strconv.ParseFloat(v, 64)
  264. if err != nil {
  265. return err
  266. }
  267. f.SetFloat(i)
  268. case bool:
  269. f.SetBool(v == "true")
  270. case []string:
  271. // We don't do anything with string slices here. Any default
  272. // we set will be appended to by the XML decoder, so we fill
  273. // those after decoding.
  274. default:
  275. panic(f.Type())
  276. }
  277. }
  278. }
  279. return nil
  280. }
  281. // fillNilSlices sets default value on slices that are still nil.
  282. func fillNilSlices(data interface{}) error {
  283. s := reflect.ValueOf(data).Elem()
  284. t := s.Type()
  285. for i := 0; i < s.NumField(); i++ {
  286. f := s.Field(i)
  287. tag := t.Field(i).Tag
  288. v := tag.Get("default")
  289. if len(v) > 0 {
  290. switch f.Interface().(type) {
  291. case []string:
  292. if f.IsNil() {
  293. // Treat the default as a comma separated slice
  294. vs := strings.Split(v, ",")
  295. for i := range vs {
  296. vs[i] = strings.TrimSpace(vs[i])
  297. }
  298. rv := reflect.MakeSlice(reflect.TypeOf([]string{}), len(vs), len(vs))
  299. for i, v := range vs {
  300. rv.Index(i).SetString(v)
  301. }
  302. f.Set(rv)
  303. }
  304. }
  305. }
  306. }
  307. return nil
  308. }
  309. func uniqueStrings(ss []string) []string {
  310. var m = make(map[string]bool, len(ss))
  311. for _, s := range ss {
  312. m[strings.Trim(s, " ")] = true
  313. }
  314. var us = make([]string, 0, len(m))
  315. for k := range m {
  316. us = append(us, k)
  317. }
  318. sort.Strings(us)
  319. return us
  320. }
  321. func ensureDevicePresent(devices []FolderDeviceConfiguration, myID protocol.DeviceID) []FolderDeviceConfiguration {
  322. for _, device := range devices {
  323. if device.DeviceID.Equals(myID) {
  324. return devices
  325. }
  326. }
  327. devices = append(devices, FolderDeviceConfiguration{
  328. DeviceID: myID,
  329. })
  330. return devices
  331. }
  332. func ensureExistingDevices(devices []FolderDeviceConfiguration, existingDevices map[protocol.DeviceID]bool) []FolderDeviceConfiguration {
  333. count := len(devices)
  334. i := 0
  335. loop:
  336. for i < count {
  337. if _, ok := existingDevices[devices[i].DeviceID]; !ok {
  338. devices[i] = devices[count-1]
  339. count--
  340. continue loop
  341. }
  342. i++
  343. }
  344. return devices[0:count]
  345. }
  346. func ensureNoDuplicates(devices []FolderDeviceConfiguration) []FolderDeviceConfiguration {
  347. count := len(devices)
  348. i := 0
  349. seenDevices := make(map[protocol.DeviceID]bool)
  350. loop:
  351. for i < count {
  352. id := devices[i].DeviceID
  353. if _, ok := seenDevices[id]; ok {
  354. devices[i] = devices[count-1]
  355. count--
  356. continue loop
  357. }
  358. seenDevices[id] = true
  359. i++
  360. }
  361. return devices[0:count]
  362. }
  363. // randomCharset contains the characters that can make up a randomString().
  364. const randomCharset = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
  365. // randomString returns a string of random characters (taken from
  366. // randomCharset) of the specified length.
  367. func randomString(l int) string {
  368. bs := make([]byte, l)
  369. for i := range bs {
  370. bs[i] = randomCharset[rand.Intn(len(randomCharset))]
  371. }
  372. return string(bs)
  373. }
  374. func tcpAddr(host string) string {
  375. u := url.URL{
  376. Scheme: "tcp",
  377. Host: host,
  378. }
  379. return u.String()
  380. }