id.go 4.93 KB
Newer Older
1 2 3
package commands

import (
4
	"bytes"
5 6 7
	"encoding/base64"
	"encoding/json"
	"errors"
8
	"io"
9
	"strings"
10

11
	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
12

13 14 15 16 17 18 19
	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	ic "github.com/ipfs/go-ipfs/p2p/crypto"
	"github.com/ipfs/go-ipfs/p2p/peer"
	identify "github.com/ipfs/go-ipfs/p2p/protocol/identify"
	kb "github.com/ipfs/go-ipfs/routing/kbucket"
	u "github.com/ipfs/go-ipfs/util"
20 21
)

22 23 24 25 26 27
const offlineIdErrorMessage = `ID command fails when run without daemon, we are working to fix this.
In the meantime, please run the daemon if you want to use 'ipfs id':

    ipfs daemon &
    ipfs id QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`
Jeromy's avatar
Jeromy committed
28

29 30 31 32 33 34 35 36
type IdOutput struct {
	ID              string
	PublicKey       string
	Addresses       []string
	AgentVersion    string
	ProtocolVersion string
}

37
var IDCmd = &cmds.Command{
38
	Helptext: cmds.HelpText{
Jeromy's avatar
Jeromy committed
39 40 41 42
		Tagline: "Show IPFS Node ID info",
		ShortDescription: `
Prints out information about the specified peer,
if no peer is specified, prints out local peers info.
Jeromy's avatar
Jeromy committed
43 44 45 46 47 48

ipfs id supports the format option for output with the following keys:
<id> : the peers id
<aver>: agent version
<pver>: protocol version
<pubkey>: public key
49
<addrs>: addresses (newline delimited)
Jeromy's avatar
Jeromy committed
50
`,
51
	},
52
	Arguments: []cmds.Argument{
53
		cmds.StringArg("peerid", false, false, "peer.ID of node to look up").EnableStdin(),
54
	},
55 56 57
	Options: []cmds.Option{
		cmds.StringOption("f", "format", "optional output format"),
	},
58
	Run: func(req cmds.Request, res cmds.Response) {
59 60
		node, err := req.Context().GetNode()
		if err != nil {
61 62
			res.SetError(err, cmds.ErrNormal)
			return
63 64 65
		}

		if len(req.Arguments()) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66
			output, err := printSelf(node)
67 68 69 70 71 72
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
			res.SetOutput(output)
			return
73 74
		}

75
		pid := req.Arguments()[0]
76 77

		id := peer.ID(b58.Decode(pid))
78
		if len(id) == 0 {
79 80
			res.SetError(cmds.ClientError("Invalid peer id"), cmds.ErrClient)
			return
81 82
		}

83 84
		// TODO handle offline mode with polymorphism instead of conditionals
		if !node.OnlineMode() {
85 86
			res.SetError(errors.New(offlineIdErrorMessage), cmds.ErrClient)
			return
87 88
		}

89
		p, err := node.Routing.FindPeer(req.Context().Context, id)
90
		if err == kb.ErrLookupFailure {
91 92
			res.SetError(errors.New(offlineIdErrorMessage), cmds.ErrClient)
			return
93 94
		}
		if err != nil {
95 96
			res.SetError(err, cmds.ErrNormal)
			return
97
		}
98 99 100 101 102 103 104

		output, err := printPeer(node.Peerstore, p.ID)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		res.SetOutput(output)
105 106
	},
	Marshalers: cmds.MarshalerMap{
107
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
108 109 110 111 112
			val, ok := res.Output().(*IdOutput)
			if !ok {
				return nil, u.ErrCast()
			}

113
			format, found, err := res.Request().Option("format").String()
114 115 116
			if err != nil {
				return nil, err
			}
117 118 119 120 121 122
			if found {
				output := format
				output = strings.Replace(output, "<id>", val.ID, -1)
				output = strings.Replace(output, "<aver>", val.AgentVersion, -1)
				output = strings.Replace(output, "<pver>", val.ProtocolVersion, -1)
				output = strings.Replace(output, "<pubkey>", val.PublicKey, -1)
123 124 125
				output = strings.Replace(output, "<addrs>", strings.Join(val.Addresses, "\n"), -1)
				output = strings.Replace(output, "\\n", "\n", -1)
				output = strings.Replace(output, "\\t", "\t", -1)
126 127 128 129 130 131 132 133 134
				return strings.NewReader(output), nil
			} else {

				marshaled, err := json.MarshalIndent(val, "", "\t")
				if err != nil {
					return nil, err
				}
				return bytes.NewReader(marshaled), nil
			}
135 136
		},
	},
137
	Type: IdOutput{},
138 139
}

140 141
func printPeer(ps peer.Peerstore, p peer.ID) (interface{}, error) {
	if p == "" {
142 143
		return nil, errors.New("Attempted to print nil peer!")
	}
144

145
	info := new(IdOutput)
146
	info.ID = p.Pretty()
147

148 149
	if pk := ps.PubKey(p); pk != nil {
		pkb, err := ic.MarshalPublicKey(pk)
Jeromy's avatar
Jeromy committed
150 151 152 153
		if err != nil {
			return nil, err
		}
		info.PublicKey = base64.StdEncoding.EncodeToString(pkb)
154
	}
155

156
	for _, a := range ps.Addrs(p) {
157 158 159
		info.Addresses = append(info.Addresses, a.String())
	}

160 161
	if v, err := ps.Get(p, "ProtocolVersion"); err == nil {
		if vs, ok := v.(string); ok {
162
			info.ProtocolVersion = vs
163 164 165 166
		}
	}
	if v, err := ps.Get(p, "AgentVersion"); err == nil {
		if vs, ok := v.(string); ok {
167
			info.AgentVersion = vs
168 169
		}
	}
170 171 172

	return info, nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

// printing self is special cased as we get values differently.
func printSelf(node *core.IpfsNode) (interface{}, error) {
	info := new(IdOutput)
	info.ID = node.Identity.Pretty()

	if node.PrivateKey == nil {
		if err := node.LoadPrivateKey(); err != nil {
			return nil, err
		}
	}

	pk := node.PrivateKey.GetPublic()
	pkb, err := ic.MarshalPublicKey(pk)
	if err != nil {
		return nil, err
	}
	info.PublicKey = base64.StdEncoding.EncodeToString(pkb)

	if node.PeerHost != nil {
		for _, a := range node.PeerHost.Addrs() {
			s := a.String() + "/ipfs/" + info.ID
			info.Addresses = append(info.Addresses, s)
		}
	}
	info.ProtocolVersion = identify.IpfsVersion
	info.AgentVersion = identify.ClientVersion
	return info, nil
}