master
go 162 lines 4.73 KB
Raw
1 package commands
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7 "strings"
8 "time"
9
10 ns "github.com/ipfs/boxo/namesys"
11 cidenc "github.com/ipfs/go-cidutil/cidenc"
12 cmdenv "github.com/ipfs/kubo/core/commands/cmdenv"
13 "github.com/ipfs/kubo/core/commands/cmdutils"
14 ncmd "github.com/ipfs/kubo/core/commands/name"
15
16 "github.com/ipfs/boxo/path"
17 cmds "github.com/ipfs/go-ipfs-cmds"
18 options "github.com/ipfs/kubo/core/coreiface/options"
19 )
20
21 const (
22 resolveRecursiveOptionName = "recursive"
23 resolveDhtRecordCountOptionName = "dht-record-count"
24 resolveDhtTimeoutOptionName = "dht-timeout"
25 )
26
27 var ResolveCmd = &cmds.Command{
28 Helptext: cmds.HelpText{
29 Tagline: "Resolve the value of names to IPFS.",
30 ShortDescription: `
31 There are a number of mutable name protocols that can link among
32 themselves and into IPNS. This command accepts any of these
33 identifiers and resolves them to the referenced item.
34 `,
35 LongDescription: `
36 There are a number of mutable name protocols that can link among
37 themselves and into IPNS. For example IPNS references can (currently)
38 point at an IPFS object, and DNS links can point at other DNS links, IPNS
39 entries, or IPFS objects. This command accepts any of these
40 identifiers and resolves them to the referenced item.
41
42 EXAMPLES
43
44 Resolve the value of your identity:
45
46 $ ipfs resolve /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
47 /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
48
49 Resolve the value of another name:
50
51 $ ipfs resolve /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
52 /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
53
54 Resolve the value of another name recursively:
55
56 $ ipfs resolve -r /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
57 /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
58
59 Resolve the value of an IPFS DAG path:
60
61 $ ipfs resolve /ipfs/QmeZy1fGbwgVSrqbfh9fKQrAWgeyRnj7h8fsHS1oy3k99x/beep/boop
62 /ipfs/QmYRMjyvAiHKN9UTi8Bzt1HUspmSRD8T8DwxfSMzLgBon1
63
64 `,
65 },
66
67 Arguments: []cmds.Argument{
68 cmds.StringArg("name", true, false, "The name to resolve.").EnableStdin(),
69 },
70 Options: []cmds.Option{
71 cmds.BoolOption(resolveRecursiveOptionName, "r", "Resolve until the result is an IPFS name.").WithDefault(true),
72 cmds.IntOption(resolveDhtRecordCountOptionName, "dhtrc", "Number of records to request for DHT resolution."),
73 cmds.StringOption(resolveDhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution e.g. \"30s\". Pass 0 for no timeout."),
74 },
75 Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
76 api, err := cmdenv.GetApi(env, req)
77 if err != nil {
78 return err
79 }
80
81 name := req.Arguments[0]
82 recursive, _ := req.Options[resolveRecursiveOptionName].(bool)
83
84 // the case when ipns is resolved step by step
85 if strings.HasPrefix(name, "/ipns/") && !recursive {
86 rc, rcok := req.Options[resolveDhtRecordCountOptionName].(uint)
87 dhtt, dhttok := req.Options[resolveDhtTimeoutOptionName].(string)
88 ropts := []options.NameResolveOption{
89 options.Name.ResolveOption(ns.ResolveWithDepth(1)),
90 }
91
92 if rcok {
93 ropts = append(ropts, options.Name.ResolveOption(ns.ResolveWithDhtRecordCount(rc)))
94 }
95 if dhttok {
96 d, err := time.ParseDuration(dhtt)
97 if err != nil {
98 return err
99 }
100 if d < 0 {
101 return errors.New("DHT timeout value must be >= 0")
102 }
103 ropts = append(ropts, options.Name.ResolveOption(ns.ResolveWithDhtTimeout(d)))
104 }
105 p, err := api.Name().Resolve(req.Context, name, ropts...)
106 // ErrResolveRecursion is fine
107 if err != nil && err != ns.ErrResolveRecursion {
108 return err
109 }
110 return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: p.String()})
111 }
112
113 var enc cidenc.Encoder
114 switch {
115 case !cmdenv.CidBaseDefined(req) && !strings.HasPrefix(name, "/ipns/"):
116 // Not specified, check the path.
117 enc, err = cmdenv.CidEncoderFromPath(name)
118 if err == nil {
119 break
120 }
121 // Nope, fallback on the default.
122 fallthrough
123 default:
124 enc, err = cmdenv.GetCidEncoder(req)
125 if err != nil {
126 return err
127 }
128 }
129
130 p, err := cmdutils.PathOrCidPath(name)
131 if err != nil {
132 return err
133 }
134
135 // else, ipfs path or ipns with recursive flag
136 rp, remainder, err := api.ResolvePath(req.Context, p)
137 if err != nil {
138 return err
139 }
140
141 // Trick to encode path with correct encoding.
142 encodedPath := "/" + rp.Namespace() + "/" + enc.Encode(rp.RootCid())
143 if len(remainder) != 0 {
144 encodedPath += path.SegmentsToString(remainder...)
145 }
146
147 // Ensure valid and sanitized.
148 ep, err := path.NewPath(encodedPath)
149 if err != nil {
150 return err
151 }
152
153 return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ep.String()})
154 },
155 Encoders: cmds.EncoderMap{
156 cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, rp *ncmd.ResolvedPath) error {
157 fmt.Fprintln(w, rp.Path)
158 return nil
159 }),
160 },
161 Type: ncmd.ResolvedPath{},
162 }