github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/cmd/swarm/manifest.go (about)

     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  
    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/ethereum/go-ethereum/cmd/utils"
    28  	"gopkg.in/urfave/cli.v1"
    29  )
    30  
    31  func add(ctx *cli.Context) {
    32  	args := ctx.Args()
    33  	if len(args) < 3 {
    34  		utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
    35  	}
    36  
    37  	var (
    38  		mhash = args[0]
    39  		path  = args[1]
    40  		hash  = args[2]
    41  
    42  		ctype        string
    43  		wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
    44  		mroot        manifest
    45  	)
    46  
    47  	if len(args) > 3 {
    48  		ctype = args[3]
    49  	} else {
    50  		ctype = mime.TypeByExtension(filepath.Ext(path))
    51  	}
    52  
    53  	newManifest := addEntryToManifest(ctx, mhash, path, hash, ctype)
    54  	fmt.Println(newManifest)
    55  
    56  	if !wantManifest {
    57  		// Print the manifest. This is the only output to stdout.
    58  		mrootJSON, _ := json.MarshalIndent(mroot, "", "  ")
    59  		fmt.Println(string(mrootJSON))
    60  		return
    61  	}
    62  }
    63  
    64  func update(ctx *cli.Context) {
    65  
    66  	args := ctx.Args()
    67  	if len(args) < 3 {
    68  		utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH>")
    69  	}
    70  
    71  	var (
    72  		mhash = args[0]
    73  		path  = args[1]
    74  		hash  = args[2]
    75  
    76  		ctype        string
    77  		wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
    78  		mroot        manifest
    79  	)
    80  	if len(args) > 3 {
    81  		ctype = args[3]
    82  	} else {
    83  		ctype = mime.TypeByExtension(filepath.Ext(path))
    84  	}
    85  
    86  	newManifest := updateEntryInManifest(ctx, mhash, path, hash, ctype)
    87  	fmt.Println(newManifest)
    88  
    89  	if !wantManifest {
    90  		// Print the manifest. This is the only output to stdout.
    91  		mrootJSON, _ := json.MarshalIndent(mroot, "", "  ")
    92  		fmt.Println(string(mrootJSON))
    93  		return
    94  	}
    95  }
    96  
    97  func remove(ctx *cli.Context) {
    98  	args := ctx.Args()
    99  	if len(args) < 2 {
   100  		utils.Fatalf("Need atleast two arguments <MHASH> <path>")
   101  	}
   102  
   103  	var (
   104  		mhash = args[0]
   105  		path  = args[1]
   106  
   107  		wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
   108  		mroot        manifest
   109  	)
   110  
   111  	newManifest := removeEntryFromManifest(ctx, mhash, path)
   112  	fmt.Println(newManifest)
   113  
   114  	if !wantManifest {
   115  		// Print the manifest. This is the only output to stdout.
   116  		mrootJSON, _ := json.MarshalIndent(mroot, "", "  ")
   117  		fmt.Println(string(mrootJSON))
   118  		return
   119  	}
   120  }
   121  
   122  func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
   123  
   124  	var (
   125  		bzzapi           = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
   126  		client           = &client{api: bzzapi}
   127  		longestPathEntry = manifestEntry{
   128  			Path:        "",
   129  			Hash:        "",
   130  			ContentType: "",
   131  		}
   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 == "application/bzz-manifest+json" {
   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 := 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 := manifestEntry{
   176  			Path:        path,
   177  			Hash:        hash,
   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   = &client{api: bzzapi}
   196  		newEntry = manifestEntry{
   197  			Path:        "",
   198  			Hash:        "",
   199  			ContentType: "",
   200  		}
   201  		longestPathEntry = manifestEntry{
   202  			Path:        "",
   203  			Hash:        "",
   204  			ContentType: "",
   205  		}
   206  	)
   207  
   208  	mroot, err := client.downloadManifest(mhash)
   209  	if err != nil {
   210  		utils.Fatalf("Manifest download failed: %v", err)
   211  	}
   212  
   213  	//TODO: check if the "hash" with which to update is valid and present in swarm
   214  
   215  	// See if we path is in this Manifest or do we have to dig deeper
   216  	for _, entry := range mroot.Entries {
   217  		if path == entry.Path {
   218  			newEntry = entry
   219  		} else {
   220  			if entry.ContentType == "application/bzz-manifest+json" {
   221  				prfxlen := strings.HasPrefix(path, entry.Path)
   222  				if prfxlen && len(path) > len(longestPathEntry.Path) {
   223  					longestPathEntry = entry
   224  				}
   225  			}
   226  		}
   227  	}
   228  
   229  	if longestPathEntry.Path == "" && newEntry.Path == "" {
   230  		utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
   231  	}
   232  
   233  	if longestPathEntry.Path != "" {
   234  		// Load the child Manifest add the entry there
   235  		newPath := path[len(longestPathEntry.Path):]
   236  		newHash := updateEntryInManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
   237  
   238  		// Replace the hash for parent Manifests
   239  		newMRoot := manifest{}
   240  		for _, entry := range mroot.Entries {
   241  			if longestPathEntry.Path == entry.Path {
   242  				entry.Hash = newHash
   243  			}
   244  			newMRoot.Entries = append(newMRoot.Entries, entry)
   245  
   246  		}
   247  		mroot = newMRoot
   248  	}
   249  
   250  	if newEntry.Path != "" {
   251  		// Replace the hash for leaf Manifest
   252  		newMRoot := manifest{}
   253  		for _, entry := range mroot.Entries {
   254  			if newEntry.Path == entry.Path {
   255  				myEntry := manifestEntry{
   256  					Path:        entry.Path,
   257  					Hash:        hash,
   258  					ContentType: ctype,
   259  				}
   260  				newMRoot.Entries = append(newMRoot.Entries, myEntry)
   261  			} else {
   262  				newMRoot.Entries = append(newMRoot.Entries, entry)
   263  			}
   264  		}
   265  		mroot = newMRoot
   266  	}
   267  
   268  	newManifestHash, err := client.uploadManifest(mroot)
   269  	if err != nil {
   270  		utils.Fatalf("Manifest upload failed: %v", err)
   271  	}
   272  	return newManifestHash
   273  }
   274  
   275  func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
   276  
   277  	var (
   278  		bzzapi        = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
   279  		client        = &client{api: bzzapi}
   280  		entryToRemove = manifestEntry{
   281  			Path:        "",
   282  			Hash:        "",
   283  			ContentType: "",
   284  		}
   285  		longestPathEntry = manifestEntry{
   286  			Path:        "",
   287  			Hash:        "",
   288  			ContentType: "",
   289  		}
   290  	)
   291  
   292  	mroot, err := client.downloadManifest(mhash)
   293  	if err != nil {
   294  		utils.Fatalf("Manifest download failed: %v", err)
   295  	}
   296  
   297  	// See if we path is in this Manifest or do we have to dig deeper
   298  	for _, entry := range mroot.Entries {
   299  		if path == entry.Path {
   300  			entryToRemove = entry
   301  		} else {
   302  			if entry.ContentType == "application/bzz-manifest+json" {
   303  				prfxlen := strings.HasPrefix(path, entry.Path)
   304  				if prfxlen && len(path) > len(longestPathEntry.Path) {
   305  					longestPathEntry = entry
   306  				}
   307  			}
   308  		}
   309  	}
   310  
   311  	if longestPathEntry.Path == "" && entryToRemove.Path == "" {
   312  		utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
   313  	}
   314  
   315  	if longestPathEntry.Path != "" {
   316  		// Load the child Manifest remove the entry there
   317  		newPath := path[len(longestPathEntry.Path):]
   318  		newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath)
   319  
   320  		// Replace the hash for parent Manifests
   321  		newMRoot := manifest{}
   322  		for _, entry := range mroot.Entries {
   323  			if longestPathEntry.Path == entry.Path {
   324  				entry.Hash = newHash
   325  			}
   326  			newMRoot.Entries = append(newMRoot.Entries, entry)
   327  		}
   328  		mroot = newMRoot
   329  	}
   330  
   331  	if entryToRemove.Path != "" {
   332  		// remove the entry in this Manifest
   333  		newMRoot := manifest{}
   334  		for _, entry := range mroot.Entries {
   335  			if entryToRemove.Path != entry.Path {
   336  				newMRoot.Entries = append(newMRoot.Entries, entry)
   337  			}
   338  		}
   339  		mroot = newMRoot
   340  	}
   341  
   342  	newManifestHash, err := client.uploadManifest(mroot)
   343  	if err != nil {
   344  		utils.Fatalf("Manifest upload failed: %v", err)
   345  	}
   346  	return newManifestHash
   347  }