// RegisterForUpdate implements the MDServer interface for MDServerRemote. func (md *MDServerRemote) RegisterForUpdate(ctx context.Context, id TlfID, currHead MetadataRevision) (<-chan error, error) { arg := keybase1.RegisterForUpdatesArg{ FolderID: id.String(), CurrRevision: currHead.Number(), LogTags: LogTagsFromContextToMap(ctx), } // register var c chan error err := md.conn.DoCommand(ctx, "register", func(rawClient rpc.GenericClient) error { // set up the server to receive updates, since we may // get disconnected between retries. server := md.conn.GetServer() err := server.Register(keybase1.MetadataUpdateProtocol(md)) if err != nil { if _, ok := err.(rpc.AlreadyRegisteredError); !ok { return err } } // TODO: Do something with server.Err() when server is // done? server.Run() // keep re-adding the observer on retries, since // disconnects or connection errors clear observers. func() { md.observerMu.Lock() defer md.observerMu.Unlock() if _, ok := md.observers[id]; ok { panic(fmt.Sprintf("Attempted double-registration for folder: %s", id)) } c = make(chan error, 1) md.observers[id] = c }() // Use this instead of md.client since we're already // inside a DoCommand(). c := keybase1.MetadataClient{Cli: rawClient} err = c.RegisterForUpdates(ctx, arg) if err != nil { func() { md.observerMu.Lock() defer md.observerMu.Unlock() // we could've been canceled by a shutdown so look this up // again before closing and deleting. if updateChan, ok := md.observers[id]; ok { close(updateChan) delete(md.observers, id) } }() } return err }) if err != nil { c = nil } return c, err }
// OnConnect implements the ConnectionHandler interface. func (md *MDServerRemote) OnConnect(ctx context.Context, conn *Connection, client keybase1.GenericClient, server *rpc.Server) error { md.log.Debug("MDServerRemote: OnConnect called with a new connection") // request a challenge -- using md.client here would cause problematic recursion. c := keybase1.MetadataClient{Cli: cancelableClient{client}} challenge, err := c.GetChallenge(ctx) if err != nil { md.log.Warning("MDServerRemote: challenge request error: %v", err) return err } md.log.Debug("MDServerRemote: received challenge") // get a new signature signature, err := md.authToken.Sign(ctx, challenge) if err != nil { md.log.Warning("MDServerRemote: error signing authentication token: %v", err) return err } md.log.Debug("MDServerRemote: authentication token signed") // authenticate pingIntervalSeconds, err := c.Authenticate(ctx, signature) if err != nil { md.log.Warning("MDServerRemote: authentication error: %v", err) return err } md.log.Debug("MDServerRemote: authentication successful; ping interval: %ds", pingIntervalSeconds) // we'll get replies asynchronously as to not block the connection // for doing other active work for the user. they will be sent to // the FolderNeedsRekey handler. if err := server.Register(keybase1.MetadataUpdateProtocol(md)); err != nil { if _, ok := err.(rpc.AlreadyRegisteredError); !ok { return err } } // request a list of folders needing rekey action if err := md.getFoldersForRekey(ctx, c); err != nil { md.log.Warning("MDServerRemote: getFoldersForRekey failed with %v", err) } md.log.Debug("MDServerRemote: requested list of folders for rekey") // start pinging md.resetPingTicker(pingIntervalSeconds) return nil }
// OnConnect implements the ConnectionHandler interface. func (md *MDServerRemote) OnConnect(ctx context.Context, conn *rpc.Connection, client rpc.GenericClient, server *rpc.Server) (err error) { defer func() { if err == nil { md.config.Reporter().Notify(ctx, connectionNotification(connectionStatusConnected)) } }() md.log.Debug("MDServerRemote: OnConnect called with a new connection") // we'll get replies asynchronously as to not block the connection // for doing other active work for the user. they will be sent to // the FolderNeedsRekey handler. if err := server.Register(keybase1.MetadataUpdateProtocol(md)); err != nil { if _, ok := err.(rpc.AlreadyRegisteredError); !ok { return err } } // reset auth -- using md.client here would cause problematic recursion. c := keybase1.MetadataClient{Cli: client} pingIntervalSeconds, err := md.resetAuth(ctx, c) switch err.(type) { case nil: case NoCurrentSessionError: default: return err } md.config.KBFSOps().PushConnectionStatusChange(MDServiceName, nil) // start pinging md.resetPingTicker(pingIntervalSeconds) return nil }