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