1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4//
5package engine
6
7import (
8	"encoding/json"
9	"regexp"
10
11	"github.com/keybase/client/go/libkb"
12	keybase1 "github.com/keybase/client/go/protocol/keybase1"
13)
14
15// SigsList is an engine for the sigs-list command.
16type SigsList struct {
17	SigsListArgs
18
19	user *libkb.User
20	sigs []libkb.TypedChainLink
21	libkb.Contextified
22}
23
24type SigsListArgs struct {
25	Username string
26	Types    map[string]bool
27	Filterx  string
28	Verbose  bool
29	Revoked  bool
30}
31
32// NewSigsList creates a SigsList engine.
33func NewSigsList(g *libkb.GlobalContext, args SigsListArgs) *SigsList {
34	return &SigsList{
35		SigsListArgs: args,
36		Contextified: libkb.NewContextified(g),
37	}
38}
39
40// Name is the unique engine name.
41func (e *SigsList) Name() string {
42	return "SigsList"
43}
44
45// GetPrereqs returns the engine prereqs.
46func (e *SigsList) Prereqs() Prereqs {
47	return Prereqs{}
48}
49
50// RequiredUIs returns the required UIs.
51func (e *SigsList) RequiredUIs() []libkb.UIKind {
52	return []libkb.UIKind{}
53}
54
55// SubConsumers returns the other UI consumers for this engine.
56func (e *SigsList) SubConsumers() []libkb.UIConsumer {
57	return nil
58}
59
60// Run starts the engine.
61func (e *SigsList) Run(m libkb.MetaContext) error {
62	arg := libkb.NewLoadUserArgWithMetaContext(m)
63	if len(e.Username) > 0 {
64		arg = arg.WithName(e.Username)
65	} else {
66		arg = arg.WithSelf(true)
67	}
68
69	var err error
70	e.user, err = libkb.LoadUser(arg)
71	if err != nil {
72		return err
73	}
74
75	e.sigs = e.user.IDTable().Order
76	return e.processSigs()
77}
78
79// Sigs returns the sig list, after processing.
80func (e *SigsList) Sigs() []keybase1.Sig {
81	res := make([]keybase1.Sig, len(e.sigs))
82	for i, s := range e.sigs {
83		var key string
84		fp := s.GetPGPFingerprint()
85		if fp != nil {
86			key = fp.ToDisplayString(e.Verbose)
87		}
88		res[i] = keybase1.Sig{
89			Seqno:        s.GetSeqno(),
90			SigIDDisplay: s.GetSigID().ToDisplayString(e.Verbose),
91			Type:         s.Type(),
92			CTime:        keybase1.ToTime(s.GetCTime()),
93			Revoked:      s.IsRevoked(),
94			Active:       e.isActiveKey(s),
95			Key:          key,
96			Body:         s.ToDisplayString(),
97		}
98	}
99	return res
100}
101
102// ugh
103type sigexp struct {
104	Seqno   keybase1.Seqno `json:"seqno"`
105	SigID   string         `json:"sig_id"`
106	Type    string         `json:"type"`
107	CTime   int64          `json:"ctime"`
108	Revoked bool           `json:"revoked"`
109	Active  bool           `json:"active"`
110	Key     string         `json:"key_fingerprint,omitempty"`
111	Body    string         `json:"statement"`
112}
113
114func (e *SigsList) JSON() (string, error) {
115	exp := make([]sigexp, len(e.sigs))
116	for i, s := range e.sigs {
117		var key string
118		fp := s.GetPGPFingerprint()
119		if fp != nil {
120			key = fp.ToDisplayString(true /* verbose */)
121		}
122		exp[i] = sigexp{
123			Seqno:   s.GetSeqno(),
124			SigID:   s.GetSigID().ToDisplayString(true /* verbose */),
125			Type:    s.Type(),
126			CTime:   s.GetCTime().Unix(),
127			Revoked: s.IsRevoked(),
128			Active:  e.isActiveKey(s),
129			Key:     key,
130			Body:    s.ToDisplayString(),
131		}
132	}
133	j, err := json.MarshalIndent(exp, "", "\t")
134	if err != nil {
135		return "", err
136	}
137	return string(j), nil
138
139}
140
141func (e *SigsList) processSigs() error {
142	if err := e.skipSigs(); err != nil {
143		return err
144	}
145	if err := e.selectSigs(); err != nil {
146		return err
147	}
148	return e.filterRxx()
149}
150
151func (e *SigsList) skipSigs() error {
152	e.filterSigs(func(l libkb.TypedChainLink) bool {
153		return !e.skipLink(l)
154	})
155	return nil
156}
157
158func (e *SigsList) selectSigs() error {
159	if e.Types != nil {
160		e.filterSigs(func(l libkb.TypedChainLink) bool {
161			ok, found := e.Types[l.Type()]
162			return ok && found
163		})
164	}
165	return nil
166}
167
168func (e *SigsList) filterRxx() error {
169	if len(e.Filterx) == 0 {
170		return nil
171	}
172	rxx, err := regexp.Compile(e.Filterx)
173	if err != nil {
174		return err
175	}
176	e.filterSigs(func(l libkb.TypedChainLink) bool {
177		return rxx.MatchString(l.ToDisplayString())
178	})
179	return nil
180}
181
182func (e *SigsList) filterSigs(f func(libkb.TypedChainLink) bool) {
183	var sigs []libkb.TypedChainLink
184	for _, link := range e.sigs {
185		if f(link) {
186			sigs = append(sigs, link)
187		}
188	}
189	e.sigs = sigs
190}
191
192func (e *SigsList) isActiveKey(link libkb.TypedChainLink) bool {
193	return link.IsInCurrentFamily(e.user)
194}
195
196func (e *SigsList) skipLink(link libkb.TypedChainLink) bool {
197	return (!e.Revoked && (link.IsRevoked() || link.IsRevocationIsh()))
198}
199