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 }