| 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 for CIDs in output. CIDv0 is automatically converted to CIDv1 when a base other than base58btc is specified.") |
| 15 | |
| 16 | // OptionUpgradeCidV0InOutput is deprecated. When --cid-base is set to |
| 17 | // anything other than base58btc, CIDv0 are now automatically upgraded |
| 18 | // to CIDv1. This flag is kept for backward compatibility and will be |
| 19 | // removed in a future release. |
| 20 | OptionUpgradeCidV0InOutput = cmds.BoolOption("upgrade-cidv0-in-output", "[DEPRECATED] Upgrade version 0 to version 1 CIDs in output.") |
| 21 | ) |
| 22 | |
| 23 | // GetCidEncoder processes the --cid-base option and returns an encoder. |
| 24 | // When --cid-base is set to a non-base58btc encoding, CIDv0 values are |
| 25 | // automatically upgraded to CIDv1 because CIDv0 can only be represented |
| 26 | // in base58btc. |
| 27 | func GetCidEncoder(req *cmds.Request) (cidenc.Encoder, error) { |
| 28 | base, _ := req.Options[OptionCidBase.Name()].(string) |
| 29 | upgrade, upgradeDefined := req.Options[OptionUpgradeCidV0InOutput.Name()].(bool) |
| 30 | |
| 31 | e := cidenc.Default() |
| 32 | |
| 33 | if base != "" { |
| 34 | var err error |
| 35 | e.Base, err = mbase.EncoderByName(base) |
| 36 | if err != nil { |
| 37 | return e, err |
| 38 | } |
| 39 | // CIDv0 can only be represented in base58btc. When any other |
| 40 | // base is requested, always upgrade CIDv0 to CIDv1 so the |
| 41 | // output actually uses the requested encoding. |
| 42 | if e.Base.Encoding() != mbase.Base58BTC { |
| 43 | e.Upgrade = true |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // Deprecated: --upgrade-cidv0-in-output still works as an explicit |
| 48 | // override for backward compatibility. |
| 49 | if upgradeDefined { |
| 50 | e.Upgrade = upgrade |
| 51 | } |
| 52 | |
| 53 | return e, nil |
| 54 | } |
| 55 | |
| 56 | // CidBaseDefined returns true if the `cid-base` option is specified on the |
| 57 | // command line |
| 58 | func CidBaseDefined(req *cmds.Request) bool { |
| 59 | base, _ := req.Options["cid-base"].(string) |
| 60 | return base != "" |
| 61 | } |
| 62 | |
| 63 | // CidEncoderFromPath creates a new encoder that is influenced from the encoded |
| 64 | // Cid in a Path. For CIDv0 the multibase from the base encoder is used and |
| 65 | // automatic upgrades are disabled. For CIDv1 the multibase from the CID is |
| 66 | // used and upgrades are enabled. |
| 67 | // |
| 68 | // This logic is intentionally fuzzy and matches anything of the form |
| 69 | // `CidLike`, `CidLike/...`, or `/namespace/CidLike/...`. |
| 70 | // |
| 71 | // For example: |
| 72 | // |
| 73 | // * Qm... |
| 74 | // * Qm.../... |
| 75 | // * /ipfs/Qm... |
| 76 | // * /ipns/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/... |
| 77 | // * /bzz/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/... |
| 78 | func CidEncoderFromPath(p string) (cidenc.Encoder, error) { |
| 79 | components := strings.SplitN(p, "/", 4) |
| 80 | |
| 81 | var maybeCid string |
| 82 | if components[0] != "" { |
| 83 | // No leading slash, first component is likely CID-like. |
| 84 | maybeCid = components[0] |
| 85 | } else if len(components) < 3 { |
| 86 | // Not enough components to include a CID. |
| 87 | return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p) |
| 88 | } else { |
| 89 | maybeCid = components[2] |
| 90 | } |
| 91 | c, err := cid.Decode(maybeCid) |
| 92 | if err != nil { |
| 93 | // Ok, not a CID-like thing. Keep the current encoder. |
| 94 | return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p) |
| 95 | } |
| 96 | if c.Version() == 0 { |
| 97 | // Version 0, use the base58 non-upgrading encoder. |
| 98 | return cidenc.Default(), nil |
| 99 | } |
| 100 | |
| 101 | // Version 1+, extract multibase encoding. |
| 102 | encoding, _, err := mbase.Decode(maybeCid) |
| 103 | if err != nil { |
| 104 | // This should be impossible, we've already decoded the cid. |
| 105 | panic(fmt.Sprintf("BUG: failed to get multibase decoder for CID %s", maybeCid)) |
| 106 | } |
| 107 | |
| 108 | return cidenc.Encoder{Base: mbase.MustNewEncoder(encoding), Upgrade: true}, nil |
| 109 | } |