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

import (
4
	"bytes"
5 6 7
	"encoding/base64"
	"encoding/json"
	"errors"
8
	"io"
9 10 11 12 13 14 15
	"time"

	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

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

	cmds "github.com/jbenet/go-ipfs/commands"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16
	ic "github.com/jbenet/go-ipfs/p2p/crypto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
17
	"github.com/jbenet/go-ipfs/p2p/peer"
18 19 20 21
	kb "github.com/jbenet/go-ipfs/routing/kbucket"
	u "github.com/jbenet/go-ipfs/util"
)

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 43
		Tagline: "Show IPFS Node ID info",
		ShortDescription: `
Prints out information about the specified peer,
if no peer is specified, prints out local peers info.
`,
44
	},
45 46 47
	Arguments: []cmds.Argument{
		cmds.StringArg("peerid", false, false, "peer.ID of node to look up"),
	},
48 49 50 51 52 53 54
	Run: func(req cmds.Request) (interface{}, error) {
		node, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		if len(req.Arguments()) == 0 {
55
			return printPeer(node.Peerstore, node.Identity)
56 57
		}

58
		pid := req.Arguments()[0]
59 60

		id := peer.ID(b58.Decode(pid))
61 62 63 64
		if len(id) == 0 {
			return nil, cmds.ClientError("Invalid peer id")
		}

65
		ctx, _ := context.WithTimeout(context.TODO(), time.Second*5)
66 67
		// TODO handle offline mode with polymorphism instead of conditionals
		if !node.OnlineMode() {
68 69 70
			return nil, errors.New(offlineIdErrorMessage)
		}

71 72
		p, err := node.Routing.FindPeer(ctx, id)
		if err == kb.ErrLookupFailure {
Jeromy's avatar
Jeromy committed
73
			return nil, errors.New(offlineIdErrorMessage)
74 75 76 77
		}
		if err != nil {
			return nil, err
		}
78
		return printPeer(node.Peerstore, p.ID)
79 80
	},
	Marshalers: cmds.MarshalerMap{
81
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
82 83 84 85 86
			val, ok := res.Output().(*IdOutput)
			if !ok {
				return nil, u.ErrCast()
			}

87 88 89 90 91
			marshaled, err := json.MarshalIndent(val, "", "\t")
			if err != nil {
				return nil, err
			}
			return bytes.NewReader(marshaled), nil
92 93
		},
	},
94
	Type: IdOutput{},
95 96
}

97 98
func printPeer(ps peer.Peerstore, p peer.ID) (interface{}, error) {
	if p == "" {
99 100
		return nil, errors.New("Attempted to print nil peer!")
	}
101

102
	info := new(IdOutput)
103
	info.ID = p.Pretty()
104

105 106
	if pk := ps.PubKey(p); pk != nil {
		pkb, err := ic.MarshalPublicKey(pk)
Jeromy's avatar
Jeromy committed
107 108 109 110
		if err != nil {
			return nil, err
		}
		info.PublicKey = base64.StdEncoding.EncodeToString(pkb)
111
	}
112 113

	for _, a := range ps.Addresses(p) {
114 115 116
		info.Addresses = append(info.Addresses, a.String())
	}

117 118 119 120 121 122 123 124 125 126
	if v, err := ps.Get(p, "ProtocolVersion"); err == nil {
		if vs, ok := v.(string); ok {
			info.AgentVersion = vs
		}
	}
	if v, err := ps.Get(p, "AgentVersion"); err == nil {
		if vs, ok := v.(string); ok {
			info.ProtocolVersion = vs
		}
	}
127 128 129

	return info, nil
}