github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/tlfupgrade/tlfupgrade.go (about) 1 package tlfupgrade 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/keybase/client/go/libkb" 10 "github.com/keybase/client/go/protocol/chat1" 11 "github.com/keybase/client/go/protocol/gregor1" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/keybase/clockwork" 14 ) 15 16 type BackgroundTLFUpdater struct { 17 libkb.Contextified 18 sync.Mutex 19 20 initialWait time.Duration 21 errWait time.Duration 22 successWait time.Duration 23 clock clockwork.Clock 24 shutdownCh chan struct{} 25 running bool 26 27 // testing 28 testingAPIServer libkb.API 29 testingChatHelper libkb.ChatHelper 30 testingDisableKBFS bool 31 upgradeCh *chan keybase1.TLFID 32 } 33 34 func NewBackgroundTLFUpdater(g *libkb.GlobalContext) *BackgroundTLFUpdater { 35 b := &BackgroundTLFUpdater{ 36 Contextified: libkb.NewContextified(g), 37 initialWait: 10 * time.Second, 38 errWait: 20 * time.Second, 39 successWait: 20 * time.Second, 40 shutdownCh: make(chan struct{}), 41 clock: clockwork.NewRealClock(), 42 } 43 g.PushShutdownHook(b.Shutdown) 44 return b 45 } 46 47 func (b *BackgroundTLFUpdater) debug(ctx context.Context, msg string, args ...interface{}) { 48 b.G().Log.CDebugf(ctx, "BackgroundTLFUpdater: %s", fmt.Sprintf(msg, args...)) 49 } 50 51 func (b *BackgroundTLFUpdater) api() libkb.API { 52 if b.testingAPIServer != nil { 53 return b.testingAPIServer 54 } 55 return b.G().API 56 } 57 58 func (b *BackgroundTLFUpdater) chat() libkb.ChatHelper { 59 if b.testingChatHelper != nil { 60 return b.testingChatHelper 61 } 62 return b.G().ChatHelper 63 } 64 65 func (b *BackgroundTLFUpdater) Run() { 66 b.runAll() 67 } 68 69 func (b *BackgroundTLFUpdater) runAll() { 70 b.Lock() 71 defer b.Unlock() 72 if b.G().IsMobileAppType() { 73 b.debug(context.Background(), "tlf updater disabled on mobile") 74 return 75 } 76 uid := b.G().Env.GetUID() 77 if uid.IsNil() { 78 b.debug(context.Background(), "not logged in, not starting") 79 return 80 } 81 if !b.running && b.G().Env.GetChatMemberType() != "kbfs" { 82 b.debug(context.Background(), "starting up") 83 b.shutdownCh = make(chan struct{}) 84 b.running = true 85 go b.runAppType(keybase1.TeamApplication_CHAT) 86 if !b.testingDisableKBFS { 87 go b.runAppType(keybase1.TeamApplication_KBFS) 88 } 89 } 90 } 91 92 func (b *BackgroundTLFUpdater) Shutdown(mctx libkb.MetaContext) error { 93 b.Lock() 94 defer b.Unlock() 95 if b.running { 96 b.debug(mctx.Ctx(), "shutting down") 97 b.running = false 98 close(b.shutdownCh) 99 } 100 return nil 101 } 102 103 func (b *BackgroundTLFUpdater) runAppType(appType keybase1.TeamApplication) { 104 var tlf *GetTLFForUpgradeAvailableRes 105 ctx := context.Background() 106 nextTime := b.deadline(b.initialWait) 107 for { 108 b.debug(ctx, "runAppType(%v): waiting until %v", appType, nextTime) 109 select { 110 case <-b.shutdownCh: 111 b.debug(ctx, "runAppType(%v): shutdown", appType) 112 return 113 case <-b.clock.AfterTime(nextTime): 114 b.debug(ctx, "runAppType(%v): woken up", appType) 115 tlf, nextTime = b.getTLFToUpgrade(ctx, appType) 116 if tlf != nil { 117 b.upgradeTLF(ctx, tlf.TlfName, tlf.TlfID, tlf.IsPublic, appType) 118 } 119 } 120 } 121 } 122 123 type getUpgradeRes struct { 124 Status libkb.AppStatus `json:"status"` 125 GetTLFForUpgradeRes 126 } 127 128 func (r *getUpgradeRes) GetAppStatus() *libkb.AppStatus { 129 return &r.Status 130 } 131 132 func (b *BackgroundTLFUpdater) deadline(d time.Duration) time.Time { 133 return b.clock.Now().Add(d) 134 } 135 136 func (b *BackgroundTLFUpdater) getTLFToUpgrade(ctx context.Context, appType keybase1.TeamApplication) (*GetTLFForUpgradeAvailableRes, time.Time) { 137 mctx := libkb.NewMetaContext(ctx, b.G()) 138 if !b.G().ActiveDevice.HaveKeys() { 139 return nil, time.Now().Add(time.Minute) 140 } 141 arg := libkb.NewAPIArg("kbfs/upgrade") 142 arg.Args = libkb.NewHTTPArgs() 143 arg.SessionType = libkb.APISessionTypeREQUIRED 144 arg.Args.Add("app_type", libkb.I{Val: int(appType)}) 145 var res getUpgradeRes 146 if err := b.api().GetDecode(mctx, arg, &res); err != nil { 147 b.debug(ctx, "getTLFToUpgrade: API fail: %s", err) 148 return nil, b.deadline(b.errWait) 149 } 150 typ, err := res.Typ() 151 if err != nil { 152 b.debug(ctx, "getTLFToUpgrade: failed to get typ: %s", err) 153 return nil, b.deadline(b.errWait) 154 } 155 switch typ { 156 case GetTLFForUpgradeResType_TLFAVAILABLE: 157 tlf := res.Tlfavailable() 158 b.debug(ctx, "getTLFUpgrade: found TLF to upgrade: %s apptype: %v", tlf.TlfID, appType) 159 return &tlf, b.deadline(b.successWait) 160 case GetTLFForUpgradeResType_DELAY: 161 b.debug(ctx, "getTLFUpgrade: delayed: reason: %s delay: %v", res.Delay().Reason, res.Delay().Delay) 162 return nil, gregor1.FromTime(res.Delay().Delay) 163 case GetTLFForUpgradeResType_DISABLED: 164 b.debug(ctx, "getTLFUpgrade: disabled: delay: %v", res.Disabled().Delay) 165 return nil, gregor1.FromTime(res.Disabled().Delay) 166 case GetTLFForUpgradeResType_ERR: 167 b.debug(ctx, "getTLFUpgrade: server err: %s delay: %v", res.Err().Error, res.Err().Delay) 168 return nil, gregor1.FromTime(res.Err().Delay) 169 default: 170 b.debug(ctx, "getTLFUpgrade: unknown result type: %v", typ) 171 } 172 return nil, b.deadline(b.errWait) 173 } 174 175 func (b *BackgroundTLFUpdater) upgradeTLF(ctx context.Context, tlfName string, tlfID keybase1.TLFID, 176 public bool, appType keybase1.TeamApplication) { 177 switch appType { 178 case keybase1.TeamApplication_CHAT: 179 b.upgradeTLFForChat(ctx, tlfName, tlfID, public) 180 case keybase1.TeamApplication_KBFS: 181 if err := UpgradeTLFForKBFS(ctx, b.G(), tlfName, public); err != nil { 182 b.debug(ctx, "upgradeTLF: KBFS upgrade failed: %s", err) 183 } 184 default: 185 b.debug(ctx, "upgradeTLF: unknown app type: %v", appType) 186 } 187 } 188 189 func (b *BackgroundTLFUpdater) upgradeTLFForChat(ctx context.Context, tlfName string, tlfID keybase1.TLFID, 190 public bool) { 191 defer func() { 192 if b.upgradeCh != nil { 193 *b.upgradeCh <- tlfID 194 } 195 }() 196 chatTLFID, err := chat1.MakeTLFID(tlfID.String()) 197 if err != nil { 198 b.debug(ctx, "upgradeTLFForChat: invalid TLFID: %s", err) 199 return 200 } 201 if err := b.chat().UpgradeKBFSToImpteam(ctx, tlfName, chatTLFID, public); err != nil { 202 b.debug(ctx, "upgradeTLFForChat: failed to upgrade TLFID for chat: tlfID: %v err: %s", tlfID, err) 203 return 204 } 205 }