332 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2017 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 (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"mime"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/ethereum/go-ethereum/cmd/utils"
 | 
						|
	"github.com/ethereum/go-ethereum/swarm/api"
 | 
						|
	swarm "github.com/ethereum/go-ethereum/swarm/api/client"
 | 
						|
	"gopkg.in/urfave/cli.v1"
 | 
						|
)
 | 
						|
 | 
						|
const bzzManifestJSON = "application/bzz-manifest+json"
 | 
						|
 | 
						|
func add(ctx *cli.Context) {
 | 
						|
	args := ctx.Args()
 | 
						|
	if len(args) < 3 {
 | 
						|
		utils.Fatalf("Need at least 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        api.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 {
 | 
						|
		utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH>")
 | 
						|
	}
 | 
						|
 | 
						|
	var (
 | 
						|
		mhash = args[0]
 | 
						|
		path  = args[1]
 | 
						|
		hash  = args[2]
 | 
						|
 | 
						|
		ctype        string
 | 
						|
		wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
 | 
						|
		mroot        api.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 {
 | 
						|
		utils.Fatalf("Need at least two arguments <MHASH> <path>")
 | 
						|
	}
 | 
						|
 | 
						|
	var (
 | 
						|
		mhash = args[0]
 | 
						|
		path  = args[1]
 | 
						|
 | 
						|
		wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
 | 
						|
		mroot        api.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           = swarm.NewClient(bzzapi)
 | 
						|
		longestPathEntry = api.ManifestEntry{}
 | 
						|
	)
 | 
						|
 | 
						|
	mroot, err := client.DownloadManifest(mhash)
 | 
						|
	if err != nil {
 | 
						|
		utils.Fatalf("Manifest download failed: %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	//TODO: check if the "hash" to add is valid and present in swarm
 | 
						|
	_, err = client.DownloadManifest(hash)
 | 
						|
	if err != nil {
 | 
						|
		utils.Fatalf("Hash to add is not present: %v", 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 {
 | 
						|
			utils.Fatalf("Path %s already present, not adding anything", path)
 | 
						|
		} else {
 | 
						|
			if entry.ContentType == bzzManifestJSON {
 | 
						|
				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 := &api.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 := api.ManifestEntry{
 | 
						|
			Hash:        hash,
 | 
						|
			Path:        path,
 | 
						|
			ContentType: ctype,
 | 
						|
		}
 | 
						|
		mroot.Entries = append(mroot.Entries, newEntry)
 | 
						|
	}
 | 
						|
 | 
						|
	newManifestHash, err := client.UploadManifest(mroot)
 | 
						|
	if err != nil {
 | 
						|
		utils.Fatalf("Manifest upload failed: %v", err)
 | 
						|
	}
 | 
						|
	return newManifestHash
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
 | 
						|
 | 
						|
	var (
 | 
						|
		bzzapi           = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
 | 
						|
		client           = swarm.NewClient(bzzapi)
 | 
						|
		newEntry         = api.ManifestEntry{}
 | 
						|
		longestPathEntry = api.ManifestEntry{}
 | 
						|
	)
 | 
						|
 | 
						|
	mroot, err := client.DownloadManifest(mhash)
 | 
						|
	if err != nil {
 | 
						|
		utils.Fatalf("Manifest download failed: %v", 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 == bzzManifestJSON {
 | 
						|
				prfxlen := strings.HasPrefix(path, entry.Path)
 | 
						|
				if prfxlen && len(path) > len(longestPathEntry.Path) {
 | 
						|
					longestPathEntry = entry
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if longestPathEntry.Path == "" && newEntry.Path == "" {
 | 
						|
		utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
 | 
						|
	}
 | 
						|
 | 
						|
	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 := &api.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 := &api.Manifest{}
 | 
						|
		for _, entry := range mroot.Entries {
 | 
						|
			if newEntry.Path == entry.Path {
 | 
						|
				myEntry := api.ManifestEntry{
 | 
						|
					Hash:        hash,
 | 
						|
					Path:        entry.Path,
 | 
						|
					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 {
 | 
						|
		utils.Fatalf("Manifest upload failed: %v", err)
 | 
						|
	}
 | 
						|
	return newManifestHash
 | 
						|
}
 | 
						|
 | 
						|
func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
 | 
						|
 | 
						|
	var (
 | 
						|
		bzzapi           = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
 | 
						|
		client           = swarm.NewClient(bzzapi)
 | 
						|
		entryToRemove    = api.ManifestEntry{}
 | 
						|
		longestPathEntry = api.ManifestEntry{}
 | 
						|
	)
 | 
						|
 | 
						|
	mroot, err := client.DownloadManifest(mhash)
 | 
						|
	if err != nil {
 | 
						|
		utils.Fatalf("Manifest download failed: %v", 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 == bzzManifestJSON {
 | 
						|
				prfxlen := strings.HasPrefix(path, entry.Path)
 | 
						|
				if prfxlen && len(path) > len(longestPathEntry.Path) {
 | 
						|
					longestPathEntry = entry
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if longestPathEntry.Path == "" && entryToRemove.Path == "" {
 | 
						|
		utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
 | 
						|
	}
 | 
						|
 | 
						|
	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 := &api.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 := &api.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 {
 | 
						|
		utils.Fatalf("Manifest upload failed: %v", err)
 | 
						|
	}
 | 
						|
	return newManifestHash
 | 
						|
}
 |