manifest.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. // Copyright 2016 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. swarm "github.com/ethereum/go-ethereum/swarm/api/client"
  26. "gopkg.in/urfave/cli.v1"
  27. )
  28. func add(ctx *cli.Context) {
  29. args := ctx.Args()
  30. if len(args) < 3 {
  31. utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
  32. }
  33. var (
  34. mhash = args[0]
  35. path = args[1]
  36. hash = args[2]
  37. ctype string
  38. wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
  39. mroot swarm.Manifest
  40. )
  41. if len(args) > 3 {
  42. ctype = args[3]
  43. } else {
  44. ctype = mime.TypeByExtension(filepath.Ext(path))
  45. }
  46. newManifest := addEntryToManifest(ctx, mhash, path, hash, ctype)
  47. fmt.Println(newManifest)
  48. if !wantManifest {
  49. // Print the manifest. This is the only output to stdout.
  50. mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
  51. fmt.Println(string(mrootJSON))
  52. return
  53. }
  54. }
  55. func update(ctx *cli.Context) {
  56. args := ctx.Args()
  57. if len(args) < 3 {
  58. utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH>")
  59. }
  60. var (
  61. mhash = args[0]
  62. path = args[1]
  63. hash = args[2]
  64. ctype string
  65. wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
  66. mroot swarm.Manifest
  67. )
  68. if len(args) > 3 {
  69. ctype = args[3]
  70. } else {
  71. ctype = mime.TypeByExtension(filepath.Ext(path))
  72. }
  73. newManifest := updateEntryInManifest(ctx, mhash, path, hash, ctype)
  74. fmt.Println(newManifest)
  75. if !wantManifest {
  76. // Print the manifest. This is the only output to stdout.
  77. mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
  78. fmt.Println(string(mrootJSON))
  79. return
  80. }
  81. }
  82. func remove(ctx *cli.Context) {
  83. args := ctx.Args()
  84. if len(args) < 2 {
  85. utils.Fatalf("Need atleast two arguments <MHASH> <path>")
  86. }
  87. var (
  88. mhash = args[0]
  89. path = args[1]
  90. wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
  91. mroot swarm.Manifest
  92. )
  93. newManifest := removeEntryFromManifest(ctx, mhash, path)
  94. fmt.Println(newManifest)
  95. if !wantManifest {
  96. // Print the manifest. This is the only output to stdout.
  97. mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
  98. fmt.Println(string(mrootJSON))
  99. return
  100. }
  101. }
  102. func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
  103. var (
  104. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  105. client = swarm.NewClient(bzzapi)
  106. longestPathEntry = swarm.ManifestEntry{
  107. Path: "",
  108. Hash: "",
  109. ContentType: "",
  110. }
  111. )
  112. mroot, err := client.DownloadManifest(mhash)
  113. if err != nil {
  114. utils.Fatalf("Manifest download failed: %v", err)
  115. }
  116. //TODO: check if the "hash" to add is valid and present in swarm
  117. _, err = client.DownloadManifest(hash)
  118. if err != nil {
  119. utils.Fatalf("Hash to add is not present: %v", err)
  120. }
  121. // See if we path is in this Manifest or do we have to dig deeper
  122. for _, entry := range mroot.Entries {
  123. if path == entry.Path {
  124. utils.Fatalf("Path %s already present, not adding anything", path)
  125. } else {
  126. if entry.ContentType == "application/bzz-manifest+json" {
  127. prfxlen := strings.HasPrefix(path, entry.Path)
  128. if prfxlen && len(path) > len(longestPathEntry.Path) {
  129. longestPathEntry = entry
  130. }
  131. }
  132. }
  133. }
  134. if longestPathEntry.Path != "" {
  135. // Load the child Manifest add the entry there
  136. newPath := path[len(longestPathEntry.Path):]
  137. newHash := addEntryToManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
  138. // Replace the hash for parent Manifests
  139. newMRoot := swarm.Manifest{}
  140. for _, entry := range mroot.Entries {
  141. if longestPathEntry.Path == entry.Path {
  142. entry.Hash = newHash
  143. }
  144. newMRoot.Entries = append(newMRoot.Entries, entry)
  145. }
  146. mroot = newMRoot
  147. } else {
  148. // Add the entry in the leaf Manifest
  149. newEntry := swarm.ManifestEntry{
  150. Path: path,
  151. Hash: hash,
  152. ContentType: ctype,
  153. }
  154. mroot.Entries = append(mroot.Entries, newEntry)
  155. }
  156. newManifestHash, err := client.UploadManifest(mroot)
  157. if err != nil {
  158. utils.Fatalf("Manifest upload failed: %v", err)
  159. }
  160. return newManifestHash
  161. }
  162. func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
  163. var (
  164. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  165. client = swarm.NewClient(bzzapi)
  166. newEntry = swarm.ManifestEntry{
  167. Path: "",
  168. Hash: "",
  169. ContentType: "",
  170. }
  171. longestPathEntry = swarm.ManifestEntry{
  172. Path: "",
  173. Hash: "",
  174. ContentType: "",
  175. }
  176. )
  177. mroot, err := client.DownloadManifest(mhash)
  178. if err != nil {
  179. utils.Fatalf("Manifest download failed: %v", err)
  180. }
  181. //TODO: check if the "hash" with which to update is valid and present in swarm
  182. // See if we path is in this Manifest or do we have to dig deeper
  183. for _, entry := range mroot.Entries {
  184. if path == entry.Path {
  185. newEntry = entry
  186. } else {
  187. if entry.ContentType == "application/bzz-manifest+json" {
  188. prfxlen := strings.HasPrefix(path, entry.Path)
  189. if prfxlen && len(path) > len(longestPathEntry.Path) {
  190. longestPathEntry = entry
  191. }
  192. }
  193. }
  194. }
  195. if longestPathEntry.Path == "" && newEntry.Path == "" {
  196. utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
  197. }
  198. if longestPathEntry.Path != "" {
  199. // Load the child Manifest add the entry there
  200. newPath := path[len(longestPathEntry.Path):]
  201. newHash := updateEntryInManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
  202. // Replace the hash for parent Manifests
  203. newMRoot := swarm.Manifest{}
  204. for _, entry := range mroot.Entries {
  205. if longestPathEntry.Path == entry.Path {
  206. entry.Hash = newHash
  207. }
  208. newMRoot.Entries = append(newMRoot.Entries, entry)
  209. }
  210. mroot = newMRoot
  211. }
  212. if newEntry.Path != "" {
  213. // Replace the hash for leaf Manifest
  214. newMRoot := swarm.Manifest{}
  215. for _, entry := range mroot.Entries {
  216. if newEntry.Path == entry.Path {
  217. myEntry := swarm.ManifestEntry{
  218. Path: entry.Path,
  219. Hash: hash,
  220. ContentType: ctype,
  221. }
  222. newMRoot.Entries = append(newMRoot.Entries, myEntry)
  223. } else {
  224. newMRoot.Entries = append(newMRoot.Entries, entry)
  225. }
  226. }
  227. mroot = newMRoot
  228. }
  229. newManifestHash, err := client.UploadManifest(mroot)
  230. if err != nil {
  231. utils.Fatalf("Manifest upload failed: %v", err)
  232. }
  233. return newManifestHash
  234. }
  235. func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
  236. var (
  237. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  238. client = swarm.NewClient(bzzapi)
  239. entryToRemove = swarm.ManifestEntry{
  240. Path: "",
  241. Hash: "",
  242. ContentType: "",
  243. }
  244. longestPathEntry = swarm.ManifestEntry{
  245. Path: "",
  246. Hash: "",
  247. ContentType: "",
  248. }
  249. )
  250. mroot, err := client.DownloadManifest(mhash)
  251. if err != nil {
  252. utils.Fatalf("Manifest download failed: %v", err)
  253. }
  254. // See if we path is in this Manifest or do we have to dig deeper
  255. for _, entry := range mroot.Entries {
  256. if path == entry.Path {
  257. entryToRemove = entry
  258. } else {
  259. if entry.ContentType == "application/bzz-manifest+json" {
  260. prfxlen := strings.HasPrefix(path, entry.Path)
  261. if prfxlen && len(path) > len(longestPathEntry.Path) {
  262. longestPathEntry = entry
  263. }
  264. }
  265. }
  266. }
  267. if longestPathEntry.Path == "" && entryToRemove.Path == "" {
  268. utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
  269. }
  270. if longestPathEntry.Path != "" {
  271. // Load the child Manifest remove the entry there
  272. newPath := path[len(longestPathEntry.Path):]
  273. newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath)
  274. // Replace the hash for parent Manifests
  275. newMRoot := swarm.Manifest{}
  276. for _, entry := range mroot.Entries {
  277. if longestPathEntry.Path == entry.Path {
  278. entry.Hash = newHash
  279. }
  280. newMRoot.Entries = append(newMRoot.Entries, entry)
  281. }
  282. mroot = newMRoot
  283. }
  284. if entryToRemove.Path != "" {
  285. // remove the entry in this Manifest
  286. newMRoot := swarm.Manifest{}
  287. for _, entry := range mroot.Entries {
  288. if entryToRemove.Path != entry.Path {
  289. newMRoot.Entries = append(newMRoot.Entries, entry)
  290. }
  291. }
  292. mroot = newMRoot
  293. }
  294. newManifestHash, err := client.UploadManifest(mroot)
  295. if err != nil {
  296. utils.Fatalf("Manifest upload failed: %v", err)
  297. }
  298. return newManifestHash
  299. }