cmd_folders.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright (C) 2014 Audrius Butkevičius
  2. package main
  3. import (
  4. "fmt"
  5. "path/filepath"
  6. "strings"
  7. "github.com/AudriusButkevicius/cli"
  8. "github.com/syncthing/syncthing/lib/config"
  9. )
  10. func init() {
  11. cliCommands = append(cliCommands, cli.Command{
  12. Name: "folders",
  13. HideHelp: true,
  14. Usage: "Folder command group",
  15. Subcommands: []cli.Command{
  16. {
  17. Name: "list",
  18. Usage: "List available folders",
  19. Requires: &cli.Requires{},
  20. Action: foldersList,
  21. },
  22. {
  23. Name: "add",
  24. Usage: "Add a new folder",
  25. Requires: &cli.Requires{"folder id", "directory"},
  26. Action: foldersAdd,
  27. },
  28. {
  29. Name: "remove",
  30. Usage: "Remove an existing folder",
  31. Requires: &cli.Requires{"folder id"},
  32. Action: foldersRemove,
  33. },
  34. {
  35. Name: "override",
  36. Usage: "Override changes from other nodes for a master folder",
  37. Requires: &cli.Requires{"folder id"},
  38. Action: foldersOverride,
  39. },
  40. {
  41. Name: "get",
  42. Usage: "Get a property of a folder",
  43. Requires: &cli.Requires{"folder id", "property"},
  44. Action: foldersGet,
  45. },
  46. {
  47. Name: "set",
  48. Usage: "Set a property of a folder",
  49. Requires: &cli.Requires{"folder id", "property", "value..."},
  50. Action: foldersSet,
  51. },
  52. {
  53. Name: "unset",
  54. Usage: "Unset a property of a folder",
  55. Requires: &cli.Requires{"folder id", "property"},
  56. Action: foldersUnset,
  57. },
  58. {
  59. Name: "devices",
  60. Usage: "Folder devices command group",
  61. HideHelp: true,
  62. Subcommands: []cli.Command{
  63. {
  64. Name: "list",
  65. Usage: "List of devices which the folder is shared with",
  66. Requires: &cli.Requires{"folder id"},
  67. Action: foldersDevicesList,
  68. },
  69. {
  70. Name: "add",
  71. Usage: "Share a folder with a device",
  72. Requires: &cli.Requires{"folder id", "device id"},
  73. Action: foldersDevicesAdd,
  74. },
  75. {
  76. Name: "remove",
  77. Usage: "Unshare a folder with a device",
  78. Requires: &cli.Requires{"folder id", "device id"},
  79. Action: foldersDevicesRemove,
  80. },
  81. {
  82. Name: "clear",
  83. Usage: "Unshare a folder with all devices",
  84. Requires: &cli.Requires{"folder id"},
  85. Action: foldersDevicesClear,
  86. },
  87. },
  88. },
  89. },
  90. })
  91. }
  92. func foldersList(c *cli.Context) {
  93. cfg := getConfig(c)
  94. first := true
  95. writer := newTableWriter()
  96. for _, folder := range cfg.Folders {
  97. if !first {
  98. fmt.Fprintln(writer)
  99. }
  100. fmt.Fprintln(writer, "ID:\t", folder.ID, "\t")
  101. fmt.Fprintln(writer, "Path:\t", folder.RawPath, "\t(directory)")
  102. fmt.Fprintln(writer, "Folder type:\t", folder.Type, "\t(type)")
  103. fmt.Fprintln(writer, "Ignore permissions:\t", folder.IgnorePerms, "\t(permissions)")
  104. fmt.Fprintln(writer, "Rescan interval in seconds:\t", folder.RescanIntervalS, "\t(rescan)")
  105. if folder.Versioning.Type != "" {
  106. fmt.Fprintln(writer, "Versioning:\t", folder.Versioning.Type, "\t(versioning)")
  107. for key, value := range folder.Versioning.Params {
  108. fmt.Fprintf(writer, "Versioning %s:\t %s \t(versioning-%s)\n", key, value, key)
  109. }
  110. }
  111. first = false
  112. }
  113. writer.Flush()
  114. }
  115. func foldersAdd(c *cli.Context) {
  116. cfg := getConfig(c)
  117. abs, err := filepath.Abs(c.Args()[1])
  118. die(err)
  119. folder := config.FolderConfiguration{
  120. ID: c.Args()[0],
  121. RawPath: filepath.Clean(abs),
  122. }
  123. cfg.Folders = append(cfg.Folders, folder)
  124. setConfig(c, cfg)
  125. }
  126. func foldersRemove(c *cli.Context) {
  127. cfg := getConfig(c)
  128. rid := c.Args()[0]
  129. for i, folder := range cfg.Folders {
  130. if folder.ID == rid {
  131. last := len(cfg.Folders) - 1
  132. cfg.Folders[i] = cfg.Folders[last]
  133. cfg.Folders = cfg.Folders[:last]
  134. setConfig(c, cfg)
  135. return
  136. }
  137. }
  138. die("Folder " + rid + " not found")
  139. }
  140. func foldersOverride(c *cli.Context) {
  141. cfg := getConfig(c)
  142. rid := c.Args()[0]
  143. for _, folder := range cfg.Folders {
  144. if folder.ID == rid && folder.Type == config.FolderTypeSendOnly {
  145. response := httpPost(c, "db/override", "")
  146. if response.StatusCode != 200 {
  147. err := fmt.Sprint("Failed to override changes\nStatus code: ", response.StatusCode)
  148. body := string(responseToBArray(response))
  149. if body != "" {
  150. err += "\nBody: " + body
  151. }
  152. die(err)
  153. }
  154. return
  155. }
  156. }
  157. die("Folder " + rid + " not found or folder not master")
  158. }
  159. func foldersGet(c *cli.Context) {
  160. cfg := getConfig(c)
  161. rid := c.Args()[0]
  162. arg := strings.ToLower(c.Args()[1])
  163. for _, folder := range cfg.Folders {
  164. if folder.ID != rid {
  165. continue
  166. }
  167. if strings.HasPrefix(arg, "versioning-") {
  168. arg = arg[11:]
  169. value, ok := folder.Versioning.Params[arg]
  170. if ok {
  171. fmt.Println(value)
  172. return
  173. }
  174. die("Versioning property " + c.Args()[1][11:] + " not found")
  175. }
  176. switch arg {
  177. case "directory":
  178. fmt.Println(folder.RawPath)
  179. case "type":
  180. fmt.Println(folder.Type)
  181. case "permissions":
  182. fmt.Println(folder.IgnorePerms)
  183. case "rescan":
  184. fmt.Println(folder.RescanIntervalS)
  185. case "versioning":
  186. if folder.Versioning.Type != "" {
  187. fmt.Println(folder.Versioning.Type)
  188. }
  189. default:
  190. die("Invalid property: " + c.Args()[1] + "\nAvailable properties: directory, type, permissions, versioning, versioning-<key>")
  191. }
  192. return
  193. }
  194. die("Folder " + rid + " not found")
  195. }
  196. func foldersSet(c *cli.Context) {
  197. rid := c.Args()[0]
  198. arg := strings.ToLower(c.Args()[1])
  199. val := strings.Join(c.Args()[2:], " ")
  200. cfg := getConfig(c)
  201. for i, folder := range cfg.Folders {
  202. if folder.ID != rid {
  203. continue
  204. }
  205. if strings.HasPrefix(arg, "versioning-") {
  206. cfg.Folders[i].Versioning.Params[arg[11:]] = val
  207. setConfig(c, cfg)
  208. return
  209. }
  210. switch arg {
  211. case "directory":
  212. cfg.Folders[i].RawPath = val
  213. case "type":
  214. var t config.FolderType
  215. if err := t.UnmarshalText([]byte(val)); err != nil {
  216. die("Invalid folder type: " + err.Error())
  217. }
  218. cfg.Folders[i].Type = t
  219. case "permissions":
  220. cfg.Folders[i].IgnorePerms = parseBool(val)
  221. case "rescan":
  222. cfg.Folders[i].RescanIntervalS = parseInt(val)
  223. case "versioning":
  224. cfg.Folders[i].Versioning.Type = val
  225. default:
  226. die("Invalid property: " + c.Args()[1] + "\nAvailable properties: directory, master, permissions, versioning, versioning-<key>")
  227. }
  228. setConfig(c, cfg)
  229. return
  230. }
  231. die("Folder " + rid + " not found")
  232. }
  233. func foldersUnset(c *cli.Context) {
  234. rid := c.Args()[0]
  235. arg := strings.ToLower(c.Args()[1])
  236. cfg := getConfig(c)
  237. for i, folder := range cfg.Folders {
  238. if folder.ID != rid {
  239. continue
  240. }
  241. if strings.HasPrefix(arg, "versioning-") {
  242. arg = arg[11:]
  243. if _, ok := folder.Versioning.Params[arg]; ok {
  244. delete(cfg.Folders[i].Versioning.Params, arg)
  245. setConfig(c, cfg)
  246. return
  247. }
  248. die("Versioning property " + c.Args()[1][11:] + " not found")
  249. }
  250. switch arg {
  251. case "versioning":
  252. cfg.Folders[i].Versioning.Type = ""
  253. cfg.Folders[i].Versioning.Params = make(map[string]string)
  254. default:
  255. die("Invalid property: " + c.Args()[1] + "\nAvailable properties: versioning, versioning-<key>")
  256. }
  257. setConfig(c, cfg)
  258. return
  259. }
  260. die("Folder " + rid + " not found")
  261. }
  262. func foldersDevicesList(c *cli.Context) {
  263. rid := c.Args()[0]
  264. cfg := getConfig(c)
  265. for _, folder := range cfg.Folders {
  266. if folder.ID != rid {
  267. continue
  268. }
  269. for _, device := range folder.Devices {
  270. fmt.Println(device.DeviceID)
  271. }
  272. return
  273. }
  274. die("Folder " + rid + " not found")
  275. }
  276. func foldersDevicesAdd(c *cli.Context) {
  277. rid := c.Args()[0]
  278. nid := parseDeviceID(c.Args()[1])
  279. cfg := getConfig(c)
  280. for i, folder := range cfg.Folders {
  281. if folder.ID != rid {
  282. continue
  283. }
  284. for _, device := range folder.Devices {
  285. if device.DeviceID == nid {
  286. die("Device " + c.Args()[1] + " is already part of this folder")
  287. }
  288. }
  289. for _, device := range cfg.Devices {
  290. if device.DeviceID == nid {
  291. cfg.Folders[i].Devices = append(folder.Devices, config.FolderDeviceConfiguration{
  292. DeviceID: device.DeviceID,
  293. })
  294. setConfig(c, cfg)
  295. return
  296. }
  297. }
  298. die("Device " + c.Args()[1] + " not found in device list")
  299. }
  300. die("Folder " + rid + " not found")
  301. }
  302. func foldersDevicesRemove(c *cli.Context) {
  303. rid := c.Args()[0]
  304. nid := parseDeviceID(c.Args()[1])
  305. cfg := getConfig(c)
  306. for ri, folder := range cfg.Folders {
  307. if folder.ID != rid {
  308. continue
  309. }
  310. for ni, device := range folder.Devices {
  311. if device.DeviceID == nid {
  312. last := len(folder.Devices) - 1
  313. cfg.Folders[ri].Devices[ni] = folder.Devices[last]
  314. cfg.Folders[ri].Devices = cfg.Folders[ri].Devices[:last]
  315. setConfig(c, cfg)
  316. return
  317. }
  318. }
  319. die("Device " + c.Args()[1] + " not found")
  320. }
  321. die("Folder " + rid + " not found")
  322. }
  323. func foldersDevicesClear(c *cli.Context) {
  324. rid := c.Args()[0]
  325. cfg := getConfig(c)
  326. for i, folder := range cfg.Folders {
  327. if folder.ID != rid {
  328. continue
  329. }
  330. cfg.Folders[i].Devices = []config.FolderDeviceConfiguration{}
  331. setConfig(c, cfg)
  332. return
  333. }
  334. die("Folder " + rid + " not found")
  335. }