github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/gregor.go (about)

     1  package service
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"sync"
     9  	"time"
    10  
    11  	"golang.org/x/net/context"
    12  
    13  	"github.com/keybase/backoff"
    14  	"github.com/keybase/client/go/badges"
    15  	"github.com/keybase/client/go/chat"
    16  	"github.com/keybase/client/go/chat/globals"
    17  	chatstorage "github.com/keybase/client/go/chat/storage"
    18  	"github.com/keybase/client/go/chat/utils"
    19  	"github.com/keybase/client/go/engine"
    20  	"github.com/keybase/client/go/gregor"
    21  	grclient "github.com/keybase/client/go/gregor/client"
    22  	"github.com/keybase/client/go/gregor/storage"
    23  	grutils "github.com/keybase/client/go/gregor/utils"
    24  	"github.com/keybase/client/go/libkb"
    25  	"github.com/keybase/client/go/logger"
    26  	"github.com/keybase/client/go/protocol/chat1"
    27  	"github.com/keybase/client/go/protocol/gregor1"
    28  	"github.com/keybase/client/go/protocol/keybase1"
    29  	"github.com/keybase/clockwork"
    30  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    31  	jsonw "github.com/keybase/go-jsonw"
    32  )
    33  
    34  const GregorRequestTimeout time.Duration = 30 * time.Second
    35  const GregorConnectionRetryInterval time.Duration = 2 * time.Second
    36  const GregorGetClientTimeout time.Duration = 4 * time.Second
    37  const slowConnSleepTime = 1 * time.Second
    38  
    39  type IdentifyUIHandler struct {
    40  	libkb.Contextified
    41  	connID      libkb.ConnectionID
    42  	alwaysAlive bool
    43  }
    44  
    45  var _ libkb.GregorInBandMessageHandler = (*IdentifyUIHandler)(nil)
    46  
    47  func NewIdentifyUIHandler(g *libkb.GlobalContext, connID libkb.ConnectionID) IdentifyUIHandler {
    48  	return IdentifyUIHandler{
    49  		Contextified: libkb.NewContextified(g),
    50  		connID:       connID,
    51  		alwaysAlive:  false,
    52  	}
    53  }
    54  
    55  func (h IdentifyUIHandler) IsAlive() bool {
    56  	return (h.alwaysAlive || h.G().ConnectionManager.LookupConnection(h.connID) != nil)
    57  }
    58  
    59  func (h IdentifyUIHandler) Name() string {
    60  	return "IdentifyUIHandler"
    61  }
    62  
    63  func (h *IdentifyUIHandler) toggleAlwaysAlive(alive bool) {
    64  	h.alwaysAlive = alive
    65  }
    66  
    67  type oobmSystemSubscriptions map[string]bool
    68  
    69  func newOOBMSystemSubscriptions(oobmSystems []string) oobmSystemSubscriptions {
    70  	if oobmSystems == nil {
    71  		return nil
    72  	}
    73  	ret := make(oobmSystemSubscriptions)
    74  	for _, system := range oobmSystems {
    75  		ret[system] = true
    76  	}
    77  	return ret
    78  }
    79  
    80  type gregorFirehoseHandler struct {
    81  	libkb.Contextified
    82  	connID     libkb.ConnectionID
    83  	cli        keybase1.GregorUIClient
    84  	oobmFilter oobmSystemSubscriptions
    85  }
    86  
    87  func newGregorFirehoseHandler(g *libkb.GlobalContext, connID libkb.ConnectionID, xp rpc.Transporter, oobmSystems []string) *gregorFirehoseHandler {
    88  	return &gregorFirehoseHandler{
    89  		Contextified: libkb.NewContextified(g),
    90  		connID:       connID,
    91  		cli:          keybase1.GregorUIClient{Cli: rpc.NewClient(xp, libkb.NewContextifiedErrorUnwrapper(g), nil)},
    92  		oobmFilter:   newOOBMSystemSubscriptions(oobmSystems),
    93  	}
    94  }
    95  
    96  func (h *gregorFirehoseHandler) IsAlive() bool {
    97  	return h.G().ConnectionManager.LookupConnection(h.connID) != nil
    98  }
    99  
   100  func (h *gregorFirehoseHandler) PushState(s gregor1.State, r keybase1.PushReason) {
   101  	defer h.G().Trace("gregorFirehoseHandler#PushState", nil)()
   102  	err := h.cli.PushState(context.Background(), keybase1.PushStateArg{State: s, Reason: r})
   103  	if err != nil {
   104  		h.G().Log.Error(fmt.Sprintf("Error in firehose push state: %s", err))
   105  	}
   106  }
   107  
   108  func (h *gregorFirehoseHandler) filterOOBMs(v []gregor1.OutOfBandMessage) []gregor1.OutOfBandMessage {
   109  	// Filter OOBMs down to wanted systems if we have a filter installed
   110  	if h.oobmFilter == nil {
   111  		return v
   112  	}
   113  
   114  	var tmp []gregor1.OutOfBandMessage
   115  	for _, m := range v {
   116  		if h.oobmFilter[m.System().String()] {
   117  			tmp = append(tmp, m)
   118  		}
   119  	}
   120  	return tmp
   121  }
   122  
   123  func (h *gregorFirehoseHandler) PushOutOfBandMessages(v []gregor1.OutOfBandMessage) {
   124  	defer h.G().Trace("gregorFirehoseHandler#PushOutOfBandMessages", nil)()
   125  	nOrig := len(v)
   126  
   127  	// Filter OOBMs down to wanted systems if we have a filter installed
   128  	v = h.filterOOBMs(v)
   129  	h.G().Log.Debug("gregorFirehoseHandler#PushOutOfBandMessages: %d message(s) (%d before filter)", len(v), nOrig)
   130  
   131  	if len(v) == 0 {
   132  		return
   133  	}
   134  
   135  	err := h.cli.PushOutOfBandMessages(context.Background(), v)
   136  	if err != nil {
   137  		h.G().Log.Error(fmt.Sprintf("Error in firehose push out-of-band messages: %s", err))
   138  	}
   139  }
   140  
   141  type testingReplayRes struct {
   142  	replayed []gregor.InBandMessage
   143  	err      error
   144  }
   145  
   146  type testingEvents struct {
   147  	broadcastSentCh chan error
   148  	replayThreadCh  chan testingReplayRes
   149  }
   150  
   151  func newTestingEvents() *testingEvents {
   152  	return &testingEvents{
   153  		broadcastSentCh: make(chan error),
   154  		replayThreadCh:  make(chan testingReplayRes, 10),
   155  	}
   156  }
   157  
   158  type connectionAuthError struct {
   159  	msg         string
   160  	shouldRetry bool
   161  }
   162  
   163  func newConnectionAuthError(msg string, shouldRetry bool) connectionAuthError {
   164  	return connectionAuthError{
   165  		msg:         msg,
   166  		shouldRetry: shouldRetry,
   167  	}
   168  }
   169  
   170  func (c connectionAuthError) ShouldRetry() bool {
   171  	return c.shouldRetry
   172  }
   173  
   174  func (c connectionAuthError) Error() string {
   175  	return fmt.Sprintf("connection auth error: msg: %s shouldRetry: %v", c.msg, c.shouldRetry)
   176  }
   177  
   178  type replayThreadArg struct {
   179  	cli gregor1.IncomingInterface
   180  	t   time.Time
   181  	ctx context.Context
   182  }
   183  
   184  type gregorHandler struct {
   185  	globals.Contextified
   186  
   187  	// This lock is to protect ibmHandlers and gregorCli and firehoseHandlers. Only public methods
   188  	// should grab it.
   189  	sync.Mutex
   190  	ibmHandlers []libkb.GregorInBandMessageHandler
   191  
   192  	// gregorCliMu just protecs the gregorCli pointer, since it can be swapped out
   193  	// in one goroutine and accessed in another.
   194  	gregorCliMu sync.Mutex
   195  	gregorCli   *grclient.Client
   196  
   197  	firehoseHandlers []libkb.GregorFirehoseHandler
   198  	badger           *badges.Badger
   199  	reachability     *reachability
   200  	chatLog          utils.DebugLabeler
   201  
   202  	// This mutex protects the con object
   203  	connMutex sync.Mutex
   204  	conn      *rpc.Connection
   205  	uri       *rpc.FMPURI
   206  
   207  	// connectHappened will be closed after gregor connection established
   208  	connectHappened chan struct{}
   209  
   210  	cli               rpc.GenericClient
   211  	pingCli           rpc.GenericClient
   212  	sessionID         gregor1.SessionID
   213  	firstConnectMu    sync.Mutex
   214  	firstConnect      bool
   215  	forceSessionCheck bool
   216  
   217  	// Function for determining if a new BroadcastMessage should trigger
   218  	// a pushState call to firehose handlers
   219  	pushStateFilter func(m gregor.Message) bool
   220  
   221  	shutdownCh  chan struct{}
   222  	broadcastCh chan gregor1.Message
   223  	replayCh    chan replayThreadArg
   224  	pushStateCh chan struct{}
   225  	forcePingCh chan struct{}
   226  
   227  	// Testing
   228  	testingEvents       *testingEvents
   229  	transportForTesting *connTransport
   230  }
   231  
   232  var _ libkb.GregorState = (*gregorHandler)(nil)
   233  var _ libkb.GregorListener = (*gregorHandler)(nil)
   234  
   235  func newGregorHandler(g *globals.Context) *gregorHandler {
   236  	gh := &gregorHandler{
   237  		Contextified:      globals.NewContextified(g),
   238  		chatLog:           utils.NewDebugLabeler(g.ExternalG(), "PushHandler", false),
   239  		firstConnect:      true,
   240  		pushStateFilter:   func(m gregor.Message) bool { return true },
   241  		badger:            nil,
   242  		broadcastCh:       make(chan gregor1.Message, 10000),
   243  		forceSessionCheck: false,
   244  		connectHappened:   make(chan struct{}),
   245  		replayCh:          make(chan replayThreadArg, 10),
   246  		pushStateCh:       make(chan struct{}, 100),
   247  		forcePingCh:       make(chan struct{}, 5),
   248  	}
   249  	return gh
   250  }
   251  
   252  // Init starts all the background services for managing connection to Gregor
   253  func (g *gregorHandler) Init() {
   254  	// Start broadcast handler goroutine
   255  	go g.broadcastMessageHandler()
   256  	// Start the app state monitor thread
   257  	go g.monitorAppState()
   258  	// Start replay thread
   259  	go g.syncReplayThread()
   260  }
   261  
   262  const (
   263  	monitorConnect int = iota
   264  	monitorDisconnect
   265  	monitorNoop
   266  )
   267  
   268  func (g *gregorHandler) monitorAppState() {
   269  	// Wait for state updates and react accordingly
   270  	state := keybase1.MobileAppState_FOREGROUND
   271  	suspended := false
   272  	for {
   273  		monitorAction := monitorNoop
   274  		select {
   275  		case state = <-g.G().MobileAppState.NextUpdate(&state):
   276  			switch state {
   277  			case keybase1.MobileAppState_FOREGROUND:
   278  				g.forcePing(context.Background())
   279  				monitorAction = monitorConnect
   280  			case keybase1.MobileAppState_BACKGROUNDACTIVE:
   281  				monitorAction = monitorConnect
   282  			case keybase1.MobileAppState_BACKGROUND, keybase1.MobileAppState_INACTIVE:
   283  				monitorAction = monitorDisconnect
   284  			}
   285  		case suspended = <-g.G().DesktopAppState.NextSuspendUpdate(&suspended):
   286  			if !suspended {
   287  				monitorAction = monitorConnect
   288  				g.chatLog.Debug(context.Background(), "resumed, connecting")
   289  			} else {
   290  				g.chatLog.Debug(context.Background(), "suspended, disconnecting")
   291  				monitorAction = monitorDisconnect
   292  			}
   293  		}
   294  		switch monitorAction {
   295  		case monitorConnect:
   296  			// Make sure the URI is set before attempting this (possible it isn't in a race)
   297  			if g.uri != nil {
   298  				g.chatLog.Debug(context.Background(), "foregrounded, reconnecting")
   299  				if err := g.Connect(g.uri); err != nil {
   300  					g.chatLog.Debug(context.Background(), "error reconnecting: %s", err)
   301  				}
   302  			}
   303  		case monitorDisconnect:
   304  			g.chatLog.Debug(context.Background(), "backgrounded, shutting down connection")
   305  			g.Shutdown()
   306  		}
   307  	}
   308  }
   309  
   310  func (g *gregorHandler) GetURI() *rpc.FMPURI {
   311  	return g.uri
   312  }
   313  
   314  func (g *gregorHandler) GetIncomingClient() gregor1.IncomingInterface {
   315  	cli := g.getRPCCli()
   316  	if g.IsShutdown() || cli == nil {
   317  		return gregor1.IncomingClient{Cli: chat.OfflineClient{}}
   318  	}
   319  	return gregor1.IncomingClient{Cli: cli}
   320  }
   321  
   322  func (g *gregorHandler) GetClient() chat1.RemoteInterface {
   323  	cli := g.getRPCCli()
   324  	if g.IsShutdown() || cli == nil {
   325  		select {
   326  		case <-g.connectHappened:
   327  			cli = g.getRPCCli()
   328  			if g.IsShutdown() || cli == nil {
   329  				g.chatLog.Debug(context.Background(), "GetClient: connectHappened, but still shutdown, using OfflineClient for chat1.RemoteClient")
   330  				return chat1.RemoteClient{Cli: chat.OfflineClient{}}
   331  
   332  			}
   333  			g.chatLog.Debug(context.Background(), "GetClient: successfully waited for connection")
   334  			return chat1.RemoteClient{Cli: chat.NewRemoteClient(g.G(), cli)}
   335  		case <-time.After(GregorGetClientTimeout):
   336  			g.chatLog.Debug(context.Background(), "GetClient: shutdown, using OfflineClient for chat1.RemoteClient (waited %s for connectHappened)", GregorGetClientTimeout)
   337  			return chat1.RemoteClient{Cli: chat.OfflineClient{}}
   338  		}
   339  	}
   340  	g.chatLog.Debug(context.Background(), "GetClient: not shutdown, making new remote client")
   341  	return chat1.RemoteClient{Cli: chat.NewRemoteClient(g.G(), cli)}
   342  }
   343  
   344  func (g *gregorHandler) isFirstConnect() bool {
   345  	g.firstConnectMu.Lock()
   346  	defer g.firstConnectMu.Unlock()
   347  	return g.firstConnect
   348  }
   349  
   350  func (g *gregorHandler) setFirstConnect(val bool) {
   351  	g.firstConnectMu.Lock()
   352  	defer g.firstConnectMu.Unlock()
   353  	g.firstConnect = val
   354  }
   355  
   356  func (g *gregorHandler) shutdownGregorClient(ctx context.Context) {
   357  	g.gregorCliMu.Lock()
   358  	gcliOld := g.gregorCli
   359  	g.gregorCli = nil
   360  	g.gregorCliMu.Unlock()
   361  	if gcliOld != nil {
   362  		gcliOld.Stop()
   363  	}
   364  }
   365  
   366  func (g *gregorHandler) resetGregorClient(ctx context.Context, uid gregor1.UID, deviceID gregor1.DeviceID) (gcli *grclient.Client, err error) {
   367  	defer g.G().Trace("gregorHandler#newGregorClient", &err)()
   368  	// Create client object if we are logged in
   369  	if uid != nil && deviceID != nil {
   370  		gcli = grclient.NewClient(uid, deviceID, func() gregor.StateMachine {
   371  			return storage.NewMemEngine(gregor1.ObjFactory{}, clockwork.NewRealClock(), g.G().Log)
   372  		}, storage.NewLocalDB(g.G().ExternalG()), g.GetIncomingClient, g.G().Log, clockwork.NewRealClock())
   373  
   374  		// Bring up local state
   375  		g.Debug(ctx, "restoring state from leveldb")
   376  		if err = gcli.Restore(ctx); err != nil {
   377  			// If this fails, we'll keep trying since the server can bail us out
   378  			g.Debug(ctx, "restore local state failed: %s", err)
   379  		}
   380  	}
   381  	g.gregorCliMu.Lock()
   382  	gcliOld := g.gregorCli
   383  	g.gregorCli = gcli
   384  	g.gregorCliMu.Unlock()
   385  	if gcliOld != nil {
   386  		gcliOld.Stop()
   387  	}
   388  	return gcli, nil
   389  }
   390  
   391  func (g *gregorHandler) getGregorCli() (*grclient.Client, error) {
   392  
   393  	if g == nil {
   394  		return nil, errors.New("gregorHandler client unset")
   395  	}
   396  
   397  	g.gregorCliMu.Lock()
   398  	ret := g.gregorCli
   399  	g.gregorCliMu.Unlock()
   400  
   401  	if ret == nil {
   402  		return nil, errors.New("client unset")
   403  	}
   404  	return ret, nil
   405  }
   406  
   407  func (g *gregorHandler) getRPCCli() rpc.GenericClient {
   408  	g.connMutex.Lock()
   409  	defer g.connMutex.Unlock()
   410  	return g.cli
   411  }
   412  
   413  func (g *gregorHandler) Debug(ctx context.Context, s string, args ...interface{}) {
   414  	g.G().Log.CloneWithAddedDepth(1).CDebugf(ctx, "PushHandler: "+s, args...)
   415  }
   416  
   417  func (g *gregorHandler) Warning(ctx context.Context, s string, args ...interface{}) {
   418  	g.G().Log.CloneWithAddedDepth(1).CWarningf(ctx, "PushHandler: "+s, args...)
   419  }
   420  
   421  func (g *gregorHandler) Errorf(ctx context.Context, s string, args ...interface{}) {
   422  	g.G().Log.CloneWithAddedDepth(1).CErrorf(ctx, "PushHandler: "+s, args...)
   423  }
   424  
   425  func (g *gregorHandler) SetPushStateFilter(f func(m gregor.Message) bool) {
   426  	g.pushStateFilter = f
   427  }
   428  
   429  func (g *gregorHandler) setReachability(r *reachability) {
   430  	g.reachability = r
   431  }
   432  
   433  func (g *gregorHandler) Connect(uri *rpc.FMPURI) (err error) {
   434  
   435  	defer g.G().Trace("gregorHandler#Connect", &err)()
   436  
   437  	g.connMutex.Lock()
   438  	defer g.connMutex.Unlock()
   439  	if g.conn != nil {
   440  		g.chatLog.Debug(context.Background(), "skipping connect, conn is not nil")
   441  		return nil
   442  	}
   443  	defer func() {
   444  		close(g.connectHappened)
   445  		g.connectHappened = make(chan struct{})
   446  	}()
   447  
   448  	// In case we need to interrupt auth'ing or the ping loop,
   449  	// set up this channel.
   450  	g.shutdownCh = make(chan struct{})
   451  	g.uri = uri
   452  	go g.pushStateNewDataDebouncer(g.shutdownCh)
   453  	if uri.UseTLS() {
   454  		err = g.connectTLS()
   455  	} else {
   456  		err = g.connectNoTLS()
   457  	}
   458  
   459  	return err
   460  }
   461  
   462  func (g *gregorHandler) HandlerName() string {
   463  	return "gregor"
   464  }
   465  
   466  // PushHandler adds a new ibm handler to our list. This is usually triggered
   467  // when an external entity (like Electron) connects to the service, and we can
   468  // safely send Gregor information to it
   469  func (g *gregorHandler) PushHandler(handler libkb.GregorInBandMessageHandler) {
   470  	defer g.chatLog.Trace(context.Background(), nil, "PushHandler")()
   471  
   472  	g.G().Log.Debug("pushing inband handler %s to position %d", handler.Name(), len(g.ibmHandlers))
   473  
   474  	g.Lock()
   475  	g.ibmHandlers = append(g.ibmHandlers, handler)
   476  	g.Unlock()
   477  
   478  	// Only try replaying if we are logged in, it's possible that a handler can
   479  	// attach before that is true (like if we start the service logged out and
   480  	// Electron connects)
   481  	cli := g.getRPCCli()
   482  	if g.IsConnected() && cli != nil {
   483  		if _, err := g.replayInBandMessages(context.TODO(), gregor1.IncomingClient{Cli: cli},
   484  			time.Time{}, handler); err != nil {
   485  			g.Errorf(context.Background(), "replayInBandMessages on PushHandler failed: %s", err)
   486  		}
   487  
   488  		if g.badger != nil {
   489  			s, err := g.getState(context.Background())
   490  			if err != nil {
   491  				g.Warning(context.Background(), "Cannot get state in PushHandler: %s", err)
   492  				return
   493  			}
   494  			g.badger.PushState(context.Background(), s)
   495  		}
   496  	}
   497  }
   498  
   499  // PushFirehoseHandler pushes a new firehose handler onto the list of currently
   500  // active firehose handles. We can have several of these active at once. All
   501  // get the "firehose" of gregor events. They're removed lazily as their underlying
   502  // connections die.
   503  func (g *gregorHandler) PushFirehoseHandler(handler libkb.GregorFirehoseHandler) {
   504  	defer g.chatLog.Trace(context.Background(), nil, "PushFirehoseHandler")()
   505  	g.Lock()
   506  	g.firehoseHandlers = append(g.firehoseHandlers, handler)
   507  	g.Unlock()
   508  
   509  	s, err := g.getState(context.Background())
   510  	if err != nil {
   511  		g.Warning(context.Background(), "Cannot push state in firehose handler: %s", err)
   512  		return
   513  	}
   514  	g.Debug(context.Background(), "PushFirehoseHandler: pushing state with %d items", len(s.Items_))
   515  	handler.PushState(s, keybase1.PushReason_RECONNECTED)
   516  }
   517  
   518  // iterateOverFirehoseHandlers applies the function f to all live firehose handlers
   519  // and then resets the list to only include the live ones.
   520  func (g *gregorHandler) iterateOverFirehoseHandlers(f func(h libkb.GregorFirehoseHandler)) {
   521  	var freshHandlers []libkb.GregorFirehoseHandler
   522  	for _, h := range g.firehoseHandlers {
   523  		if h.IsAlive() {
   524  			f(h)
   525  			freshHandlers = append(freshHandlers, h)
   526  		}
   527  	}
   528  	g.firehoseHandlers = freshHandlers
   529  }
   530  
   531  func (g *gregorHandler) pushStateNewDataDebouncer(shutdownCh chan struct{}) {
   532  	shouldSend := false
   533  	var lastTime time.Time
   534  	dur := time.Second
   535  	trigger := func() {
   536  		if shouldSend {
   537  			go g.pushStateOnce(keybase1.PushReason_NEW_DATA)
   538  			shouldSend = false
   539  			lastTime = time.Now()
   540  		}
   541  	}
   542  	for {
   543  		select {
   544  		case <-g.pushStateCh:
   545  			shouldSend = true
   546  			if time.Since(lastTime) > dur {
   547  				trigger()
   548  			}
   549  		case <-time.After(dur):
   550  			trigger()
   551  		case <-shutdownCh:
   552  			return
   553  		}
   554  	}
   555  }
   556  
   557  func (g *gregorHandler) pushStateOnce(r keybase1.PushReason) {
   558  	s, err := g.getState(context.Background())
   559  	if err != nil {
   560  		g.Warning(context.Background(), "Cannot push state in firehose handler: %s", err)
   561  		return
   562  	}
   563  	g.iterateOverFirehoseHandlers(func(h libkb.GregorFirehoseHandler) {
   564  		g.Debug(context.Background(), "pushState: pushing state with %d items", len(s.Items_))
   565  		h.PushState(s, r)
   566  	})
   567  	// Only send this state update on reception of new data, not a reconnect since we will
   568  	// be sending that on a different code path altogether (see OnConnect).
   569  	if g.badger != nil && r != keybase1.PushReason_RECONNECTED {
   570  		g.badger.PushState(context.Background(), s)
   571  	}
   572  }
   573  
   574  func (g *gregorHandler) pushState(r keybase1.PushReason) {
   575  	switch r {
   576  	case keybase1.PushReason_RECONNECTED, keybase1.PushReason_NONE:
   577  		g.pushStateOnce(r)
   578  	default:
   579  		g.pushStateCh <- struct{}{}
   580  	}
   581  }
   582  
   583  func (g *gregorHandler) pushOutOfBandMessages(m []gregor1.OutOfBandMessage) {
   584  	g.iterateOverFirehoseHandlers(func(h libkb.GregorFirehoseHandler) { h.PushOutOfBandMessages(m) })
   585  }
   586  
   587  // replayInBandMessages will replay all the messages in the current state from
   588  // the given time. If a handler is specified, it will only replay using it,
   589  // otherwise it will try all of them. gregorHandler needs to be locked when calling
   590  // this function.
   591  func (g *gregorHandler) replayInBandMessages(ctx context.Context, cli gregor1.IncomingInterface,
   592  	t time.Time, handler libkb.GregorInBandMessageHandler) ([]gregor.InBandMessage, error) {
   593  
   594  	var msgs []gregor.InBandMessage
   595  	var err error
   596  
   597  	gcli, err := g.getGregorCli()
   598  	if err != nil {
   599  		return nil, err
   600  	}
   601  
   602  	if t.IsZero() {
   603  		g.Debug(ctx, "replayInBandMessages: fresh replay: using state items")
   604  		state, err := gcli.StateMachineState(ctx, nil, true)
   605  		if err != nil {
   606  			g.Debug(ctx, "replayInBandMessages: unable to fetch state for replay: %s", err)
   607  			return nil, err
   608  		}
   609  		if msgs, err = gcli.InBandMessagesFromState(state); err != nil {
   610  			g.Debug(ctx, "replayInBandMessages: unable to fetch messages from state for replay: %s", err)
   611  			return nil, err
   612  		}
   613  	} else {
   614  		g.Debug(ctx, "replayInBandMessages: incremental replay: using ibms since")
   615  		if msgs, err = gcli.StateMachineInBandMessagesSince(ctx, t, true); err != nil {
   616  			g.Debug(ctx, "replayInBandMessages: unable to fetch messages for replay: %s", err)
   617  			return nil, err
   618  		}
   619  	}
   620  
   621  	g.Debug(ctx, "replayInBandMessages: replaying %d messages", len(msgs))
   622  	for _, msg := range msgs {
   623  		g.Debug(ctx, "replayInBandMessages: replaying: %s", msg.Metadata().MsgID())
   624  		// If we have a handler, just run it on that, otherwise run it against
   625  		// all of the handlers we know about
   626  		if handler == nil {
   627  			err = g.handleInBandMessage(ctx, cli, msg)
   628  		} else {
   629  			_, err = g.handleInBandMessageWithHandler(ctx, cli, msg, handler)
   630  		}
   631  
   632  		// If an error happens when replaying, don't kill everything else that
   633  		// follows, just make a warning.
   634  		if err != nil {
   635  			g.Debug(ctx, "replayInBandMessages: failure in message replay: %s", err.Error())
   636  			err = nil
   637  		}
   638  	}
   639  
   640  	return msgs, nil
   641  }
   642  
   643  func (g *gregorHandler) IsShutdown() bool {
   644  	g.connMutex.Lock()
   645  	defer g.connMutex.Unlock()
   646  	return g.conn == nil
   647  }
   648  
   649  func (g *gregorHandler) IsConnected() bool {
   650  	g.connMutex.Lock()
   651  	defer g.connMutex.Unlock()
   652  	return g.conn != nil && g.conn.IsConnected()
   653  }
   654  
   655  func (g *gregorHandler) syncReplayThread() {
   656  	for rarg := range g.replayCh {
   657  		var trr testingReplayRes
   658  		now := time.Now()
   659  		g.Debug(rarg.ctx, "serverSync: starting replay thread")
   660  		replayedMsgs, err := g.replayInBandMessages(rarg.ctx, rarg.cli, rarg.t, nil)
   661  		if err != nil {
   662  			g.Debug(rarg.ctx, "serverSync: replayThread: replay messages failed: %s", err)
   663  			trr.err = err
   664  		} else {
   665  			g.Debug(rarg.ctx, "serverSync: replayThread: replayed %d messages", len(replayedMsgs))
   666  			trr.replayed = replayedMsgs
   667  		}
   668  		if g.testingEvents != nil {
   669  			g.testingEvents.replayThreadCh <- trr
   670  		}
   671  		g.Debug(rarg.ctx, "serverSync: syncReplayThread complete: %v", time.Since(now))
   672  	}
   673  }
   674  
   675  // serverSync is called from
   676  // gregord. This can happen either on initial startup, or after a reconnect. Needs
   677  // to be called with gregorHandler locked.
   678  func (g *gregorHandler) serverSync(ctx context.Context,
   679  	cli gregor1.IncomingInterface, gcli *grclient.Client, syncRes *chat1.SyncAllNotificationRes) (res []gregor.InBandMessage, err error) {
   680  	defer g.chatLog.Trace(ctx, &err, "serverSync")()
   681  
   682  	// Get time of the last message we synced (unless this is our first time syncing)
   683  	var t time.Time
   684  	if !g.isFirstConnect() {
   685  		pt := gcli.StateMachineLatestCTime(ctx)
   686  		if pt != nil {
   687  			t = *pt
   688  		}
   689  		g.Debug(ctx, "serverSync: starting replay from: %s", t)
   690  	} else {
   691  		g.Debug(ctx, "serverSync: performing a fresh replay")
   692  	}
   693  
   694  	// Sync down everything from the server
   695  	consumedMsgs, err := gcli.Sync(ctx, cli, syncRes)
   696  	if err != nil {
   697  		g.Debug(ctx, "serverSync: error syncing from the server, reason: %s", err)
   698  		return nil, err
   699  	}
   700  	g.Debug(ctx, "serverSync: consumed %d messages", len(consumedMsgs))
   701  
   702  	// Schedule replay of in-band messages
   703  	g.replayCh <- replayThreadArg{
   704  		cli: cli,
   705  		t:   t,
   706  		ctx: globals.BackgroundChatCtx(ctx, g.G()),
   707  	}
   708  
   709  	g.pushState(keybase1.PushReason_RECONNECTED)
   710  	return consumedMsgs, nil
   711  }
   712  
   713  func (g *gregorHandler) makeReconnectOobm() gregor1.Message {
   714  	return gregor1.Message{
   715  		Oobm_: &gregor1.OutOfBandMessage{
   716  			System_: "internal.reconnect",
   717  		},
   718  	}
   719  }
   720  
   721  func (g *gregorHandler) authParams(ctx context.Context) (uid gregor1.UID, deviceID gregor1.DeviceID,
   722  	token gregor1.SessionToken, nist *libkb.NIST, err error) {
   723  	var res loggedInRes
   724  	var stoken string
   725  	var kuid keybase1.UID
   726  	var kdid keybase1.DeviceID
   727  	if kuid, kdid, stoken, nist, res = g.loggedIn(ctx); res != loggedInYes {
   728  		return uid, deviceID, token, nil,
   729  			newConnectionAuthError("failed to check logged in status", res == loggedInMaybe)
   730  	}
   731  	deviceID = make([]byte, libkb.DeviceIDLen)
   732  	if err := kdid.ToBytes(deviceID); err != nil {
   733  		return uid, deviceID, token, nil, err
   734  	}
   735  	g.chatLog.Debug(ctx, "generated NIST for UID %s", kuid)
   736  	return kuid.ToBytes(), deviceID, gregor1.SessionToken(stoken), nist, nil
   737  }
   738  
   739  func (g *gregorHandler) inboxParams(ctx context.Context, uid gregor1.UID) chat1.InboxVers {
   740  	// Grab current on disk version
   741  	ibox := chatstorage.NewInbox(g.G())
   742  	vers, err := ibox.Version(ctx, uid)
   743  	if err != nil {
   744  		g.chatLog.Debug(ctx, "inboxParams: failed to get current inbox version (using 0): %s",
   745  			err.Error())
   746  		vers = chat1.InboxVers(0)
   747  	}
   748  	return vers
   749  }
   750  
   751  func (g *gregorHandler) notificationParams(ctx context.Context, gcli *grclient.Client) (t gregor1.Time) {
   752  	pt := gcli.StateMachineLatestCTime(ctx)
   753  	if pt != nil {
   754  		t = gregor1.ToTime(*pt)
   755  	}
   756  	g.chatLog.Debug(ctx, "notificationParams: latest ctime: %v", t.Time())
   757  	return t
   758  }
   759  
   760  // OnConnect is called by the rpc library to indicate we have connected to
   761  // gregord
   762  func (g *gregorHandler) OnConnect(ctx context.Context, conn *rpc.Connection,
   763  	cli rpc.GenericClient, srv *rpc.Server) (err error) {
   764  
   765  	ctx = libkb.WithLogTag(ctx, "GRGRONCONN")
   766  
   767  	defer g.chatLog.Trace(ctx, &err, "OnConnect")()
   768  
   769  	// If we get a random OnConnect on some other connection that is not g.conn, then
   770  	// just reject it.
   771  	g.connMutex.Lock()
   772  	if conn != g.conn {
   773  		g.connMutex.Unlock()
   774  		g.chatLog.Debug(ctx, "aborting on dup connection")
   775  		return chat.ErrDuplicateConnection
   776  	}
   777  	g.connMutex.Unlock()
   778  
   779  	g.chatLog.Debug(ctx, "connected")
   780  	timeoutCli := WrapGenericClientWithTimeout(cli, GregorRequestTimeout, chat.ErrChatServerTimeout)
   781  	chatCli := chat1.RemoteClient{Cli: chat.NewRemoteClient(g.G(), cli)}
   782  	if err := srv.Register(gregor1.OutgoingProtocol(g)); err != nil {
   783  		return fmt.Errorf("error registering protocol: %s", err)
   784  	}
   785  
   786  	uid, deviceID, token, nist, err := g.authParams(ctx)
   787  	if err != nil {
   788  		return err
   789  	}
   790  	gcli, err := g.resetGregorClient(ctx, uid, deviceID)
   791  	if err != nil {
   792  		return fmt.Errorf("failed to get gregor client: %s", err)
   793  	}
   794  	iboxVers := g.inboxParams(ctx, uid)
   795  	latestCtime := g.notificationParams(ctx, gcli)
   796  
   797  	// Run SyncAll to both authenticate, and grab all the data we will need to run the
   798  	// various resync procedures for chat and notifications
   799  	var identBreaks []keybase1.TLFIdentifyFailure
   800  	ctx = globals.ChatCtx(ctx, g.G(), keybase1.TLFIdentifyBehavior_CHAT_GUI, &identBreaks,
   801  		chat.NewCachingIdentifyNotifier(g.G()))
   802  	g.chatLog.Debug(ctx, "OnConnect begin")
   803  	syncAllRes, err := chatCli.SyncAll(ctx, chat1.SyncAllArg{
   804  		Uid:              uid,
   805  		DeviceID:         deviceID,
   806  		Session:          token,
   807  		InboxVers:        iboxVers,
   808  		Ctime:            latestCtime,
   809  		Fresh:            g.isFirstConnect(),
   810  		ProtVers:         chat1.SyncAllProtVers_V1,
   811  		HostName:         g.GetURI().Host,
   812  		SummarizeMaxMsgs: true,
   813  		ParticipantsMode: chat1.InboxParticipantsMode_SKIP_TEAMS,
   814  	})
   815  	if err != nil {
   816  		// This will cause us to try and refresh session on the next attempt
   817  		if _, ok := err.(libkb.BadSessionError); ok {
   818  			g.chatLog.Debug(ctx, "bad session from SyncAll(): forcing session check on next attempt")
   819  			g.forceSessionCheck = true
   820  			nist.MarkFailure()
   821  		}
   822  		return fmt.Errorf("error running SyncAll: %s", err)
   823  	}
   824  
   825  	// Use the client parameter instead of conn.GetClient(), since we can get stuck
   826  	// in a recursive loop if we keep retrying on reconnect.
   827  	if err := g.auth(ctx, timeoutCli, &syncAllRes.Auth); err != nil {
   828  		return fmt.Errorf("error authenticating: %s", err)
   829  	}
   830  
   831  	// Update badging for chat.
   832  	// This happens before Syncer.Connected for a reason.
   833  	// If the new inbox version (e.g. 8) were committed to disk and then the
   834  	// app lost connection and bailed out of OnConnect before applying the
   835  	// badging update (7->8) then on reconnect an incomplete chat badge update (8->9)
   836  	// could be received.
   837  	// See: https://github.com/keybase/client/pull/12651
   838  	if g.badger != nil {
   839  		g.badger.PushChatFullUpdate(ctx, syncAllRes.Badge)
   840  	}
   841  
   842  	// Sync chat data using a Syncer object
   843  	// This commits the new inbox version to persistent storage.
   844  	if err := g.G().Syncer.Connected(ctx, chatCli, uid, &syncAllRes.Chat); err != nil {
   845  		return fmt.Errorf("error running chat sync: %s", err)
   846  	}
   847  
   848  	// Sync down events since we have been dead
   849  	if _, err := g.serverSync(ctx, gregor1.IncomingClient{Cli: timeoutCli}, gcli,
   850  		&syncAllRes.Notification); err != nil {
   851  		g.chatLog.Debug(ctx, "serverSync: failure: %s", err)
   852  		return fmt.Errorf("error running state sync: %s", err)
   853  	}
   854  
   855  	// Update badging from gregor.
   856  	if g.badger != nil {
   857  		state, err := gcli.StateMachineState(ctx, nil, false)
   858  		if err != nil {
   859  			g.chatLog.Debug(ctx, "unable to get gregor state for badging: %v", err)
   860  			g.badger.PushState(ctx, gregor1.State{})
   861  		} else {
   862  			g.badger.PushState(ctx, state)
   863  		}
   864  	}
   865  
   866  	// Call out to reachability module if we have one
   867  	if g.reachability != nil {
   868  		g.chatLog.Debug(ctx, "setting reachability")
   869  		g.reachability.setReachability(keybase1.Reachability{
   870  			Reachable: keybase1.Reachable_YES,
   871  		})
   872  	}
   873  
   874  	// Broadcast reconnect oobm. Spawn this off into a goroutine so that we don't delay
   875  	// reconnection any longer than we have to.
   876  	g.chatLog.Debug(ctx, "broadcasting reconnect oobm")
   877  	go func(m gregor1.Message) {
   878  		ctx := context.Background()
   879  		err := g.BroadcastMessage(ctx, m)
   880  		if err != nil {
   881  			g.chatLog.Debug(ctx, "Gregor broadcast error: %+v", err)
   882  		}
   883  	}(g.makeReconnectOobm())
   884  
   885  	// No longer first connect if we are now connected
   886  	g.chatLog.Debug(ctx, "setting first connect to false")
   887  	g.setFirstConnect(false)
   888  	// On successful login we can reset this guy to not force a check
   889  	g.forceSessionCheck = false
   890  	g.chatLog.Debug(ctx, "OnConnect complete")
   891  
   892  	return nil
   893  }
   894  
   895  func (g *gregorHandler) OnConnectError(err error, reconnectThrottleDuration time.Duration) {
   896  	defer g.chatLog.Trace(context.Background(), nil, "OnConnectError")()
   897  	g.chatLog.Debug(context.Background(), "OnConnectError: err: %s, reconnect throttle duration: %s", err,
   898  		reconnectThrottleDuration)
   899  
   900  	// Check reachability here to see the nature of our offline status
   901  	go func() {
   902  		if g.reachability != nil && !g.isReachable() {
   903  			g.reachability.setReachability(keybase1.Reachability{
   904  				Reachable: keybase1.Reachable_NO,
   905  			})
   906  		}
   907  	}()
   908  }
   909  
   910  func (g *gregorHandler) OnDisconnected(ctx context.Context, status rpc.DisconnectStatus) {
   911  	g.chatLog.Debug(context.Background(), "disconnected: %v", status)
   912  
   913  	// Alert chat syncer that we are now disconnected
   914  	g.G().Syncer.Disconnected(ctx)
   915  
   916  	// Call out to reachability module if we have one (and we are currently connected)
   917  	go func() {
   918  		if g.reachability != nil && status != rpc.StartingFirstConnection && !g.isReachable() {
   919  			g.reachability.setReachability(keybase1.Reachability{
   920  				Reachable: keybase1.Reachable_NO,
   921  			})
   922  		}
   923  	}()
   924  }
   925  
   926  func (g *gregorHandler) OnDoCommandError(err error, nextTime time.Duration) {
   927  	g.chatLog.Debug(context.Background(), "do command error: %s, nextTime: %s", err, nextTime)
   928  }
   929  
   930  func (g *gregorHandler) ShouldRetry(name string, err error) bool {
   931  	g.chatLog.Debug(context.Background(), "should retry: name %s, err %v (returning false)", name, err)
   932  	return false
   933  }
   934  
   935  func (g *gregorHandler) ShouldRetryOnConnect(err error) bool {
   936  	if err == nil {
   937  		return false
   938  	}
   939  
   940  	ctx := context.Background()
   941  	g.chatLog.Debug(ctx, "should retry on connect, err %v", err)
   942  	if err == chat.ErrDuplicateConnection {
   943  		g.chatLog.Debug(ctx, "duplicate connection error, not retrying")
   944  		return false
   945  	}
   946  	if _, ok := err.(libkb.BadSessionError); ok {
   947  		g.chatLog.Debug(ctx, "bad session error, not retrying")
   948  		return false
   949  	}
   950  	if cerr, ok := err.(connectionAuthError); ok && !cerr.ShouldRetry() {
   951  		g.chatLog.Debug(ctx, "should retry on connect, non-retry error, ending: %s", err.Error())
   952  		return false
   953  	}
   954  
   955  	return true
   956  }
   957  
   958  func (g *gregorHandler) broadcastMessageOnce(ctx context.Context, m gregor1.Message) (err error) {
   959  	defer g.chatLog.Trace(ctx, &err, "broadcastMessageOnce")()
   960  
   961  	// Handle the message
   962  	var obm gregor.OutOfBandMessage
   963  	ibm := m.ToInBandMessage()
   964  	if ibm != nil {
   965  		gcli, err := g.getGregorCli()
   966  		if err != nil {
   967  			g.Debug(ctx, "BroadcastMessage: failed to get Gregor client: %s", err.Error())
   968  			return err
   969  		}
   970  		// Check to see if this is already in our state
   971  		msgID := ibm.Metadata().MsgID()
   972  		state, err := gcli.StateMachineState(ctx, nil, false)
   973  		if err != nil {
   974  			g.Debug(ctx, "BroadcastMessage: no state machine available: %s", err.Error())
   975  			return err
   976  		}
   977  		if _, ok := state.GetItem(msgID); ok {
   978  			g.Debug(ctx, "BroadcastMessage: msgID: %s already in state, ignoring", msgID)
   979  			return errors.New("ignored repeat message")
   980  		}
   981  
   982  		g.Debug(ctx, "broadcast: in-band message: msgID: %s Ctime: %s", msgID, ibm.Metadata().CTime())
   983  		err = g.handleInBandMessage(ctx, g.GetIncomingClient(), ibm)
   984  
   985  		// Send message to local state machine
   986  		consumeErr := gcli.StateMachineConsumeMessage(ctx, m)
   987  		if consumeErr != nil {
   988  			g.Debug(ctx, "broadcast: error consuming message: %+v", consumeErr)
   989  		}
   990  
   991  		// Forward to electron or whichever UI is listening for the new gregor state
   992  		if g.pushStateFilter(m) {
   993  			g.pushState(keybase1.PushReason_NEW_DATA)
   994  		}
   995  
   996  		return err
   997  	}
   998  
   999  	obm = m.ToOutOfBandMessage()
  1000  	if obm != nil {
  1001  		g.Debug(ctx, "broadcast: out-of-band message: uid: %s",
  1002  			m.ToOutOfBandMessage().UID())
  1003  		if err := g.handleOutOfBandMessage(ctx, obm); err != nil {
  1004  			g.Debug(ctx, "BroadcastMessage: error handling oobm: %s", err.Error())
  1005  			return err
  1006  		}
  1007  		return nil
  1008  	}
  1009  
  1010  	g.Debug(ctx, "BroadcastMessage: both in-band and out-of-band message nil")
  1011  	return errors.New("invalid message, no ibm or oobm")
  1012  }
  1013  
  1014  func (g *gregorHandler) broadcastMessageHandler() {
  1015  	ctx := context.Background()
  1016  	for {
  1017  		m := <-g.broadcastCh
  1018  		if g.G().GetEnv().GetSlowGregorConn() {
  1019  			g.Debug(ctx, "[slow conn]: sleeping")
  1020  			time.Sleep(slowConnSleepTime)
  1021  			g.Debug(ctx, "[slow conn]: awake")
  1022  		}
  1023  		err := g.broadcastMessageOnce(ctx, m)
  1024  		if err != nil {
  1025  			g.Debug(context.Background(), "broadcast error: %v", err)
  1026  		}
  1027  
  1028  		// Testing alerts
  1029  		if g.testingEvents != nil {
  1030  			g.testingEvents.broadcastSentCh <- err
  1031  		}
  1032  	}
  1033  }
  1034  
  1035  // BroadcastMessage is called when we receive a new message from gregord. Grabs
  1036  // the lock protect the state machine and handleInBandMessage
  1037  func (g *gregorHandler) BroadcastMessage(ctx context.Context, m gregor1.Message) error {
  1038  	// Send the message on a channel so we can return to Gregor as fast as possible. Note
  1039  	// that this can block, but broadcastCh has a large buffer to try and mitigate
  1040  	g.broadcastCh <- m
  1041  	return nil
  1042  }
  1043  
  1044  // handleInBandMessage runs a message on all the alive handlers. gregorHandler
  1045  // must be locked when calling this function.
  1046  func (g *gregorHandler) handleInBandMessage(ctx context.Context, cli gregor1.IncomingInterface,
  1047  	ibm gregor.InBandMessage) (err error) {
  1048  
  1049  	defer g.G().Trace(fmt.Sprintf("gregorHandler#handleInBandMessage with %d handlers", len(g.ibmHandlers)), &err)()
  1050  	ctx = libkb.WithLogTag(ctx, "GRGIBM")
  1051  
  1052  	var freshHandlers []libkb.GregorInBandMessageHandler
  1053  
  1054  	// Loop over all handlers and run the messages against any that are alive
  1055  	// If the handler is not alive, we prune it from our list
  1056  	for i, handler := range g.ibmHandlers {
  1057  		g.Debug(ctx, "trying handler %s at position %d", handler.Name(), i)
  1058  		if handler.IsAlive() {
  1059  			if handled, err := g.handleInBandMessageWithHandler(ctx, cli, ibm, handler); err != nil {
  1060  				if handled {
  1061  					// Don't stop handling errors on a first failure.
  1062  					g.Errorf(ctx, "failed to run %s handler: %s", handler.Name(), err)
  1063  				} else {
  1064  					g.Debug(ctx, "handleInBandMessage() failed to run %s handler: %s", handler.Name(), err)
  1065  				}
  1066  			}
  1067  			freshHandlers = append(freshHandlers, handler)
  1068  		} else {
  1069  			g.Debug(ctx, "skipping handler as it's marked dead: %s", handler.Name())
  1070  		}
  1071  	}
  1072  
  1073  	if len(g.ibmHandlers) != len(freshHandlers) {
  1074  		g.Debug(ctx, "Change # of live handlers from %d to %d", len(g.ibmHandlers), len(freshHandlers))
  1075  		g.ibmHandlers = freshHandlers
  1076  	}
  1077  	return nil
  1078  }
  1079  
  1080  // handleInBandMessageWithHandler runs a message against the specified handler
  1081  func (g *gregorHandler) handleInBandMessageWithHandler(ctx context.Context, cli gregor1.IncomingInterface,
  1082  	ibm gregor.InBandMessage, handler libkb.GregorInBandMessageHandler) (bool, error) {
  1083  	g.Debug(ctx, "handleInBand: %+v", ibm)
  1084  
  1085  	gcli, err := g.getGregorCli()
  1086  	if err != nil {
  1087  		return false, err
  1088  	}
  1089  	state, err := gcli.StateMachineState(ctx, nil, false)
  1090  	if err != nil {
  1091  		return false, err
  1092  	}
  1093  
  1094  	sync := ibm.ToStateSyncMessage()
  1095  	if sync != nil {
  1096  		g.Debug(ctx, "state sync message")
  1097  		return false, nil
  1098  	}
  1099  
  1100  	update := ibm.ToStateUpdateMessage()
  1101  	if update != nil {
  1102  		g.Debug(ctx, "state update message")
  1103  
  1104  		item := update.Creation()
  1105  		if item != nil {
  1106  			id := item.Metadata().MsgID().String()
  1107  			g.Debug(ctx, "msg ID %s created ctime: %s", id,
  1108  				item.Metadata().CTime())
  1109  
  1110  			category := ""
  1111  			if item.Category() != nil {
  1112  				category = item.Category().String()
  1113  				g.Debug(ctx, "item %s has category %s", id, category)
  1114  			}
  1115  
  1116  			if handled, err := handler.Create(ctx, cli, category, item); err != nil {
  1117  				return handled, err
  1118  			}
  1119  		}
  1120  
  1121  		dismissal := update.Dismissal()
  1122  		if dismissal != nil {
  1123  			g.Debug(ctx, "received dismissal")
  1124  			for _, id := range dismissal.MsgIDsToDismiss() {
  1125  				item, present := state.GetItem(id)
  1126  				if !present {
  1127  					g.Debug(ctx, "tried to dismiss item %s, not present", id.String())
  1128  					continue
  1129  				}
  1130  				g.Debug(ctx, "dismissing item %s", id.String())
  1131  
  1132  				category := ""
  1133  				if item.Category() != nil {
  1134  					category = item.Category().String()
  1135  					g.Debug(ctx, "dismissal %s has category %s", id, category)
  1136  				}
  1137  
  1138  				if handled, err := handler.Dismiss(ctx, cli, category, item); handled && err != nil {
  1139  					return handled, err
  1140  				}
  1141  			}
  1142  			if len(dismissal.RangesToDismiss()) > 0 {
  1143  				g.Debug(ctx, "message range dismissing not implemented")
  1144  			}
  1145  		}
  1146  
  1147  		return true, nil
  1148  	}
  1149  
  1150  	return false, nil
  1151  }
  1152  
  1153  func (h IdentifyUIHandler) Create(ctx context.Context, cli gregor1.IncomingInterface, category string,
  1154  	item gregor.Item) (bool, error) {
  1155  
  1156  	switch category {
  1157  	case "show_tracker_popup":
  1158  		return true, h.handleShowTrackerPopupCreate(ctx, cli, item)
  1159  	default:
  1160  		return false, nil
  1161  	}
  1162  }
  1163  
  1164  func (h IdentifyUIHandler) Dismiss(ctx context.Context, cli gregor1.IncomingInterface, category string,
  1165  	item gregor.Item) (bool, error) {
  1166  
  1167  	switch category {
  1168  	case "show_tracker_popup":
  1169  		return true, h.handleShowTrackerPopupDismiss(ctx, cli, item)
  1170  	default:
  1171  		return false, nil
  1172  	}
  1173  }
  1174  
  1175  func (h IdentifyUIHandler) handleShowTrackerPopupCreate(ctx context.Context, cli gregor1.IncomingInterface,
  1176  	item gregor.Item) error {
  1177  
  1178  	h.G().Log.Debug("handleShowTrackerPopupCreate: %+v", item)
  1179  	if item.Body() == nil {
  1180  		return errors.New("gregor handler for show_tracker_popup: nil message body")
  1181  	}
  1182  	body, err := jsonw.Unmarshal(item.Body().Bytes())
  1183  	if err != nil {
  1184  		h.G().Log.Debug("body failed to unmarshal", err)
  1185  		return err
  1186  	}
  1187  	uidString, err := body.AtPath("uid").GetString()
  1188  	if err != nil {
  1189  		h.G().Log.Debug("failed to extract uid", err)
  1190  		return err
  1191  	}
  1192  	uid, err := keybase1.UIDFromString(uidString)
  1193  	if err != nil {
  1194  		h.G().Log.Debug("failed to convert UID from string", err)
  1195  		return err
  1196  	}
  1197  
  1198  	identifyUI, err := h.G().UIRouter.GetIdentifyUI()
  1199  	if err != nil {
  1200  		h.G().Log.Debug("failed to get IdentifyUI", err)
  1201  		return err
  1202  	}
  1203  	if identifyUI == nil {
  1204  		h.G().Log.Debug("got nil IdentifyUI")
  1205  		return errors.New("got nil IdentifyUI")
  1206  	}
  1207  	secretUI, err := h.G().UIRouter.GetSecretUI(0)
  1208  	if err != nil {
  1209  		h.G().Log.Debug("failed to get SecretUI", err)
  1210  		return err
  1211  	}
  1212  	if secretUI == nil {
  1213  		h.G().Log.Debug("got nil SecretUI")
  1214  		return errors.New("got nil SecretUI")
  1215  	}
  1216  	uis := libkb.UIs{
  1217  		IdentifyUI: identifyUI,
  1218  		SecretUI:   secretUI,
  1219  	}
  1220  
  1221  	identifyReason := keybase1.IdentifyReason{
  1222  		Type: keybase1.IdentifyReasonType_TRACK,
  1223  		// TODO: text here?
  1224  	}
  1225  	identifyArg := keybase1.Identify2Arg{Uid: uid, Reason: identifyReason}
  1226  	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
  1227  	identifyEng := engine.NewIdentify2WithUID(h.G(), &identifyArg)
  1228  	identifyEng.SetResponsibleGregorItem(item)
  1229  	return identifyEng.Run(m)
  1230  }
  1231  
  1232  func (h IdentifyUIHandler) handleShowTrackerPopupDismiss(ctx context.Context, cli gregor1.IncomingInterface,
  1233  	item gregor.Item) error {
  1234  	mctx := libkb.NewMetaContext(ctx, h.G())
  1235  
  1236  	mctx.Debug("handleShowTrackerPopupDismiss: %+v", item)
  1237  	if item.Body() == nil {
  1238  		return errors.New("gregor dismissal for show_tracker_popup: nil message body")
  1239  	}
  1240  	body, err := jsonw.Unmarshal(item.Body().Bytes())
  1241  	if err != nil {
  1242  		mctx.Debug("body failed to unmarshal", err)
  1243  		return err
  1244  	}
  1245  	uidString, err := body.AtPath("uid").GetString()
  1246  	if err != nil {
  1247  		mctx.Debug("failed to extract uid", err)
  1248  		return err
  1249  	}
  1250  	uid, err := keybase1.UIDFromString(uidString)
  1251  	if err != nil {
  1252  		mctx.Debug("failed to convert UID from string", err)
  1253  		return err
  1254  	}
  1255  	user, err := libkb.LoadUser(libkb.NewLoadUserByUIDArg(ctx, h.G(), uid))
  1256  	if err != nil {
  1257  		mctx.Debug("failed to load user from UID", err)
  1258  		return err
  1259  	}
  1260  
  1261  	identifyUI, err := h.G().UIRouter.GetIdentifyUI()
  1262  	if err != nil {
  1263  		mctx.Debug("failed to get IdentifyUI", err)
  1264  		return err
  1265  	}
  1266  	if identifyUI == nil {
  1267  		mctx.Debug("got nil IdentifyUI")
  1268  		return errors.New("got nil IdentifyUI")
  1269  	}
  1270  
  1271  	reason := keybase1.DismissReason{
  1272  		Type: keybase1.DismissReasonType_HANDLED_ELSEWHERE,
  1273  	}
  1274  	_ = identifyUI.Dismiss(mctx, user.GetName(), reason)
  1275  
  1276  	return nil
  1277  }
  1278  
  1279  func (g *gregorHandler) handleOutOfBandMessage(ctx context.Context, obm gregor.OutOfBandMessage) error {
  1280  	if obm.System() == nil {
  1281  		return errors.New("nil system in out of band message")
  1282  	}
  1283  
  1284  	if tmp, ok := obm.(gregor1.OutOfBandMessage); ok {
  1285  		g.pushOutOfBandMessages([]gregor1.OutOfBandMessage{tmp})
  1286  	} else {
  1287  		g.G().Log.Warning("Got non-exportable out-of-band message")
  1288  	}
  1289  
  1290  	// Send the oobm to the chat system so that it can potentially handle it
  1291  	if g.G().PushHandler != nil {
  1292  		handled, err := g.G().PushHandler.HandleOobm(ctx, obm)
  1293  		if err != nil {
  1294  			return err
  1295  		}
  1296  		if handled {
  1297  			return nil
  1298  		}
  1299  	}
  1300  
  1301  	// Send the oobm to the wallet system so that it can potentially handle it
  1302  	if g.G().StellarPushHandler != nil {
  1303  		handled, err := g.G().StellarPushHandler.HandleOobm(ctx, obm)
  1304  		if err != nil {
  1305  			return err
  1306  		}
  1307  		if handled {
  1308  			return nil
  1309  		}
  1310  	}
  1311  
  1312  	switch obm.System().String() {
  1313  	case "internal.reconnect":
  1314  		g.G().Log.Debug("reconnected to push server")
  1315  		return nil
  1316  	default:
  1317  		return fmt.Errorf("unhandled system: %s", obm.System())
  1318  	}
  1319  }
  1320  
  1321  func (g *gregorHandler) Shutdown() {
  1322  	defer g.chatLog.Trace(context.Background(), nil, "Shutdown")()
  1323  	g.connMutex.Lock()
  1324  	defer g.connMutex.Unlock()
  1325  
  1326  	if g.conn == nil {
  1327  		return
  1328  	}
  1329  
  1330  	// Alert chat syncer that we are now disconnected
  1331  	g.G().Syncer.Disconnected(context.Background())
  1332  
  1333  	close(g.shutdownCh)
  1334  	g.conn.Shutdown()
  1335  	g.conn = nil
  1336  	g.cli = nil
  1337  }
  1338  
  1339  func (g *gregorHandler) Reset() error {
  1340  	g.Shutdown()
  1341  	g.setFirstConnect(true)
  1342  	g.shutdownGregorClient(context.TODO())
  1343  	return nil
  1344  }
  1345  
  1346  type loggedInRes int
  1347  
  1348  const (
  1349  	loggedInYes loggedInRes = iota
  1350  	loggedInNo
  1351  	loggedInMaybe
  1352  )
  1353  
  1354  func (g *gregorHandler) loggedIn(ctx context.Context) (uid keybase1.UID, did keybase1.DeviceID, token string, nist *libkb.NIST, res loggedInRes) {
  1355  
  1356  	// Check to see if we have been shut down,
  1357  	select {
  1358  	case <-g.shutdownCh:
  1359  		return uid, did, token, nil, loggedInMaybe
  1360  	default:
  1361  		// if we were going to block, then that means we are still alive
  1362  	}
  1363  
  1364  	var err error
  1365  
  1366  	nist, uid, did, err = g.G().ActiveDevice.NISTAndUIDDeviceID(ctx)
  1367  	if nist == nil {
  1368  		g.G().Log.CDebugf(ctx, "gregorHandler: no NIST for login; user isn't logged in")
  1369  		return uid, did, token, nil, loggedInNo
  1370  	}
  1371  	if err != nil {
  1372  		g.G().Log.CDebugf(ctx, "gregorHandler: error in generating NIST: %s", err.Error())
  1373  		return uid, did, token, nil, loggedInMaybe
  1374  	}
  1375  
  1376  	return uid, did, nist.Token().String(), nist, loggedInYes
  1377  }
  1378  
  1379  func (g *gregorHandler) auth(ctx context.Context, cli rpc.GenericClient, auth *gregor1.AuthResult) (err error) {
  1380  	var token string
  1381  	var res loggedInRes
  1382  	var uid keybase1.UID
  1383  	var nist *libkb.NIST
  1384  
  1385  	if uid, _, token, nist, res = g.loggedIn(ctx); res != loggedInYes {
  1386  		return newConnectionAuthError("not logged in for auth", res == loggedInMaybe)
  1387  	}
  1388  
  1389  	if auth == nil {
  1390  		g.chatLog.Debug(ctx, "logged in: authenticating")
  1391  		ac := gregor1.AuthClient{Cli: cli}
  1392  		auth = new(gregor1.AuthResult)
  1393  		*auth, err = ac.AuthenticateSessionToken(ctx, gregor1.SessionToken(token))
  1394  		if err != nil {
  1395  			g.chatLog.Debug(ctx, "auth error: %s", err)
  1396  			g.forceSessionCheck = true
  1397  			nist.DidFail()
  1398  			return err
  1399  		}
  1400  	} else {
  1401  		g.Debug(ctx, "using previously obtained auth result")
  1402  	}
  1403  
  1404  	g.chatLog.Debug(ctx, "auth result: %+v", *auth)
  1405  	if !bytes.Equal(auth.Uid, uid.ToBytes()) {
  1406  		msg := fmt.Sprintf("auth result uid %x doesn't match session uid %q", auth.Uid, uid)
  1407  		return newConnectionAuthError(msg, false)
  1408  	}
  1409  	g.sessionID = auth.Sid
  1410  
  1411  	return nil
  1412  }
  1413  
  1414  func (g *gregorHandler) isReachable() bool {
  1415  	ctx := context.Background()
  1416  	timeout := g.G().Env.GetGregorPingTimeout()
  1417  	url, err := url.Parse(g.G().Env.GetGregorURI())
  1418  	if err != nil {
  1419  		g.chatLog.Debug(ctx, "isReachable: failed to parse server uri, exiting: %s", err.Error())
  1420  		return false
  1421  	}
  1422  
  1423  	// If we currently think we are online, then make sure
  1424  	conn, err := libkb.ProxyDialTimeout(g.G().Env, "tcp", url.Host, timeout)
  1425  	if conn != nil {
  1426  		conn.Close()
  1427  		return true
  1428  	}
  1429  	if err != nil {
  1430  		g.chatLog.Debug(ctx, "isReachable: error: terminating connection: %s", err.Error())
  1431  		if _, err := g.Reconnect(ctx); err != nil {
  1432  			g.chatLog.Debug(ctx, "isReachable: error reconnecting: %s", err.Error())
  1433  		}
  1434  		return false
  1435  	}
  1436  
  1437  	return true
  1438  }
  1439  
  1440  func (g *gregorHandler) Reconnect(ctx context.Context) (didShutdown bool, err error) {
  1441  	if g.IsConnected() {
  1442  		didShutdown = true
  1443  		g.chatLog.Debug(ctx, "Reconnect: reconnecting to server")
  1444  		g.Shutdown()
  1445  		return didShutdown, g.Connect(g.uri)
  1446  	}
  1447  
  1448  	didShutdown = false
  1449  	g.chatLog.Debug(ctx, "Reconnect: skipping reconnect, already disconnected")
  1450  	return didShutdown, nil
  1451  }
  1452  
  1453  func (g *gregorHandler) forcePing(ctx context.Context) {
  1454  	select {
  1455  	case g.forcePingCh <- struct{}{}:
  1456  	default:
  1457  		g.Debug(ctx, "forcePing: failed to write to channel, its full")
  1458  	}
  1459  }
  1460  
  1461  func (g *gregorHandler) pingOnce(ctx context.Context, id []byte, shutdownCancel context.CancelFunc) {
  1462  	var err error
  1463  	doneCh := make(chan error)
  1464  	timeout := g.G().Env.GetGregorPingTimeout()
  1465  	go func(ctx context.Context) {
  1466  		if g.IsConnected() {
  1467  			// If we are connected, subject the ping call to a fairly
  1468  			// aggressive timeout so our chat stuff can be responsive
  1469  			// to changes in connectivity
  1470  			var timeoutCancel context.CancelFunc
  1471  			var timeoutCtx context.Context
  1472  			timeoutCtx, timeoutCancel = context.WithTimeout(ctx, timeout)
  1473  			_, err = gregor1.IncomingClient{Cli: g.pingCli}.Ping(timeoutCtx)
  1474  			timeoutCancel()
  1475  		} else {
  1476  			// If we are not connected, we don't want to timeout anything
  1477  			// Just hook into the normal reconnect chan stuff in the RPC
  1478  			// library
  1479  			g.chatLog.Debug(ctx, "ping loop: id: %x normal ping, not connected", id)
  1480  			_, err = gregor1.IncomingClient{Cli: g.pingCli}.Ping(ctx)
  1481  			g.chatLog.Debug(ctx, "ping loop: id: %x normal ping success", id)
  1482  		}
  1483  		select {
  1484  		case <-ctx.Done():
  1485  			g.chatLog.Debug(ctx, "ping loop: id: %x context cancelled, so not sending err", id)
  1486  		default:
  1487  			doneCh <- err
  1488  		}
  1489  	}(ctx)
  1490  
  1491  	select {
  1492  	case err = <-doneCh:
  1493  	case <-g.shutdownCh:
  1494  		g.chatLog.Debug(ctx, "ping loop: id: %x shutdown received", id)
  1495  		shutdownCancel()
  1496  		return
  1497  	}
  1498  	if err != nil {
  1499  		g.Debug(ctx, "ping loop: id: %x error: %s", id, err)
  1500  		if err == context.DeadlineExceeded {
  1501  			g.chatLog.Debug(ctx, "ping loop: timeout: terminating connection")
  1502  			var didShutdown bool
  1503  			var err error
  1504  			if didShutdown, err = g.Reconnect(ctx); err != nil {
  1505  				g.chatLog.Debug(ctx, "ping loop: id: %x error reconnecting: %s", id, err)
  1506  			}
  1507  			// It is possible that we have already reconnected by the time we call Reconnect
  1508  			// above. If that is the case, we don't want to terminate the ping loop. Only
  1509  			// if Reconnect has actually reset the connection do we stop this ping loop.
  1510  			if didShutdown {
  1511  				shutdownCancel()
  1512  				return
  1513  			}
  1514  		}
  1515  	}
  1516  }
  1517  
  1518  func (g *gregorHandler) pingLoop() {
  1519  	ctx := context.Background()
  1520  	id, _ := libkb.RandBytes(4)
  1521  	duration := g.G().Env.GetGregorPingInterval()
  1522  	timeout := g.G().Env.GetGregorPingTimeout()
  1523  	url, err := url.Parse(g.G().Env.GetGregorURI())
  1524  	if err != nil {
  1525  		g.chatLog.Debug(ctx, "ping loop: failed to parse server uri, exiting: %s", err.Error())
  1526  		return
  1527  	}
  1528  	g.chatLog.Debug(ctx, "ping loop: starting up: id: %x duration: %v timeout: %v url: %s",
  1529  		id, duration, timeout, url.Host)
  1530  	defer g.chatLog.Debug(ctx, "ping loop: id: %x terminating", id)
  1531  	ticker := time.NewTicker(duration)
  1532  	for {
  1533  		ctx, shutdownCancel := context.WithCancel(context.Background())
  1534  		select {
  1535  		case <-g.forcePingCh:
  1536  			g.chatLog.Debug(ctx, "ping loop: forced attempt")
  1537  			g.pingOnce(ctx, id, shutdownCancel)
  1538  		case <-ticker.C:
  1539  			g.pingOnce(ctx, id, shutdownCancel)
  1540  		case <-g.shutdownCh:
  1541  			g.chatLog.Debug(ctx, "ping loop: id: %x shutdown received", id)
  1542  			shutdownCancel()
  1543  			return
  1544  		}
  1545  		shutdownCancel()
  1546  	}
  1547  }
  1548  
  1549  // connMutex must be locked before calling this
  1550  func (g *gregorHandler) connectTLS() error {
  1551  	ctx := context.Background()
  1552  	if g.conn != nil {
  1553  		g.chatLog.Debug(ctx, "skipping connect, conn is not nil")
  1554  		return nil
  1555  	}
  1556  
  1557  	uri := g.uri
  1558  	g.chatLog.Debug(ctx, "connecting to gregord via TLS at %s", uri)
  1559  	rawCA := g.G().Env.GetBundledCA(uri.Host)
  1560  	if len(rawCA) == 0 {
  1561  		return fmt.Errorf("No bundled CA for %s", uri.Host)
  1562  	}
  1563  	g.chatLog.Debug(ctx, "Using CA for gregor: %s", libkb.ShortCA(rawCA))
  1564  	// Let people know we are trying to sync
  1565  	g.G().NotifyRouter.HandleChatInboxSyncStarted(ctx, g.G().Env.GetUID())
  1566  
  1567  	opts := rpc.ConnectionOpts{
  1568  		TagsFunc:      logger.LogTagsFromContextRPC,
  1569  		WrapErrorFunc: libkb.MakeWrapError(g.G().ExternalG()),
  1570  		ReconnectBackoff: func() backoff.BackOff {
  1571  			return backoff.NewConstantBackOff(GregorConnectionRetryInterval)
  1572  		},
  1573  		DialerTimeout:    10 * time.Second,
  1574  		HandshakeTimeout: 10 * time.Second,
  1575  		// We deliberately avoid ForceInitialBackoff here, becuase we don't
  1576  		// want to penalize mobile, which tears down its connection frequently.
  1577  	}
  1578  	g.conn = rpc.NewTLSConnectionWithDialable(rpc.NewFixedRemote(uri.HostPort),
  1579  		[]byte(rawCA), libkb.NewContextifiedErrorUnwrapper(g.G().ExternalG()),
  1580  		g, libkb.NewRPCLogFactory(g.G().ExternalG()),
  1581  		g.G().ExternalG().RemoteNetworkInstrumenterStorage,
  1582  		logger.LogOutputWithDepthAdder{Logger: g.G().Log},
  1583  		rpc.DefaultMaxFrameLength, opts,
  1584  		libkb.NewProxyDialable(g.G().Env))
  1585  
  1586  	// The client we get here will reconnect to gregord on disconnect if necessary.
  1587  	// We should grab it here instead of in OnConnect, since the connection is not
  1588  	// fully established in OnConnect. Anything that wants to make calls outside
  1589  	// of OnConnect should use g.cli, everything else should the client that is
  1590  	// a parameter to OnConnect
  1591  	g.cli = WrapGenericClientWithTimeout(g.conn.GetClient(), GregorRequestTimeout,
  1592  		chat.ErrChatServerTimeout)
  1593  	g.pingCli = g.conn.GetClient() // Don't want this to have a timeout from here
  1594  
  1595  	// Start up ping loop to keep the connection to gregord alive, and to kick
  1596  	// off the reconnect logic in the RPC library
  1597  	go g.pingLoop()
  1598  
  1599  	return nil
  1600  }
  1601  
  1602  // connMutex must be locked before calling this
  1603  func (g *gregorHandler) connectNoTLS() error {
  1604  	ctx := context.Background()
  1605  	if g.conn != nil {
  1606  		g.chatLog.Debug(ctx, "skipping connect, conn is not nil")
  1607  		return nil
  1608  	}
  1609  	uri := g.uri
  1610  	g.chatLog.Debug(ctx, "connecting to gregord without TLS at %s", uri)
  1611  	t := newConnTransport(g.G().ExternalG(), uri.HostPort)
  1612  	g.transportForTesting = t
  1613  
  1614  	opts := rpc.ConnectionOpts{
  1615  		TagsFunc:      logger.LogTagsFromContextRPC,
  1616  		WrapErrorFunc: libkb.MakeWrapError(g.G().ExternalG()),
  1617  		ReconnectBackoff: func() backoff.BackOff {
  1618  			return backoff.NewConstantBackOff(GregorConnectionRetryInterval)
  1619  		},
  1620  	}
  1621  	g.conn = rpc.NewConnectionWithTransport(g, t,
  1622  		libkb.NewContextifiedErrorUnwrapper(g.G().ExternalG()),
  1623  		logger.LogOutputWithDepthAdder{Logger: g.G().Log}, opts)
  1624  
  1625  	g.cli = WrapGenericClientWithTimeout(g.conn.GetClient(), GregorRequestTimeout,
  1626  		chat.ErrChatServerTimeout)
  1627  	g.pingCli = g.conn.GetClient()
  1628  
  1629  	// Start up ping loop to keep the connection to gregord alive, and to kick
  1630  	// off the reconnect logic in the RPC library
  1631  	go g.pingLoop()
  1632  
  1633  	return nil
  1634  }
  1635  
  1636  func (g *gregorHandler) currentUID() gregor1.UID {
  1637  	return gregor1.UID(g.G().ActiveDevice.UID().ToBytes())
  1638  }
  1639  
  1640  // `cli` is the interface used to talk to gregor.
  1641  // If nil then the global cli will be used.
  1642  // Be sure to pass a cli when called from within OnConnect, as the global cli would deadlock.
  1643  func (g *gregorHandler) DismissItem(ctx context.Context, cli gregor1.IncomingInterface, id gregor.MsgID) error {
  1644  	if id == nil {
  1645  		return nil
  1646  	}
  1647  	var err error
  1648  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.dismissItem(%s)", id.String()),
  1649  		&err,
  1650  	)()
  1651  	defer g.pushState(keybase1.PushReason_NEW_DATA)
  1652  	dismissal, err := grutils.FormMessageForDismissItem(ctx, g.currentUID(), id)
  1653  	if err != nil {
  1654  		return err
  1655  	}
  1656  	gcli, err := g.getGregorCli()
  1657  	if err != nil {
  1658  		return err
  1659  	}
  1660  	return gcli.ConsumeMessage(ctx, dismissal)
  1661  }
  1662  
  1663  func (g *gregorHandler) LocalDismissItem(ctx context.Context, id gregor.MsgID) (err error) {
  1664  	if id == nil {
  1665  		return nil
  1666  	}
  1667  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.localDismissItem(%s)", id.String()),
  1668  		&err,
  1669  	)()
  1670  	defer g.pushState(keybase1.PushReason_NEW_DATA)
  1671  
  1672  	cli, err := g.getGregorCli()
  1673  	if err != nil {
  1674  		return err
  1675  	}
  1676  	return cli.StateMachineConsumeLocalDismissal(ctx, id)
  1677  }
  1678  
  1679  func (g *gregorHandler) DismissCategory(ctx context.Context, category gregor1.Category) error {
  1680  	var err error
  1681  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.DismissCategory(%s)", category.String()),
  1682  		&err,
  1683  	)()
  1684  	defer g.pushState(keybase1.PushReason_NEW_DATA)
  1685  
  1686  	dismissal, err := grutils.FormMessageForDismissCategory(ctx, g.currentUID(), category)
  1687  	if err != nil {
  1688  		return err
  1689  	}
  1690  
  1691  	gcli, err := g.getGregorCli()
  1692  	if err != nil {
  1693  		return err
  1694  	}
  1695  	return gcli.ConsumeMessage(ctx, dismissal)
  1696  }
  1697  
  1698  func (g *gregorHandler) InjectItem(ctx context.Context, cat string, body []byte, dtime gregor1.TimeOrOffset) (gregor1.MsgID, error) {
  1699  	var err error
  1700  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.InjectItem(%s)", cat),
  1701  		&err,
  1702  	)()
  1703  	defer g.pushState(keybase1.PushReason_NEW_DATA)
  1704  
  1705  	creation, err := grutils.FormMessageForInjectItem(ctx, g.currentUID(), cat, body, dtime)
  1706  	if err != nil {
  1707  		return nil, err
  1708  	}
  1709  
  1710  	gcli, err := g.getGregorCli()
  1711  	if err != nil {
  1712  		return nil, err
  1713  	}
  1714  	retMsgID := gregor1.MsgID(creation.ToInBandMessage().Metadata().MsgID().Bytes())
  1715  	return retMsgID, gcli.ConsumeMessage(ctx, creation)
  1716  }
  1717  
  1718  func (g *gregorHandler) UpdateItem(ctx context.Context, msgID gregor1.MsgID, cat string, body []byte, dtime gregor1.TimeOrOffset) (gregor1.MsgID, error) {
  1719  	var err error
  1720  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.UpdateItem(%s,%s)", msgID.String(), cat),
  1721  		&err,
  1722  	)()
  1723  	defer g.pushState(keybase1.PushReason_NEW_DATA)
  1724  
  1725  	msg, err := grutils.TemplateMessage(g.currentUID())
  1726  	if err != nil {
  1727  		return nil, err
  1728  	}
  1729  	msg.Ibm_.StateUpdate_.Creation_ = &gregor1.Item{
  1730  		Category_: gregor1.Category(cat),
  1731  		Body_:     gregor1.Body(body),
  1732  		Dtime_:    dtime,
  1733  	}
  1734  	msg.Ibm_.StateUpdate_.Dismissal_ = &gregor1.Dismissal{
  1735  		MsgIDs_: []gregor1.MsgID{msgID},
  1736  	}
  1737  
  1738  	gcli, err := g.getGregorCli()
  1739  	if err != nil {
  1740  		return nil, err
  1741  	}
  1742  	return msg.Ibm_.StateUpdate_.Md_.MsgID_, gcli.ConsumeMessage(ctx, msg)
  1743  }
  1744  
  1745  func (g *gregorHandler) UpdateCategory(ctx context.Context, cat string, body []byte,
  1746  	dtime gregor1.TimeOrOffset) (res gregor1.MsgID, err error) {
  1747  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.UpdateCategory(%s)", cat),
  1748  		&err,
  1749  	)()
  1750  	defer g.pushState(keybase1.PushReason_NEW_DATA)
  1751  
  1752  	msg, err := grutils.TemplateMessage(g.currentUID())
  1753  	if err != nil {
  1754  		return nil, err
  1755  	}
  1756  	msgID := msg.Ibm_.StateUpdate_.Md_.MsgID_
  1757  	msg.Ibm_.StateUpdate_.Creation_ = &gregor1.Item{
  1758  		Category_: gregor1.Category(cat),
  1759  		Body_:     gregor1.Body(body),
  1760  		Dtime_:    dtime,
  1761  	}
  1762  	msg.Ibm_.StateUpdate_.Dismissal_ = &gregor1.Dismissal{
  1763  		Ranges_: []gregor1.MsgRange{
  1764  			{
  1765  				Category_:   gregor1.Category(cat),
  1766  				SkipMsgIDs_: []gregor1.MsgID{msgID},
  1767  			}},
  1768  	}
  1769  
  1770  	gcli, err := g.getGregorCli()
  1771  	if err != nil {
  1772  		return nil, err
  1773  	}
  1774  	return msgID, gcli.ConsumeMessage(ctx, msg)
  1775  }
  1776  
  1777  func (g *gregorHandler) InjectOutOfBandMessage(ctx context.Context, system string, body []byte) error {
  1778  	var err error
  1779  	defer g.G().CTrace(ctx, fmt.Sprintf("gregorHandler.InjectOutOfBandMessage(%s)", system),
  1780  		&err,
  1781  	)()
  1782  
  1783  	uid := g.G().Env.GetUID()
  1784  	if uid.IsNil() {
  1785  		return libkb.LoggedInError{}
  1786  	}
  1787  	gregorUID := gregor1.UID(uid.ToBytes())
  1788  
  1789  	msg := gregor1.Message{
  1790  		Oobm_: &gregor1.OutOfBandMessage{
  1791  			Uid_:    gregorUID,
  1792  			System_: gregor1.System(system),
  1793  			Body_:   gregor1.Body(body),
  1794  		},
  1795  	}
  1796  
  1797  	gcli, err := g.getGregorCli()
  1798  	if err != nil {
  1799  		return err
  1800  	}
  1801  	return gcli.ConsumeMessage(ctx, msg)
  1802  }
  1803  
  1804  func (g *gregorHandler) simulateCrashForTesting() {
  1805  	g.transportForTesting.Reset()
  1806  	_, _ = gregor1.IncomingClient{Cli: g.cli}.Ping(context.Background())
  1807  }
  1808  
  1809  type gregorRPCHandler struct {
  1810  	libkb.Contextified
  1811  	xp rpc.Transporter
  1812  	gh *gregorHandler
  1813  }
  1814  
  1815  func newGregorRPCHandler(xp rpc.Transporter, g *libkb.GlobalContext, gh *gregorHandler) *gregorRPCHandler {
  1816  	return &gregorRPCHandler{
  1817  		Contextified: libkb.NewContextified(g),
  1818  		xp:           xp,
  1819  		gh:           gh,
  1820  	}
  1821  }
  1822  
  1823  func (g *gregorHandler) getState(ctx context.Context) (res gregor1.State, err error) {
  1824  	var s gregor.State
  1825  
  1826  	gcli, err := g.getGregorCli()
  1827  	if err != nil {
  1828  		return res, err
  1829  	}
  1830  
  1831  	s, err = gcli.StateMachineState(ctx, nil, true)
  1832  	if err != nil {
  1833  		return res, err
  1834  	}
  1835  
  1836  	ps, err := s.Export()
  1837  	if err != nil {
  1838  		return res, err
  1839  	}
  1840  
  1841  	var ok bool
  1842  	if res, ok = ps.(gregor1.State); !ok {
  1843  		return res, errors.New("failed to convert state to exportable format")
  1844  	}
  1845  
  1846  	return res, nil
  1847  }
  1848  
  1849  func (g *gregorHandler) State(ctx context.Context) (res gregor.State, err error) {
  1850  	defer g.G().CTrace(ctx, "gregorHandler#State", &err)()
  1851  	gcli, err := g.getGregorCli()
  1852  	if err != nil {
  1853  		return res, err
  1854  	}
  1855  	return gcli.StateMachineState(ctx, nil, true)
  1856  }
  1857  
  1858  func (g *gregorRPCHandler) GetState(ctx context.Context) (res gregor1.State, err error) {
  1859  	defer g.G().CTrace(ctx, "gregorRPCHandler#GetState", &err)()
  1860  	if res, err = g.gh.getState(ctx); err != nil {
  1861  		return res, err
  1862  	}
  1863  	g.G().Log.CDebugf(ctx, "GetState: returning %d items", len(res.Items_))
  1864  	return res, nil
  1865  }
  1866  
  1867  func (g *gregorRPCHandler) InjectItem(ctx context.Context, arg keybase1.InjectItemArg) (res gregor1.MsgID, err error) {
  1868  	defer g.G().CTrace(ctx, "gregorRPCHandler#InjectItem", &err)()
  1869  	return g.gh.InjectItem(ctx, arg.Cat, []byte(arg.Body), arg.Dtime)
  1870  }
  1871  
  1872  func (g *gregorRPCHandler) UpdateItem(ctx context.Context, arg keybase1.UpdateItemArg) (res gregor1.MsgID, err error) {
  1873  	defer g.G().CTrace(ctx, "gregorRPCHandler#UpdateItem", &err)()
  1874  	return g.gh.UpdateItem(ctx, arg.MsgID, arg.Cat, []byte(arg.Body), arg.Dtime)
  1875  }
  1876  
  1877  func (g *gregorRPCHandler) UpdateCategory(ctx context.Context, arg keybase1.UpdateCategoryArg) (res gregor1.MsgID, err error) {
  1878  	defer g.G().CTrace(ctx, "gregorRPCHandler#UpdateCategory", &err)()
  1879  	return g.gh.UpdateCategory(ctx, arg.Category, []byte(arg.Body), arg.Dtime)
  1880  }
  1881  
  1882  func (g *gregorRPCHandler) DismissCategory(ctx context.Context, category gregor1.Category) (err error) {
  1883  	defer g.G().CTrace(ctx, "gregorRPCHandler#DismissCategory", &err)()
  1884  	return g.gh.DismissCategory(ctx, category)
  1885  }
  1886  
  1887  func (g *gregorRPCHandler) DismissItem(ctx context.Context, id gregor1.MsgID) (err error) {
  1888  	defer g.G().CTrace(ctx, "gregorRPCHandler#DismissItem", &err)()
  1889  	return g.gh.DismissItem(ctx, nil, id)
  1890  }
  1891  
  1892  func WrapGenericClientWithTimeout(client rpc.GenericClient, timeout time.Duration, timeoutErr error) rpc.GenericClient {
  1893  	return &timeoutClient{client, timeout, timeoutErr}
  1894  }
  1895  
  1896  type timeoutClient struct {
  1897  	inner      rpc.GenericClient
  1898  	timeout    time.Duration
  1899  	timeoutErr error
  1900  }
  1901  
  1902  var _ rpc.GenericClient = (*timeoutClient)(nil)
  1903  
  1904  func (t *timeoutClient) Call(ctx context.Context, method string, arg interface{},
  1905  	res interface{}, timeout time.Duration) error {
  1906  	if timeout == 0 {
  1907  		timeout = t.timeout
  1908  	}
  1909  	err := t.inner.Call(ctx, method, arg, res, timeout)
  1910  	if err == context.DeadlineExceeded {
  1911  		return t.timeoutErr
  1912  	}
  1913  	return err
  1914  }
  1915  
  1916  func (t *timeoutClient) CallCompressed(ctx context.Context, method string, arg interface{},
  1917  	res interface{}, ctype rpc.CompressionType, timeout time.Duration) error {
  1918  	if timeout == 0 {
  1919  		timeout = t.timeout
  1920  	}
  1921  	err := t.inner.CallCompressed(ctx, method, arg, res, ctype, timeout)
  1922  	if err == context.DeadlineExceeded {
  1923  		return t.timeoutErr
  1924  	}
  1925  	return err
  1926  }
  1927  
  1928  func (t *timeoutClient) Notify(ctx context.Context, method string, arg interface{}, timeout time.Duration) error {
  1929  	if timeout == 0 {
  1930  		timeout = t.timeout
  1931  	}
  1932  	err := t.inner.Notify(ctx, method, arg, timeout)
  1933  	if err == context.DeadlineExceeded {
  1934  		return t.timeoutErr
  1935  	}
  1936  	return err
  1937  }