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  }