config.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. // All rights reserved. Use of this source code is governed by an MIT-style
  3. // license that can be found in the LICENSE file.
  4. // Package config implements reading and writing of the syncthing configuration file.
  5. package config
  6. import (
  7. "encoding/xml"
  8. "fmt"
  9. "os"
  10. "reflect"
  11. "sort"
  12. "strconv"
  13. "code.google.com/p/go.crypto/bcrypt"
  14. "github.com/syncthing/syncthing/logger"
  15. "github.com/syncthing/syncthing/osutil"
  16. "github.com/syncthing/syncthing/protocol"
  17. )
  18. var l = logger.DefaultLogger
  19. type Configuration struct {
  20. Location string `xml:"-" json:"-"`
  21. Version int `xml:"version,attr" default:"3"`
  22. Repositories []RepositoryConfiguration `xml:"repository"`
  23. Nodes []NodeConfiguration `xml:"node"`
  24. GUI GUIConfiguration `xml:"gui"`
  25. Options OptionsConfiguration `xml:"options"`
  26. XMLName xml.Name `xml:"configuration" json:"-"`
  27. }
  28. type RepositoryConfiguration struct {
  29. ID string `xml:"id,attr"`
  30. Directory string `xml:"directory,attr"`
  31. Nodes []RepositoryNodeConfiguration `xml:"node"`
  32. ReadOnly bool `xml:"ro,attr"`
  33. RescanIntervalS int `xml:"rescanIntervalS,attr" default:"60"`
  34. IgnorePerms bool `xml:"ignorePerms,attr"`
  35. Invalid string `xml:"-"` // Set at runtime when there is an error, not saved
  36. Versioning VersioningConfiguration `xml:"versioning"`
  37. nodeIDs []protocol.NodeID
  38. }
  39. type VersioningConfiguration struct {
  40. Type string `xml:"type,attr"`
  41. Params map[string]string
  42. }
  43. type InternalVersioningConfiguration struct {
  44. Type string `xml:"type,attr,omitempty"`
  45. Params []InternalParam `xml:"param"`
  46. }
  47. type InternalParam struct {
  48. Key string `xml:"key,attr"`
  49. Val string `xml:"val,attr"`
  50. }
  51. func (c *VersioningConfiguration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  52. var tmp InternalVersioningConfiguration
  53. tmp.Type = c.Type
  54. for k, v := range c.Params {
  55. tmp.Params = append(tmp.Params, InternalParam{k, v})
  56. }
  57. return e.EncodeElement(tmp, start)
  58. }
  59. func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  60. var tmp InternalVersioningConfiguration
  61. err := d.DecodeElement(&tmp, &start)
  62. if err != nil {
  63. return err
  64. }
  65. c.Type = tmp.Type
  66. c.Params = make(map[string]string, len(tmp.Params))
  67. for _, p := range tmp.Params {
  68. c.Params[p.Key] = p.Val
  69. }
  70. return nil
  71. }
  72. func (r *RepositoryConfiguration) NodeIDs() []protocol.NodeID {
  73. if r.nodeIDs == nil {
  74. for _, n := range r.Nodes {
  75. r.nodeIDs = append(r.nodeIDs, n.NodeID)
  76. }
  77. }
  78. return r.nodeIDs
  79. }
  80. type NodeConfiguration struct {
  81. NodeID protocol.NodeID `xml:"id,attr"`
  82. Name string `xml:"name,attr,omitempty"`
  83. Addresses []string `xml:"address,omitempty"`
  84. Compression bool `xml:"compression,attr"`
  85. CertName string `xml:"certName,attr,omitempty"`
  86. }
  87. type RepositoryNodeConfiguration struct {
  88. NodeID protocol.NodeID `xml:"id,attr"`
  89. Deprecated_Name string `xml:"name,attr,omitempty" json:"-"`
  90. Deprecated_Addresses []string `xml:"address,omitempty" json:"-"`
  91. }
  92. type OptionsConfiguration struct {
  93. ListenAddress []string `xml:"listenAddress" default:"0.0.0.0:22000"`
  94. GlobalAnnServer string `xml:"globalAnnounceServer" default:"announce.syncthing.net:22026"`
  95. GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" default:"true"`
  96. LocalAnnEnabled bool `xml:"localAnnounceEnabled" default:"true"`
  97. LocalAnnPort int `xml:"localAnnouncePort" default:"21025"`
  98. LocalAnnMCAddr string `xml:"localAnnounceMCAddr" default:"[ff32::5222]:21026"`
  99. ParallelRequests int `xml:"parallelRequests" default:"16"`
  100. MaxSendKbps int `xml:"maxSendKbps"`
  101. ReconnectIntervalS int `xml:"reconnectionIntervalS" default:"60"`
  102. StartBrowser bool `xml:"startBrowser" default:"true"`
  103. UPnPEnabled bool `xml:"upnpEnabled" default:"true"`
  104. UPnPLease int `xml:"upnpLeaseMinutes" default:"0"`
  105. UPnPRenewal int `xml:"upnpRenewalMinutes" default:"30"`
  106. URAccepted int `xml:"urAccepted"` // Accepted usage reporting version; 0 for off (undecided), -1 for off (permanently)
  107. Deprecated_RescanIntervalS int `xml:"rescanIntervalS,omitempty" json:"-"`
  108. Deprecated_UREnabled bool `xml:"urEnabled,omitempty" json:"-"`
  109. Deprecated_URDeclined bool `xml:"urDeclined,omitempty" json:"-"`
  110. Deprecated_ReadOnly bool `xml:"readOnly,omitempty" json:"-"`
  111. Deprecated_GUIEnabled bool `xml:"guiEnabled,omitempty" json:"-"`
  112. Deprecated_GUIAddress string `xml:"guiAddress,omitempty" json:"-"`
  113. }
  114. type GUIConfiguration struct {
  115. Enabled bool `xml:"enabled,attr" default:"true"`
  116. Address string `xml:"address" default:"127.0.0.1:8080"`
  117. User string `xml:"user,omitempty"`
  118. Password string `xml:"password,omitempty"`
  119. UseTLS bool `xml:"tls,attr"`
  120. APIKey string `xml:"apikey,omitempty"`
  121. }
  122. func (cfg *Configuration) NodeMap() map[protocol.NodeID]NodeConfiguration {
  123. m := make(map[protocol.NodeID]NodeConfiguration, len(cfg.Nodes))
  124. for _, n := range cfg.Nodes {
  125. m[n.NodeID] = n
  126. }
  127. return m
  128. }
  129. func (cfg *Configuration) GetNodeConfiguration(nodeid protocol.NodeID) *NodeConfiguration {
  130. for i, node := range cfg.Nodes {
  131. if node.NodeID == nodeid {
  132. return &cfg.Nodes[i]
  133. }
  134. }
  135. return nil
  136. }
  137. func (cfg *Configuration) RepoMap() map[string]RepositoryConfiguration {
  138. m := make(map[string]RepositoryConfiguration, len(cfg.Repositories))
  139. for _, r := range cfg.Repositories {
  140. m[r.ID] = r
  141. }
  142. return m
  143. }
  144. func setDefaults(data interface{}) error {
  145. s := reflect.ValueOf(data).Elem()
  146. t := s.Type()
  147. for i := 0; i < s.NumField(); i++ {
  148. f := s.Field(i)
  149. tag := t.Field(i).Tag
  150. v := tag.Get("default")
  151. if len(v) > 0 {
  152. switch f.Interface().(type) {
  153. case string:
  154. f.SetString(v)
  155. case int:
  156. i, err := strconv.ParseInt(v, 10, 64)
  157. if err != nil {
  158. return err
  159. }
  160. f.SetInt(i)
  161. case bool:
  162. f.SetBool(v == "true")
  163. case []string:
  164. // We don't do anything with string slices here. Any default
  165. // we set will be appended to by the XML decoder, so we fill
  166. // those after decoding.
  167. default:
  168. panic(f.Type())
  169. }
  170. }
  171. }
  172. return nil
  173. }
  174. // fillNilSlices sets default value on slices that are still nil.
  175. func fillNilSlices(data interface{}) error {
  176. s := reflect.ValueOf(data).Elem()
  177. t := s.Type()
  178. for i := 0; i < s.NumField(); i++ {
  179. f := s.Field(i)
  180. tag := t.Field(i).Tag
  181. v := tag.Get("default")
  182. if len(v) > 0 {
  183. switch f.Interface().(type) {
  184. case []string:
  185. if f.IsNil() {
  186. rv := reflect.MakeSlice(reflect.TypeOf([]string{}), 1, 1)
  187. rv.Index(0).SetString(v)
  188. f.Set(rv)
  189. }
  190. }
  191. }
  192. }
  193. return nil
  194. }
  195. func (cfg *Configuration) Save() error {
  196. fd, err := os.Create(cfg.Location + ".tmp")
  197. if err != nil {
  198. l.Warnln("Saving config:", err)
  199. return err
  200. }
  201. e := xml.NewEncoder(fd)
  202. e.Indent("", " ")
  203. err = e.Encode(cfg)
  204. if err != nil {
  205. fd.Close()
  206. return err
  207. }
  208. _, err = fd.Write([]byte("\n"))
  209. if err != nil {
  210. l.Warnln("Saving config:", err)
  211. fd.Close()
  212. return err
  213. }
  214. err = fd.Close()
  215. if err != nil {
  216. l.Warnln("Saving config:", err)
  217. return err
  218. }
  219. err = osutil.Rename(cfg.Location+".tmp", cfg.Location)
  220. if err != nil {
  221. l.Warnln("Saving config:", err)
  222. }
  223. return err
  224. }
  225. func uniqueStrings(ss []string) []string {
  226. var m = make(map[string]bool, len(ss))
  227. for _, s := range ss {
  228. m[s] = true
  229. }
  230. var us = make([]string, 0, len(m))
  231. for k := range m {
  232. us = append(us, k)
  233. }
  234. return us
  235. }
  236. func (cfg *Configuration) prepare(myID protocol.NodeID) {
  237. fillNilSlices(&cfg.Options)
  238. cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
  239. // Initialize an empty slice for repositories if the config has none
  240. if cfg.Repositories == nil {
  241. cfg.Repositories = []RepositoryConfiguration{}
  242. }
  243. // Check for missing, bad or duplicate repository ID:s
  244. var seenRepos = map[string]*RepositoryConfiguration{}
  245. var uniqueCounter int
  246. for i := range cfg.Repositories {
  247. repo := &cfg.Repositories[i]
  248. if len(repo.Directory) == 0 {
  249. repo.Invalid = "no directory configured"
  250. continue
  251. }
  252. if repo.ID == "" {
  253. repo.ID = "default"
  254. }
  255. if seen, ok := seenRepos[repo.ID]; ok {
  256. l.Warnf("Multiple repositories with ID %q; disabling", repo.ID)
  257. seen.Invalid = "duplicate repository ID"
  258. if seen.ID == repo.ID {
  259. uniqueCounter++
  260. seen.ID = fmt.Sprintf("%s~%d", repo.ID, uniqueCounter)
  261. }
  262. repo.Invalid = "duplicate repository ID"
  263. uniqueCounter++
  264. repo.ID = fmt.Sprintf("%s~%d", repo.ID, uniqueCounter)
  265. } else {
  266. seenRepos[repo.ID] = repo
  267. }
  268. }
  269. if cfg.Options.Deprecated_URDeclined {
  270. cfg.Options.URAccepted = -1
  271. }
  272. cfg.Options.Deprecated_URDeclined = false
  273. cfg.Options.Deprecated_UREnabled = false
  274. // Upgrade to v2 configuration if appropriate
  275. if cfg.Version == 1 {
  276. convertV1V2(cfg)
  277. }
  278. // Upgrade to v3 configuration if appropriate
  279. if cfg.Version == 2 {
  280. convertV2V3(cfg)
  281. }
  282. // Upgrade to v4 configuration if appropriate
  283. if cfg.Version == 3 {
  284. convertV3V4(cfg)
  285. }
  286. // Hash old cleartext passwords
  287. if len(cfg.GUI.Password) > 0 && cfg.GUI.Password[0] != '$' {
  288. hash, err := bcrypt.GenerateFromPassword([]byte(cfg.GUI.Password), 0)
  289. if err != nil {
  290. l.Warnln("bcrypting password:", err)
  291. } else {
  292. cfg.GUI.Password = string(hash)
  293. }
  294. }
  295. // Build a list of available nodes
  296. existingNodes := make(map[protocol.NodeID]bool)
  297. existingNodes[myID] = true
  298. for _, node := range cfg.Nodes {
  299. existingNodes[node.NodeID] = true
  300. }
  301. // Ensure this node is present in all relevant places
  302. me := cfg.GetNodeConfiguration(myID)
  303. if me == nil {
  304. myName, _ := os.Hostname()
  305. cfg.Nodes = append(cfg.Nodes, NodeConfiguration{
  306. NodeID: myID,
  307. Name: myName,
  308. })
  309. }
  310. sort.Sort(NodeConfigurationList(cfg.Nodes))
  311. // Ensure that any loose nodes are not present in the wrong places
  312. // Ensure that there are no duplicate nodes
  313. for i := range cfg.Repositories {
  314. cfg.Repositories[i].Nodes = ensureNodePresent(cfg.Repositories[i].Nodes, myID)
  315. cfg.Repositories[i].Nodes = ensureExistingNodes(cfg.Repositories[i].Nodes, existingNodes)
  316. cfg.Repositories[i].Nodes = ensureNoDuplicates(cfg.Repositories[i].Nodes)
  317. sort.Sort(RepositoryNodeConfigurationList(cfg.Repositories[i].Nodes))
  318. }
  319. // An empty address list is equivalent to a single "dynamic" entry
  320. for i := range cfg.Nodes {
  321. n := &cfg.Nodes[i]
  322. if len(n.Addresses) == 0 || len(n.Addresses) == 1 && n.Addresses[0] == "" {
  323. n.Addresses = []string{"dynamic"}
  324. }
  325. }
  326. }
  327. func New(location string, myID protocol.NodeID) Configuration {
  328. var cfg Configuration
  329. cfg.Location = location
  330. setDefaults(&cfg)
  331. setDefaults(&cfg.Options)
  332. setDefaults(&cfg.GUI)
  333. cfg.prepare(myID)
  334. return cfg
  335. }
  336. func Load(location string, myID protocol.NodeID) (Configuration, error) {
  337. var cfg Configuration
  338. cfg.Location = location
  339. setDefaults(&cfg)
  340. setDefaults(&cfg.Options)
  341. setDefaults(&cfg.GUI)
  342. fd, err := os.Open(location)
  343. if err != nil {
  344. return Configuration{}, err
  345. }
  346. err = xml.NewDecoder(fd).Decode(&cfg)
  347. fd.Close()
  348. cfg.prepare(myID)
  349. return cfg, err
  350. }
  351. func convertV3V4(cfg *Configuration) {
  352. // In previous versions, rescan interval was common for each repository.
  353. // From now, it can be set independently. We have to make sure, that after upgrade
  354. // the individual rescan interval will be defined for every existing repository.
  355. for i := range cfg.Repositories {
  356. cfg.Repositories[i].RescanIntervalS = cfg.Options.Deprecated_RescanIntervalS
  357. }
  358. cfg.Options.Deprecated_RescanIntervalS = 0
  359. // In previous versions, repositories held full node configurations.
  360. // Since that's the only place where node configs were in V1, we still have
  361. // to define the deprecated fields to be able to upgrade from V1 to V4.
  362. for i, repo := range cfg.Repositories {
  363. for j := range repo.Nodes {
  364. rncfg := cfg.Repositories[i].Nodes[j]
  365. rncfg.Deprecated_Name = ""
  366. rncfg.Deprecated_Addresses = nil
  367. }
  368. }
  369. cfg.Version = 4
  370. }
  371. func convertV2V3(cfg *Configuration) {
  372. // In previous versions, compression was always on. When upgrading, enable
  373. // compression on all existing new. New nodes will get compression on by
  374. // default by the GUI.
  375. for i := range cfg.Nodes {
  376. cfg.Nodes[i].Compression = true
  377. }
  378. // The global discovery format and port number changed in v0.9. Having the
  379. // default announce server but old port number is guaranteed to be legacy.
  380. if cfg.Options.GlobalAnnServer == "announce.syncthing.net:22025" {
  381. cfg.Options.GlobalAnnServer = "announce.syncthing.net:22026"
  382. }
  383. cfg.Version = 3
  384. }
  385. func convertV1V2(cfg *Configuration) {
  386. // Collect the list of nodes.
  387. // Replace node configs inside repositories with only a reference to the nide ID.
  388. // Set all repositories to read only if the global read only flag is set.
  389. var nodes = map[string]RepositoryNodeConfiguration{}
  390. for i, repo := range cfg.Repositories {
  391. cfg.Repositories[i].ReadOnly = cfg.Options.Deprecated_ReadOnly
  392. for j, node := range repo.Nodes {
  393. id := node.NodeID.String()
  394. if _, ok := nodes[id]; !ok {
  395. nodes[id] = node
  396. }
  397. cfg.Repositories[i].Nodes[j] = RepositoryNodeConfiguration{NodeID: node.NodeID}
  398. }
  399. }
  400. cfg.Options.Deprecated_ReadOnly = false
  401. // Set and sort the list of nodes.
  402. for _, node := range nodes {
  403. cfg.Nodes = append(cfg.Nodes, NodeConfiguration{
  404. NodeID: node.NodeID,
  405. Name: node.Deprecated_Name,
  406. Addresses: node.Deprecated_Addresses,
  407. })
  408. }
  409. sort.Sort(NodeConfigurationList(cfg.Nodes))
  410. // GUI
  411. cfg.GUI.Address = cfg.Options.Deprecated_GUIAddress
  412. cfg.GUI.Enabled = cfg.Options.Deprecated_GUIEnabled
  413. cfg.Options.Deprecated_GUIEnabled = false
  414. cfg.Options.Deprecated_GUIAddress = ""
  415. cfg.Version = 2
  416. }
  417. type NodeConfigurationList []NodeConfiguration
  418. func (l NodeConfigurationList) Less(a, b int) bool {
  419. return l[a].NodeID.Compare(l[b].NodeID) == -1
  420. }
  421. func (l NodeConfigurationList) Swap(a, b int) {
  422. l[a], l[b] = l[b], l[a]
  423. }
  424. func (l NodeConfigurationList) Len() int {
  425. return len(l)
  426. }
  427. type RepositoryNodeConfigurationList []RepositoryNodeConfiguration
  428. func (l RepositoryNodeConfigurationList) Less(a, b int) bool {
  429. return l[a].NodeID.Compare(l[b].NodeID) == -1
  430. }
  431. func (l RepositoryNodeConfigurationList) Swap(a, b int) {
  432. l[a], l[b] = l[b], l[a]
  433. }
  434. func (l RepositoryNodeConfigurationList) Len() int {
  435. return len(l)
  436. }
  437. func ensureNodePresent(nodes []RepositoryNodeConfiguration, myID protocol.NodeID) []RepositoryNodeConfiguration {
  438. for _, node := range nodes {
  439. if node.NodeID.Equals(myID) {
  440. return nodes
  441. }
  442. }
  443. nodes = append(nodes, RepositoryNodeConfiguration{
  444. NodeID: myID,
  445. })
  446. return nodes
  447. }
  448. func ensureExistingNodes(nodes []RepositoryNodeConfiguration, existingNodes map[protocol.NodeID]bool) []RepositoryNodeConfiguration {
  449. count := len(nodes)
  450. i := 0
  451. loop:
  452. for i < count {
  453. if _, ok := existingNodes[nodes[i].NodeID]; !ok {
  454. nodes[i] = nodes[count-1]
  455. count--
  456. continue loop
  457. }
  458. i++
  459. }
  460. return nodes[0:count]
  461. }
  462. func ensureNoDuplicates(nodes []RepositoryNodeConfiguration) []RepositoryNodeConfiguration {
  463. count := len(nodes)
  464. i := 0
  465. seenNodes := make(map[protocol.NodeID]bool)
  466. loop:
  467. for i < count {
  468. id := nodes[i].NodeID
  469. if _, ok := seenNodes[id]; ok {
  470. nodes[i] = nodes[count-1]
  471. count--
  472. continue loop
  473. }
  474. seenNodes[id] = true
  475. i++
  476. }
  477. return nodes[0:count]
  478. }