aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/swarm
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/swarm')
-rw-r--r--cmd/swarm/main.go37
-rw-r--r--cmd/swarm/manifest.go360
-rw-r--r--cmd/swarm/upload.go26
3 files changed, 423 insertions, 0 deletions
diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go
index 34b3f71c4..546c646f1 100644
--- a/cmd/swarm/main.go
+++ b/cmd/swarm/main.go
@@ -154,6 +154,43 @@ The output of this command is supposed to be machine-readable.
Prints the swarm hash of file or directory.
`,
},
+ {
+ Name: "manifest",
+ Usage: "update a MANIFEST",
+ ArgsUsage: "manifest COMMAND",
+ Description: `
+Updates a MANIFEST by adding/removing/updating the hash of a path.
+`,
+ Subcommands: []cli.Command{
+ {
+ Action: add,
+ Name: "add",
+ Usage: "add a new path to the manifest",
+ ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]",
+ Description: `
+Adds a new path to the manifest
+`,
+ },
+ {
+ Action: update,
+ Name: "update",
+ Usage: "update the hash for an already existing path in the manifest",
+ ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]",
+ Description: `
+Update the hash for an already existing path in the manifest
+`,
+ },
+ {
+ Action: remove,
+ Name: "remove",
+ Usage: "removes a path from the manifest",
+ ArgsUsage: "<MANIFEST> <path>",
+ Description: `
+Removes a path from the manifest
+`,
+ },
+ },
+ },
}
app.Flags = []cli.Flag{
diff --git a/cmd/swarm/manifest.go b/cmd/swarm/manifest.go
new file mode 100644
index 000000000..0de0d69bb
--- /dev/null
+++ b/cmd/swarm/manifest.go
@@ -0,0 +1,360 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+// Command MANIFEST update
+package main
+
+import (
+ "gopkg.in/urfave/cli.v1"
+ "log"
+ "mime"
+ "path/filepath"
+ "strings"
+ "fmt"
+ "encoding/json"
+)
+
+func add(ctx *cli.Context) {
+
+ args := ctx.Args()
+ if len(args) < 3 {
+ log.Fatal("need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
+ }
+
+ var (
+ mhash = args[0]
+ path = args[1]
+ hash = args[2]
+
+ ctype string
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ mroot manifest
+ )
+
+
+ if len(args) > 3 {
+ ctype = args[3]
+ } else {
+ ctype = mime.TypeByExtension(filepath.Ext(path))
+ }
+
+ newManifest := addEntryToManifest (ctx, mhash, path, hash, ctype)
+ fmt.Println(newManifest)
+
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+}
+
+func update(ctx *cli.Context) {
+
+ args := ctx.Args()
+ if len(args) < 3 {
+ log.Fatal("need atleast three arguments <MHASH> <path> <HASH>")
+ }
+
+ var (
+ mhash = args[0]
+ path = args[1]
+ hash = args[2]
+
+ ctype string
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ mroot manifest
+ )
+ if len(args) > 3 {
+ ctype = args[3]
+ } else {
+ ctype = mime.TypeByExtension(filepath.Ext(path))
+ }
+
+ newManifest := updateEntryInManifest (ctx, mhash, path, hash, ctype)
+ fmt.Println(newManifest)
+
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+}
+
+func remove(ctx *cli.Context) {
+ args := ctx.Args()
+ if len(args) < 2 {
+ log.Fatal("need atleast two arguments <MHASH> <path>")
+ }
+
+ var (
+ mhash = args[0]
+ path = args[1]
+
+ wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
+ mroot manifest
+ )
+
+ newManifest := removeEntryFromManifest (ctx, mhash, path)
+ fmt.Println(newManifest)
+
+ if !wantManifest {
+ // Print the manifest. This is the only output to stdout.
+ mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
+ fmt.Println(string(mrootJSON))
+ return
+ }
+}
+
+func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) string {
+
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ client = &client{api: bzzapi}
+ longestPathEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ )
+
+ mroot, err := client.downloadManifest(mhash)
+ if err != nil {
+ log.Fatalln("manifest download failed:", err)
+ }
+
+ //TODO: check if the "hash" to add is valid and present in swarm
+ _, err = client.downloadManifest(hash)
+ if err != nil {
+ log.Fatalln("hash to add is not present:", err)
+ }
+
+
+ // See if we path is in this Manifest or do we have to dig deeper
+ for _, entry := range mroot.Entries {
+ if path == entry.Path {
+ log.Fatal(path, "Already present, not adding anything")
+ }else {
+ if entry.ContentType == "application/bzz-manifest+json" {
+ prfxlen := strings.HasPrefix(path, entry.Path)
+ if prfxlen && len(path) > len(longestPathEntry.Path) {
+ longestPathEntry = entry
+ }
+ }
+ }
+ }
+
+ if longestPathEntry.Path != "" {
+ // Load the child Manifest add the entry there
+ newPath := path[len(longestPathEntry.Path):]
+ newHash := addEntryToManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype)
+
+ // Replace the hash for parent Manifests
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if longestPathEntry.Path == entry.Path {
+ entry.Hash = newHash
+ }
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ mroot = newMRoot
+ } else {
+ // Add the entry in the leaf Manifest
+ newEntry := manifestEntry{
+ Path: path,
+ Hash: hash,
+ ContentType: ctype,
+ }
+ mroot.Entries = append(mroot.Entries, newEntry)
+ }
+
+
+ newManifestHash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ return newManifestHash
+
+
+
+}
+
+func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string) string {
+
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ client = &client{api: bzzapi}
+ newEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ longestPathEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ )
+
+ mroot, err := client.downloadManifest(mhash)
+ if err != nil {
+ log.Fatalln("manifest download failed:", err)
+ }
+
+ //TODO: check if the "hash" with which to update is valid and present in swarm
+
+
+ // See if we path is in this Manifest or do we have to dig deeper
+ for _, entry := range mroot.Entries {
+ if path == entry.Path {
+ newEntry = entry
+ }else {
+ if entry.ContentType == "application/bzz-manifest+json" {
+ prfxlen := strings.HasPrefix(path, entry.Path)
+ if prfxlen && len(path) > len(longestPathEntry.Path) {
+ longestPathEntry = entry
+ }
+ }
+ }
+ }
+
+ if longestPathEntry.Path == "" && newEntry.Path == "" {
+ log.Fatal(path, " Path not present in the Manifest, not setting anything")
+ }
+
+ if longestPathEntry.Path != "" {
+ // Load the child Manifest add the entry there
+ newPath := path[len(longestPathEntry.Path):]
+ newHash := updateEntryInManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype)
+
+ // Replace the hash for parent Manifests
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if longestPathEntry.Path == entry.Path {
+ entry.Hash = newHash
+ }
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+
+ }
+ mroot = newMRoot
+ }
+
+ if newEntry.Path != "" {
+ // Replace the hash for leaf Manifest
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if newEntry.Path == entry.Path {
+ myEntry := manifestEntry{
+ Path: entry.Path,
+ Hash: hash,
+ ContentType: ctype,
+ }
+ newMRoot.Entries = append(newMRoot.Entries, myEntry)
+ } else {
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ }
+ mroot = newMRoot
+ }
+
+
+ newManifestHash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ return newManifestHash
+}
+
+func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string {
+
+ var (
+ bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
+ client = &client{api: bzzapi}
+ entryToRemove = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ longestPathEntry = manifestEntry{
+ Path: "",
+ Hash: "",
+ ContentType: "",
+ }
+ )
+
+ mroot, err := client.downloadManifest(mhash)
+ if err != nil {
+ log.Fatalln("manifest download failed:", err)
+ }
+
+
+
+ // See if we path is in this Manifest or do we have to dig deeper
+ for _, entry := range mroot.Entries {
+ if path == entry.Path {
+ entryToRemove = entry
+ }else {
+ if entry.ContentType == "application/bzz-manifest+json" {
+ prfxlen := strings.HasPrefix(path, entry.Path)
+ if prfxlen && len(path) > len(longestPathEntry.Path) {
+ longestPathEntry = entry
+ }
+ }
+ }
+ }
+
+ if longestPathEntry.Path == "" && entryToRemove.Path == "" {
+ log.Fatal(path, "Path not present in the Manifest, not removing anything")
+ }
+
+ if longestPathEntry.Path != "" {
+ // Load the child Manifest remove the entry there
+ newPath := path[len(longestPathEntry.Path):]
+ newHash := removeEntryFromManifest (ctx, longestPathEntry.Hash, newPath)
+
+ // Replace the hash for parent Manifests
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if longestPathEntry.Path == entry.Path {
+ entry.Hash = newHash
+ }
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ mroot = newMRoot
+ }
+
+ if entryToRemove.Path != "" {
+ // remove the entry in this Manifest
+ newMRoot := manifest{}
+ for _, entry := range mroot.Entries {
+ if entryToRemove.Path != entry.Path {
+ newMRoot.Entries = append(newMRoot.Entries, entry)
+ }
+ }
+ mroot = newMRoot
+ }
+
+
+ newManifestHash, err := client.uploadManifest(mroot)
+ if err != nil {
+ log.Fatalln("manifest upload failed:", err)
+ }
+ return newManifestHash
+
+
+}
+
diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go
index d8039d45b..871713b2d 100644
--- a/cmd/swarm/upload.go
+++ b/cmd/swarm/upload.go
@@ -229,3 +229,29 @@ func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (strin
content, err := ioutil.ReadAll(resp.Body)
return string(content), err
}
+
+func (c *client) downloadManifest(mhash string) (manifest, error) {
+
+ mroot := manifest{}
+ req, err := http.NewRequest("GET", c.api + "/bzzr:/" + mhash, nil)
+ if err != nil {
+ return mroot, err
+ }
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return mroot, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode >= 400 {
+ return mroot, fmt.Errorf("bad status: %s", resp.Status)
+
+ }
+ content, err := ioutil.ReadAll(resp.Body)
+
+ err = json.Unmarshal(content, &mroot)
+ if err != nil {
+ return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err)
+ }
+ return mroot, err
+} \ No newline at end of file