manifest.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright 2017 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. // Command MANIFEST update
  17. package main
  18. import (
  19. "encoding/json"
  20. "fmt"
  21. "mime"
  22. "path/filepath"
  23. "strings"
  24. "github.com/ethereum/go-ethereum/cmd/utils"
  25. "github.com/ethereum/go-ethereum/swarm/api"
  26. swarm "github.com/ethereum/go-ethereum/swarm/api/client"
  27. "gopkg.in/urfave/cli.v1"
  28. )
  29. const bzzManifestJSON = "application/bzz-manifest+json"
  30. func add(ctx *cli.Context) {
  31. args := ctx.Args()
  32. if len(args) < 3 {
  33. utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH> [<content-type>]")
  34. }
  35. var (
  36. mhash = args[0]
  37. path = args[1]
  38. hash = args[2]
  39. ctype string
  40. wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
  41. mroot api.Manifest
  42. )
  43. if len(args) > 3 {
  44. ctype = args[3]
  45. } else {
  46. ctype = mime.TypeByExtension(filepath.Ext(path))
  47. }
  48. newManifest := addEntryToManifest(ctx, mhash, path, hash, ctype)
  49. fmt.Println(newManifest)
  50. if !wantManifest {
  51. // Print the manifest. This is the only output to stdout.
  52. mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
  53. fmt.Println(string(mrootJSON))
  54. return
  55. }
  56. }
  57. func update(ctx *cli.Context) {
  58. args := ctx.Args()
  59. if len(args) < 3 {
  60. utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH>")
  61. }
  62. var (
  63. mhash = args[0]
  64. path = args[1]
  65. hash = args[2]
  66. ctype string
  67. wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
  68. mroot api.Manifest
  69. )
  70. if len(args) > 3 {
  71. ctype = args[3]
  72. } else {
  73. ctype = mime.TypeByExtension(filepath.Ext(path))
  74. }
  75. newManifest := updateEntryInManifest(ctx, mhash, path, hash, ctype)
  76. fmt.Println(newManifest)
  77. if !wantManifest {
  78. // Print the manifest. This is the only output to stdout.
  79. mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
  80. fmt.Println(string(mrootJSON))
  81. return
  82. }
  83. }
  84. func remove(ctx *cli.Context) {
  85. args := ctx.Args()
  86. if len(args) < 2 {
  87. utils.Fatalf("Need at least two arguments <MHASH> <path>")
  88. }
  89. var (
  90. mhash = args[0]
  91. path = args[1]
  92. wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
  93. mroot api.Manifest
  94. )
  95. newManifest := removeEntryFromManifest(ctx, mhash, path)
  96. fmt.Println(newManifest)
  97. if !wantManifest {
  98. // Print the manifest. This is the only output to stdout.
  99. mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
  100. fmt.Println(string(mrootJSON))
  101. return
  102. }
  103. }
  104. func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
  105. var (
  106. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  107. client = swarm.NewClient(bzzapi)
  108. longestPathEntry = api.ManifestEntry{}
  109. )
  110. mroot, err := client.DownloadManifest(mhash)
  111. if err != nil {
  112. utils.Fatalf("Manifest download failed: %v", err)
  113. }
  114. //TODO: check if the "hash" to add is valid and present in swarm
  115. _, err = client.DownloadManifest(hash)
  116. if err != nil {
  117. utils.Fatalf("Hash to add is not present: %v", err)
  118. }
  119. // See if we path is in this Manifest or do we have to dig deeper
  120. for _, entry := range mroot.Entries {
  121. if path == entry.Path {
  122. utils.Fatalf("Path %s already present, not adding anything", path)
  123. } else {
  124. if entry.ContentType == bzzManifestJSON {
  125. prfxlen := strings.HasPrefix(path, entry.Path)
  126. if prfxlen && len(path) > len(longestPathEntry.Path) {
  127. longestPathEntry = entry
  128. }
  129. }
  130. }
  131. }
  132. if longestPathEntry.Path != "" {
  133. // Load the child Manifest add the entry there
  134. newPath := path[len(longestPathEntry.Path):]
  135. newHash := addEntryToManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
  136. // Replace the hash for parent Manifests
  137. newMRoot := &api.Manifest{}
  138. for _, entry := range mroot.Entries {
  139. if longestPathEntry.Path == entry.Path {
  140. entry.Hash = newHash
  141. }
  142. newMRoot.Entries = append(newMRoot.Entries, entry)
  143. }
  144. mroot = newMRoot
  145. } else {
  146. // Add the entry in the leaf Manifest
  147. newEntry := api.ManifestEntry{
  148. Hash: hash,
  149. Path: path,
  150. ContentType: ctype,
  151. }
  152. mroot.Entries = append(mroot.Entries, newEntry)
  153. }
  154. newManifestHash, err := client.UploadManifest(mroot)
  155. if err != nil {
  156. utils.Fatalf("Manifest upload failed: %v", err)
  157. }
  158. return newManifestHash
  159. }
  160. func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
  161. var (
  162. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  163. client = swarm.NewClient(bzzapi)
  164. newEntry = api.ManifestEntry{}
  165. longestPathEntry = api.ManifestEntry{}
  166. )
  167. mroot, err := client.DownloadManifest(mhash)
  168. if err != nil {
  169. utils.Fatalf("Manifest download failed: %v", err)
  170. }
  171. //TODO: check if the "hash" with which to update is valid and present in swarm
  172. // See if we path is in this Manifest or do we have to dig deeper
  173. for _, entry := range mroot.Entries {
  174. if path == entry.Path {
  175. newEntry = entry
  176. } else {
  177. if entry.ContentType == bzzManifestJSON {
  178. prfxlen := strings.HasPrefix(path, entry.Path)
  179. if prfxlen && len(path) > len(longestPathEntry.Path) {
  180. longestPathEntry = entry
  181. }
  182. }
  183. }
  184. }
  185. if longestPathEntry.Path == "" && newEntry.Path == "" {
  186. utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
  187. }
  188. if longestPathEntry.Path != "" {
  189. // Load the child Manifest add the entry there
  190. newPath := path[len(longestPathEntry.Path):]
  191. newHash := updateEntryInManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
  192. // Replace the hash for parent Manifests
  193. newMRoot := &api.Manifest{}
  194. for _, entry := range mroot.Entries {
  195. if longestPathEntry.Path == entry.Path {
  196. entry.Hash = newHash
  197. }
  198. newMRoot.Entries = append(newMRoot.Entries, entry)
  199. }
  200. mroot = newMRoot
  201. }
  202. if newEntry.Path != "" {
  203. // Replace the hash for leaf Manifest
  204. newMRoot := &api.Manifest{}
  205. for _, entry := range mroot.Entries {
  206. if newEntry.Path == entry.Path {
  207. myEntry := api.ManifestEntry{
  208. Hash: hash,
  209. Path: entry.Path,
  210. ContentType: ctype,
  211. }
  212. newMRoot.Entries = append(newMRoot.Entries, myEntry)
  213. } else {
  214. newMRoot.Entries = append(newMRoot.Entries, entry)
  215. }
  216. }
  217. mroot = newMRoot
  218. }
  219. newManifestHash, err := client.UploadManifest(mroot)
  220. if err != nil {
  221. utils.Fatalf("Manifest upload failed: %v", err)
  222. }
  223. return newManifestHash
  224. }
  225. func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
  226. var (
  227. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  228. client = swarm.NewClient(bzzapi)
  229. entryToRemove = api.ManifestEntry{}
  230. longestPathEntry = api.ManifestEntry{}
  231. )
  232. mroot, err := client.DownloadManifest(mhash)
  233. if err != nil {
  234. utils.Fatalf("Manifest download failed: %v", err)
  235. }
  236. // See if we path is in this Manifest or do we have to dig deeper
  237. for _, entry := range mroot.Entries {
  238. if path == entry.Path {
  239. entryToRemove = entry
  240. } else {
  241. if entry.ContentType == bzzManifestJSON {
  242. prfxlen := strings.HasPrefix(path, entry.Path)
  243. if prfxlen && len(path) > len(longestPathEntry.Path) {
  244. longestPathEntry = entry
  245. }
  246. }
  247. }
  248. }
  249. if longestPathEntry.Path == "" && entryToRemove.Path == "" {
  250. utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
  251. }
  252. if longestPathEntry.Path != "" {
  253. // Load the child Manifest remove the entry there
  254. newPath := path[len(longestPathEntry.Path):]
  255. newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath)
  256. // Replace the hash for parent Manifests
  257. newMRoot := &api.Manifest{}
  258. for _, entry := range mroot.Entries {
  259. if longestPathEntry.Path == entry.Path {
  260. entry.Hash = newHash
  261. }
  262. newMRoot.Entries = append(newMRoot.Entries, entry)
  263. }
  264. mroot = newMRoot
  265. }
  266. if entryToRemove.Path != "" {
  267. // remove the entry in this Manifest
  268. newMRoot := &api.Manifest{}
  269. for _, entry := range mroot.Entries {
  270. if entryToRemove.Path != entry.Path {
  271. newMRoot.Entries = append(newMRoot.Entries, entry)
  272. }
  273. }
  274. mroot = newMRoot
  275. }
  276. newManifestHash, err := client.UploadManifest(mroot)
  277. if err != nil {
  278. utils.Fatalf("Manifest upload failed: %v", err)
  279. }
  280. return newManifestHash
  281. }