1// Copyright 2015 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4package libkb 5 6import ( 7 "encoding/json" 8 "fmt" 9 "regexp" 10 "strconv" 11 "strings" 12 13 "github.com/keybase/client/go/msgpack" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 jsonw "github.com/keybase/go-jsonw" 16) 17 18const ( 19 DBUser = 0x00 20 DBSig = 0x0f 21 DBTeamChain = 0x10 22 DBUserPlusAllKeysV1 = 0x19 23 24 DBIncomingSharePreference = 0xa4 25 DBChatUserEmojis = 0xa5 26 DBChatInboxIndex = 0xa6 27 DBChatInboxConvs = 0xa7 28 DBChatParticipants = 0xa8 29 DBOpenTeams = 0xa9 30 DBNetworkInstrumentation = 0xaa 31 DBFeaturedBots = 0xab 32 DBChatEphemeralTracker = 0xac 33 DBLoginTimes = 0xad 34 DBChatJourney = 0xae 35 DBTeamRoleMap = 0xaf 36 DBMisc = 0xb0 37 DBTeamMerkleCheck = 0xb1 38 DBUidToServiceMap = 0xb2 39 DBChatPinIgnore = 0xb3 40 DBTeambotKey = 0xb4 41 DBTeambotKeyWrongKID = 0xb5 42 DBChatBotCommands = 0xb6 43 DBSavedContacts = 0xb7 44 DBChatLocation = 0xb8 45 DBHiddenChainStorage = 0xb9 46 DBContactResolution = 0xba 47 DBBoxAuditorPermanent = 0xbb 48 DBBoxAuditor = 0xbc 49 DBUserPlusKeysVersionedUnstubbed = 0xbd 50 DBOfflineRPC = 0xbe 51 DBChatCollapses = 0xbf 52 DBSupportsHiddenFlagStorage = 0xc0 53 DBMerkleAudit = 0xca 54 DBUnfurler = 0xcb 55 DBStellarDisclaimer = 0xcc 56 DBFTLStorage = 0xcd 57 DBTeamAuditor = 0xce 58 DBAttachmentUploader = 0xcf 59 DBLegacyHasRandomPW = 0xd0 60 DBDiskLRUEntries = 0xda 61 DBDiskLRUIndex = 0xdb 62 DBImplicitTeamConflictInfo = 0xdc 63 DBUidToFullName = 0xdd 64 DBUidToUsername = 0xde 65 DBUserPlusKeysVersioned = 0xdf 66 DBLink = 0xe0 67 DBLocalTrack = 0xe1 68 DBPGPKey = 0xe3 69 DBSigHints = 0xe4 70 DBProofCheck = 0xe5 71 DBUserSecretKeys = 0xe6 72 DBSigChainTailPublic = 0xe7 73 DBSigChainTailSemiprivate = 0xe8 74 DBSigChainTailEncrypted = 0xe9 75 DBChatActive = 0xea 76 DBUserEKBox = 0xeb 77 DBTeamEKBox = 0xec 78 DBChatIndex = 0xed 79 DBChatReacji = 0xef 80 DBMerkleRoot = 0xf0 81 DBTrackers = 0xf1 82 DBGregor = 0xf2 83 DBUnverifiedTrackersFollowers = 0xf3 84 DBUnverifiedTrackersFollowing = 0xf4 85 DBNotificationDismiss = 0xf5 86 DBChatBlockIndex = 0xf6 87 DBChatBlocks = 0xf7 88 DBChatOutbox = 0xf8 89 DBChatInbox = 0xf9 90 DBIdentify = 0xfa 91 DBResolveUsernameToUID = 0xfb 92 DBChatBodyHashIndex = 0xfc 93 DBMerkleStore = 0xfd 94 DBChatConvFailures = 0xfe 95 DBTeamList = 0xff 96) 97 98// Note(maxtaco) 2018.10.08 --- Note a bug here, that we used the `libkb.DBChatInbox` type here. 99// That's a copy-paste bug, but we get away with it since we have a `tid:` prefix that 100// disambiguates these entries from true Chat entries. We're not going to fix it now 101// since it would kill the team cache, but sometime in the future we should fix it. 102const ( 103 DBSlowTeamsAlias = DBChatInbox 104) 105 106const ( 107 DBLookupUsername = 0x00 108 // was once used to store latest merkle root with Key:"HEAD" 109 DBLookupMerkleRoot = 0x01 110) 111 112func DbKeyUID(t ObjType, uid keybase1.UID) DbKey { 113 return DbKey{Typ: t, Key: uid.String()} 114} 115 116func DbKeyNotificationDismiss(prefix string, username NormalizedUsername) DbKey { 117 return DbKey{ 118 Typ: DBNotificationDismiss, 119 Key: fmt.Sprintf("%s:%s", prefix, username), 120 } 121} 122 123// IsPermDbKey returns true for keys ignored by the leveldb cleaner and always 124// persisted to disk. Ideally these keys handling some cleanup/size bounding 125// themselves. 126func IsPermDbKey(typ ObjType) bool { 127 switch typ { 128 case DBDiskLRUEntries, 129 DBDiskLRUIndex, 130 DBOfflineRPC, 131 DBChatCollapses, 132 DBLegacyHasRandomPW, 133 DBChatReacji, 134 DBStellarDisclaimer, 135 DBChatIndex, 136 DBBoxAuditorPermanent, 137 DBSavedContacts, 138 DBContactResolution, 139 DBTeambotKeyWrongKID, 140 DBMisc, 141 DBIncomingSharePreference: 142 return true 143 default: 144 return false 145 } 146} 147 148type ObjType byte 149 150type DbKey struct { 151 Typ ObjType 152 Key string 153} 154 155// tablePrefix builds a key prefix for the given table for use in `util.Range` 156// or `util.BytesPrefix` 157func tablePrefix(table string) []byte { 158 return []byte(fmt.Sprintf("%s:", table)) 159} 160 161func prefixStringWithTable(table string, typ ObjType) string { 162 return fmt.Sprintf("%s:%02x", table, typ) 163} 164 165func PrefixString(typ ObjType) string { 166 return prefixStringWithTable(typ.table(), typ) 167} 168 169func (t ObjType) table() string { 170 if IsPermDbKey(t) { 171 return levelDbTablePerm 172 } 173 return levelDbTableKv 174} 175 176func (k DbKey) toBytes(prefixString string) []byte { 177 return []byte(fmt.Sprintf("%s:%s", prefixString, k.Key)) 178} 179 180func (k DbKey) ToBytes() []byte { 181 return k.toBytes(PrefixString(k.Typ)) 182} 183 184func (k DbKey) ToBytesLookup() []byte { 185 return k.toBytes(prefixStringWithTable(levelDbTableLo, k.Typ)) 186} 187 188var fieldExp = regexp.MustCompile(`[a-f0-9]{2}`) 189 190func DbKeyParse(s string) (string, DbKey, error) { 191 v := strings.Split(s, ":") 192 if len(v) < 3 { 193 return "", DbKey{}, fmt.Errorf("expected 3 colon-separated fields, found %d", len(v)) 194 } 195 196 if !fieldExp.MatchString(v[1]) { 197 return "", DbKey{}, fmt.Errorf("2nd field should be a 1-byte hex string") 198 } 199 200 b, err := strconv.ParseUint(v[1], 16, 8) 201 if err != nil { 202 return "", DbKey{}, err 203 } 204 dbKey := DbKey{ 205 Typ: ObjType(b), 206 Key: strings.Join(v[2:], ":"), 207 } 208 return v[0], dbKey, nil 209} 210 211func jsonLocalDbPut(ops LocalDbOps, id DbKey, aliases []DbKey, val *jsonw.Wrapper) error { 212 bytes, err := val.Marshal() 213 if err == nil { 214 err = ops.Put(id, aliases, bytes) 215 } 216 return err 217} 218func jsonLocalDbGet(ops LocalDbOps, id DbKey) (*jsonw.Wrapper, error) { 219 bytes, found, err := ops.Get(id) 220 var ret *jsonw.Wrapper 221 if found { 222 ret, err = jsonw.Unmarshal(bytes) 223 } 224 return ret, err 225} 226 227func jsonLocalDbGetInto(ops LocalDbOps, obj interface{}, id DbKey) (found bool, err error) { 228 var buf []byte 229 buf, found, err = ops.Get(id) 230 if err == nil && found { 231 err = jsonw.EnsureMaxDepthBytesDefault(buf) 232 if err != nil { 233 return found, err 234 } 235 err = json.Unmarshal(buf, &obj) 236 } 237 return found, err 238} 239 240func jsonLocalDbPutObj(ops LocalDbOps, id DbKey, aliases []DbKey, obj interface{}) (err error) { 241 var bytes []byte 242 bytes, err = json.Marshal(obj) 243 if err == nil { 244 err = ops.Put(id, aliases, bytes) 245 } 246 return err 247} 248 249func jsonLocalDbLookup(ops LocalDbOps, id DbKey) (*jsonw.Wrapper, error) { 250 bytes, found, err := ops.Lookup(id) 251 var ret *jsonw.Wrapper 252 if found { 253 ret, err = jsonw.Unmarshal(bytes) 254 } 255 return ret, err 256} 257 258func jsonLocalDbLookupIntoMsgpack(ops LocalDbOps, obj interface{}, alias DbKey) (found bool, err error) { 259 var buf []byte 260 buf, found, err = ops.Lookup(alias) 261 if err != nil || !found { 262 return found, err 263 } 264 err = msgpack.Decode(obj, buf) 265 return true, err 266} 267 268func jsonLocalDbGetIntoMsgpack(ops LocalDbOps, obj interface{}, id DbKey) (found bool, err error) { 269 var buf []byte 270 buf, found, err = ops.Get(id) 271 if err != nil || !found { 272 return found, err 273 } 274 err = msgpack.Decode(obj, buf) 275 return true, err 276} 277 278func jsonLocalDbPutObjMsgpack(ops LocalDbOps, id DbKey, aliases []DbKey, obj interface{}) error { 279 bytes, err := msgpack.Encode(obj) 280 if err != nil { 281 return err 282 } 283 return ops.Put(id, aliases, bytes) 284} 285 286type JSONLocalDb struct { 287 engine LocalDb 288} 289 290func NewJSONLocalDb(e LocalDb) *JSONLocalDb { return &JSONLocalDb{e} } 291func (j *JSONLocalDb) Open() error { return j.engine.Open() } 292func (j *JSONLocalDb) ForceOpen() error { return j.engine.ForceOpen() } 293func (j *JSONLocalDb) Close() error { return j.engine.Close() } 294func (j *JSONLocalDb) Nuke() (string, error) { return j.engine.Nuke() } 295func (j *JSONLocalDb) Clean(force bool) error { return j.engine.Clean(force) } 296func (j *JSONLocalDb) Stats() string { return j.engine.Stats() } 297func (j *JSONLocalDb) CompactionStats() (bool, bool, error) { 298 return j.engine.CompactionStats() 299} 300func (j *JSONLocalDb) KeysWithPrefixes(prefixes ...[]byte) (DBKeySet, error) { 301 return j.engine.KeysWithPrefixes(prefixes...) 302} 303 304func (j *JSONLocalDb) PutRaw(id DbKey, b []byte) error { return j.engine.Put(id, nil, b) } 305func (j *JSONLocalDb) GetRaw(id DbKey) ([]byte, bool, error) { return j.engine.Get(id) } 306func (j *JSONLocalDb) Delete(id DbKey) error { return j.engine.Delete(id) } 307 308func (j *JSONLocalDb) Put(id DbKey, aliases []DbKey, val *jsonw.Wrapper) error { 309 return jsonLocalDbPut(j.engine, id, aliases, val) 310} 311 312func (j *JSONLocalDb) Get(id DbKey) (*jsonw.Wrapper, error) { 313 return jsonLocalDbGet(j.engine, id) 314} 315 316func (j *JSONLocalDb) GetInto(obj interface{}, id DbKey) (found bool, err error) { 317 return jsonLocalDbGetInto(j.engine, obj, id) 318} 319 320func (j *JSONLocalDb) PutObj(id DbKey, aliases []DbKey, obj interface{}) (err error) { 321 return jsonLocalDbPutObj(j.engine, id, aliases, obj) 322} 323 324func (j *JSONLocalDb) Lookup(id DbKey) (*jsonw.Wrapper, error) { 325 return jsonLocalDbLookup(j.engine, id) 326} 327 328func (j *JSONLocalDb) LookupIntoMsgpack(obj interface{}, alias DbKey) (found bool, err error) { 329 return jsonLocalDbLookupIntoMsgpack(j.engine, obj, alias) 330} 331 332func (j *JSONLocalDb) GetIntoMsgpack(obj interface{}, id DbKey) (found bool, err error) { 333 return jsonLocalDbGetIntoMsgpack(j.engine, obj, id) 334} 335 336func (j *JSONLocalDb) PutObjMsgpack(id DbKey, aliases []DbKey, obj interface{}) (err error) { 337 return jsonLocalDbPutObjMsgpack(j.engine, id, aliases, obj) 338} 339 340func (j *JSONLocalDb) OpenTransaction() (JSONLocalDbTransaction, error) { 341 var ( 342 jtr JSONLocalDbTransaction 343 err error 344 ) 345 if jtr.tr, err = j.engine.OpenTransaction(); err != nil { 346 return JSONLocalDbTransaction{}, err 347 } 348 return jtr, nil 349} 350 351func (j *JSONLocalDb) GetEngine() LocalDb { 352 return j.engine 353} 354 355type JSONLocalDbTransaction struct { 356 tr LocalDbTransaction 357} 358 359func (j JSONLocalDbTransaction) PutRaw(id DbKey, b []byte) error { return j.tr.Put(id, nil, b) } 360func (j JSONLocalDbTransaction) GetRaw(id DbKey) ([]byte, bool, error) { return j.tr.Get(id) } 361func (j JSONLocalDbTransaction) Delete(id DbKey) error { return j.tr.Delete(id) } 362 363func (j JSONLocalDbTransaction) Put(id DbKey, aliases []DbKey, val *jsonw.Wrapper) error { 364 return jsonLocalDbPut(j.tr, id, aliases, val) 365} 366 367func (j JSONLocalDbTransaction) Get(id DbKey) (*jsonw.Wrapper, error) { 368 return jsonLocalDbGet(j.tr, id) 369} 370 371func (j JSONLocalDbTransaction) GetInto(obj interface{}, id DbKey) (found bool, err error) { 372 return jsonLocalDbGetInto(j.tr, obj, id) 373} 374 375func (j JSONLocalDbTransaction) PutObj(id DbKey, aliases []DbKey, obj interface{}) (err error) { 376 return jsonLocalDbPutObj(j.tr, id, aliases, obj) 377} 378 379func (j JSONLocalDbTransaction) Lookup(id DbKey) (*jsonw.Wrapper, error) { 380 return jsonLocalDbLookup(j.tr, id) 381} 382 383func (j JSONLocalDbTransaction) Commit() error { 384 return j.tr.Commit() 385} 386 387func (j JSONLocalDbTransaction) Discard() { 388 j.tr.Discard() 389} 390