/ core / commands / cmdenv / cidbase.go
cidbase.go
  1  package cmdenv
  2  
  3  import (
  4  	"fmt"
  5  	"strings"
  6  
  7  	cid "github.com/ipfs/go-cid"
  8  	cidenc "github.com/ipfs/go-cidutil/cidenc"
  9  	cmds "github.com/ipfs/go-ipfs-cmds"
 10  	mbase "github.com/multiformats/go-multibase"
 11  )
 12  
 13  var (
 14  	OptionCidBase              = cmds.StringOption("cid-base", "Multibase encoding used for version 1 CIDs in output.")
 15  	OptionUpgradeCidV0InOutput = cmds.BoolOption("upgrade-cidv0-in-output", "Upgrade version 0 to version 1 CIDs in output.")
 16  )
 17  
 18  // GetCidEncoder processes the `cid-base` and `output-cidv1` options and
 19  // returns an encoder to use based on those parameters.
 20  func GetCidEncoder(req *cmds.Request) (cidenc.Encoder, error) {
 21  	return getCidBase(req, true)
 22  }
 23  
 24  // GetLowLevelCidEncoder is like GetCidEncoder but meant to be used by lower
 25  // level commands. It differs from GetCidEncoder in that CIDv0 are not, by
 26  // default, auto-upgraded to CIDv1.
 27  func GetLowLevelCidEncoder(req *cmds.Request) (cidenc.Encoder, error) {
 28  	return getCidBase(req, false)
 29  }
 30  
 31  func getCidBase(req *cmds.Request, autoUpgrade bool) (cidenc.Encoder, error) {
 32  	base, _ := req.Options[OptionCidBase.Name()].(string)
 33  	upgrade, upgradeDefined := req.Options[OptionUpgradeCidV0InOutput.Name()].(bool)
 34  
 35  	e := cidenc.Default()
 36  
 37  	if base != "" {
 38  		var err error
 39  		e.Base, err = mbase.EncoderByName(base)
 40  		if err != nil {
 41  			return e, err
 42  		}
 43  		if autoUpgrade {
 44  			e.Upgrade = true
 45  		}
 46  	}
 47  
 48  	if upgradeDefined {
 49  		e.Upgrade = upgrade
 50  	}
 51  
 52  	return e, nil
 53  }
 54  
 55  // CidBaseDefined returns true if the `cid-base` option is specified on the
 56  // command line
 57  func CidBaseDefined(req *cmds.Request) bool {
 58  	base, _ := req.Options["cid-base"].(string)
 59  	return base != ""
 60  }
 61  
 62  // CidEncoderFromPath creates a new encoder that is influenced from the encoded
 63  // Cid in a Path. For CIDv0 the multibase from the base encoder is used and
 64  // automatic upgrades are disabled. For CIDv1 the multibase from the CID is
 65  // used and upgrades are enabled.
 66  //
 67  // This logic is intentionally fuzzy and matches anything of the form
 68  // `CidLike`, `CidLike/...`, or `/namespace/CidLike/...`.
 69  //
 70  // For example:
 71  //
 72  // * Qm...
 73  // * Qm.../...
 74  // * /ipfs/Qm...
 75  // * /ipns/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
 76  // * /bzz/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
 77  func CidEncoderFromPath(p string) (cidenc.Encoder, error) {
 78  	components := strings.SplitN(p, "/", 4)
 79  
 80  	var maybeCid string
 81  	if components[0] != "" {
 82  		// No leading slash, first component is likely CID-like.
 83  		maybeCid = components[0]
 84  	} else if len(components) < 3 {
 85  		// Not enough components to include a CID.
 86  		return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
 87  	} else {
 88  		maybeCid = components[2]
 89  	}
 90  	c, err := cid.Decode(maybeCid)
 91  	if err != nil {
 92  		// Ok, not a CID-like thing. Keep the current encoder.
 93  		return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
 94  	}
 95  	if c.Version() == 0 {
 96  		// Version 0, use the base58 non-upgrading encoder.
 97  		return cidenc.Default(), nil
 98  	}
 99  
100  	// Version 1+, extract multibase encoding.
101  	encoding, _, err := mbase.Decode(maybeCid)
102  	if err != nil {
103  		// This should be impossible, we've already decoded the cid.
104  		panic(fmt.Sprintf("BUG: failed to get multibase decoder for CID %s", maybeCid))
105  	}
106  
107  	return cidenc.Encoder{Base: mbase.MustNewEncoder(encoding), Upgrade: true}, nil
108  }