1package globals 2 3import ( 4 "context" 5 "sync" 6 7 "github.com/keybase/client/go/chat/types" 8 "github.com/keybase/client/go/libkb" 9 "github.com/keybase/client/go/protocol/chat1" 10 "github.com/keybase/client/go/protocol/keybase1" 11 "github.com/keybase/go-framed-msgpack-rpc/rpc" 12) 13 14type keyfinderKey int 15type identifyNotifierKey int 16type chatTrace int 17type identifyModeKey int 18type upakfinderKey int 19type rateLimitKey int 20type nameInfoOverride int 21type localizerCancelableKeyTyp int 22type messageSkipsKeyTyp int 23type unboxModeKeyTyp int 24type emojiHarvesterKeyTyp int 25type ctxMutexKeyTyp int 26 27var kfKey keyfinderKey 28var inKey identifyNotifierKey 29var chatTraceKey chatTrace 30var identModeKey identifyModeKey 31var upKey upakfinderKey 32var rlKey rateLimitKey 33var nameInfoOverrideKey nameInfoOverride 34var localizerCancelableKey localizerCancelableKeyTyp 35var messageSkipsKey messageSkipsKeyTyp 36var unboxModeKey unboxModeKeyTyp 37var emojiHarvesterKey emojiHarvesterKeyTyp 38var ctxMutexKey ctxMutexKeyTyp 39 40type identModeData struct { 41 mode keybase1.TLFIdentifyBehavior 42 breaks *[]keybase1.TLFIdentifyFailure 43} 44 45func CtxKeyFinder(ctx context.Context, g *Context) types.KeyFinder { 46 if kf, ok := ctx.Value(kfKey).(types.KeyFinder); ok { 47 return kf 48 } 49 return g.CtxFactory.NewKeyFinder() 50} 51 52func CtxUPAKFinder(ctx context.Context, g *Context) types.UPAKFinder { 53 if up, ok := ctx.Value(upKey).(types.UPAKFinder); ok { 54 return up 55 } 56 return g.CtxFactory.NewUPAKFinder() 57} 58 59func CtxIdentifyMode(ctx context.Context) (ib keybase1.TLFIdentifyBehavior, breaks *[]keybase1.TLFIdentifyFailure, ok bool) { 60 if imd, ok := ctx.Value(identModeKey).(identModeData); ok { 61 return imd.mode, imd.breaks, ok 62 } 63 return keybase1.TLFIdentifyBehavior_CHAT_CLI, nil, false 64} 65 66func CtxAddIdentifyMode(ctx context.Context, mode keybase1.TLFIdentifyBehavior, 67 breaks *[]keybase1.TLFIdentifyFailure) context.Context { 68 if mode == keybase1.TLFIdentifyBehavior_UNSET { 69 mode = keybase1.TLFIdentifyBehavior_CHAT_CLI 70 } 71 return context.WithValue(ctx, identModeKey, identModeData{mode: mode, breaks: breaks}) 72} 73 74func CtxIdentifyNotifier(ctx context.Context) types.IdentifyNotifier { 75 if in, ok := ctx.Value(inKey).(types.IdentifyNotifier); ok { 76 return in 77 } 78 return nil 79} 80 81func CtxModifyIdentifyNotifier(ctx context.Context, notifier types.IdentifyNotifier) context.Context { 82 return context.WithValue(ctx, inKey, notifier) 83} 84 85func CtxAddRateLimit(ctx context.Context, rl []chat1.RateLimit) { 86 if l, ok := ctx.Value(ctxMutexKey).(*sync.RWMutex); ok { 87 l.Lock() 88 defer l.Unlock() 89 if existingRL, ok := ctx.Value(rlKey).(map[string]chat1.RateLimit); ok { 90 for _, r := range rl { 91 existingRL[r.Name] = r 92 } 93 } 94 } 95} 96 97func CtxRateLimits(ctx context.Context) (res []chat1.RateLimit) { 98 if l, ok := ctx.Value(ctxMutexKey).(*sync.RWMutex); ok { 99 l.RLock() 100 defer l.RUnlock() 101 if existingRL, ok := ctx.Value(rlKey).(map[string]chat1.RateLimit); ok { 102 for _, rl := range existingRL { 103 res = append(res, rl) 104 } 105 } 106 } 107 return res 108} 109 110func CtxAddMessageCacheSkips(ctx context.Context, convID chat1.ConversationID, msgs []chat1.MessageUnboxed) { 111 if l, ok := ctx.Value(ctxMutexKey).(*sync.RWMutex); ok { 112 l.Lock() 113 defer l.Unlock() 114 if existingSkips, ok := ctx.Value(messageSkipsKey).(map[chat1.ConvIDStr]MessageCacheSkip); ok { 115 existingSkips[convID.ConvIDStr()] = MessageCacheSkip{ 116 ConvID: convID, 117 Msgs: append(existingSkips[convID.ConvIDStr()].Msgs, msgs...), 118 } 119 } 120 } 121} 122 123type MessageCacheSkip struct { 124 ConvID chat1.ConversationID 125 Msgs []chat1.MessageUnboxed 126} 127 128func CtxMessageCacheSkips(ctx context.Context) (res []MessageCacheSkip) { 129 if l, ok := ctx.Value(ctxMutexKey).(*sync.RWMutex); ok { 130 l.RLock() 131 defer l.RUnlock() 132 if existingSkips, ok := ctx.Value(messageSkipsKey).(map[chat1.ConvIDStr]MessageCacheSkip); ok { 133 for _, skips := range existingSkips { 134 res = append(res, skips) 135 } 136 } 137 } 138 return res 139} 140 141func CtxModifyUnboxMode(ctx context.Context, unboxMode types.UnboxMode) context.Context { 142 return context.WithValue(ctx, unboxModeKey, unboxMode) 143} 144 145func CtxUnboxMode(ctx context.Context) types.UnboxMode { 146 if unboxMode, ok := ctx.Value(unboxModeKey).(types.UnboxMode); ok { 147 return unboxMode 148 } 149 return types.UnboxModeFull 150} 151 152func CtxOverrideNameInfoSource(ctx context.Context) (types.NameInfoSource, bool) { 153 if ni, ok := ctx.Value(nameInfoOverrideKey).(types.NameInfoSource); ok { 154 return ni, true 155 } 156 return nil, false 157} 158 159func CtxAddOverrideNameInfoSource(ctx context.Context, ni types.NameInfoSource) context.Context { 160 return context.WithValue(ctx, nameInfoOverrideKey, ni) 161} 162 163func CtxTrace(ctx context.Context) (string, bool) { 164 if trace, ok := ctx.Value(chatTraceKey).(string); ok { 165 return trace, true 166 } 167 return "", false 168} 169 170func CtxAddLogTags(ctx context.Context, g *Context) context.Context { 171 172 // Add trace context value 173 trace := libkb.RandStringB64(3) 174 ctx = context.WithValue(ctx, chatTraceKey, trace) 175 176 // Add log tags 177 ctx = libkb.WithLogTagWithValue(ctx, "chat-trace", trace) 178 179 rpcTags := make(map[string]interface{}) 180 rpcTags["user-agent"] = libkb.UserAgent 181 rpcTags["platform"] = libkb.GetPlatformString() 182 rpcTags["apptype"] = g.GetAppType() 183 ctx = rpc.AddRpcTagsToContext(ctx, rpcTags) 184 185 return ctx 186} 187 188func IsLocalizerCancelableCtx(ctx context.Context) bool { 189 if bval, ok := ctx.Value(localizerCancelableKey).(bool); ok && bval { 190 return true 191 } 192 return false 193} 194 195func CtxAddLocalizerCancelable(ctx context.Context) context.Context { 196 return context.WithValue(ctx, localizerCancelableKey, true) 197} 198 199func CtxRemoveLocalizerCancelable(ctx context.Context) context.Context { 200 if IsLocalizerCancelableCtx(ctx) { 201 return context.WithValue(ctx, localizerCancelableKey, false) 202 } 203 return ctx 204} 205 206func IsEmojiHarvesterCtx(ctx context.Context) bool { 207 if bval, ok := ctx.Value(emojiHarvesterKey).(bool); ok && bval { 208 return true 209 } 210 return false 211} 212 213func CtxMakeEmojiHarvester(ctx context.Context) context.Context { 214 return context.WithValue(ctx, emojiHarvesterKey, true) 215} 216 217func ChatCtx(ctx context.Context, g *Context, mode keybase1.TLFIdentifyBehavior, 218 breaks *[]keybase1.TLFIdentifyFailure, notifier types.IdentifyNotifier) context.Context { 219 if breaks == nil { 220 breaks = new([]keybase1.TLFIdentifyFailure) 221 } 222 res := ctx 223 if _, _, ok := CtxIdentifyMode(res); !ok { 224 res = CtxAddIdentifyMode(res, mode, breaks) 225 } 226 if _, ok := res.Value(kfKey).(types.KeyFinder); !ok { 227 res = context.WithValue(res, kfKey, g.CtxFactory.NewKeyFinder()) 228 } 229 if _, ok := res.Value(inKey).(types.IdentifyNotifier); !ok { 230 res = context.WithValue(res, inKey, notifier) 231 } 232 if _, ok := res.Value(upKey).(types.UPAKFinder); !ok { 233 res = context.WithValue(res, upKey, g.CtxFactory.NewUPAKFinder()) 234 } 235 if _, ok := res.Value(ctxMutexKey).(*sync.RWMutex); !ok { 236 res = context.WithValue(res, ctxMutexKey, &sync.RWMutex{}) 237 } 238 if _, ok := res.Value(rlKey).(map[string]chat1.RateLimit); !ok { 239 res = context.WithValue(res, rlKey, make(map[string]chat1.RateLimit)) 240 } 241 if _, ok := res.Value(messageSkipsKey).(map[chat1.ConvIDStr]MessageCacheSkip); !ok { 242 res = context.WithValue(res, messageSkipsKey, make(map[chat1.ConvIDStr]MessageCacheSkip)) 243 } 244 if _, ok := res.Value(unboxModeKey).(types.UnboxMode); !ok { 245 res = context.WithValue(res, unboxModeKey, types.UnboxModeFull) 246 } 247 if _, ok := CtxTrace(res); !ok { 248 res = CtxAddLogTags(res, g) 249 } 250 return res 251} 252 253func BackgroundChatCtx(sourceCtx context.Context, g *Context) context.Context { 254 rctx := libkb.CopyTagsToBackground(sourceCtx) 255 256 in := CtxIdentifyNotifier(sourceCtx) 257 if ident, breaks, ok := CtxIdentifyMode(sourceCtx); ok { 258 rctx = ChatCtx(rctx, g, ident, breaks, in) 259 } 260 261 // Overwrite trace tag 262 if tr, ok := sourceCtx.Value(chatTraceKey).(string); ok { 263 rctx = context.WithValue(rctx, chatTraceKey, tr) 264 } 265 266 if ni, ok := CtxOverrideNameInfoSource(sourceCtx); ok { 267 rctx = CtxAddOverrideNameInfoSource(rctx, ni) 268 } 269 rctx = context.WithValue(rctx, kfKey, CtxKeyFinder(sourceCtx, g)) 270 rctx = context.WithValue(rctx, upKey, CtxUPAKFinder(sourceCtx, g)) 271 rctx = context.WithValue(rctx, inKey, in) 272 rctx = libkb.WithLogTag(rctx, "CHTBKG") 273 if IsLocalizerCancelableCtx(sourceCtx) { 274 rctx = CtxAddLocalizerCancelable(rctx) 275 } 276 if IsEmojiHarvesterCtx(sourceCtx) { 277 rctx = CtxMakeEmojiHarvester(rctx) 278 } 279 return rctx 280} 281