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	"fmt"
8	"strconv"
9	"strings"
10	"sync"
11	"time"
12
13	keybase1 "github.com/keybase/client/go/protocol/keybase1"
14	jsonw "github.com/keybase/go-jsonw"
15)
16
17type UserConfigWrapper struct {
18	userConfig *UserConfig
19	sync.Mutex
20}
21
22type JSONConfigFile struct {
23	*JSONFile
24	userConfigWrapper *UserConfigWrapper
25}
26
27var _ (ConfigReader) = (*JSONConfigFile)(nil)
28
29func NewJSONConfigFile(g *GlobalContext, s string) *JSONConfigFile {
30	return &JSONConfigFile{NewJSONFile(g, s, "config"), &UserConfigWrapper{}}
31}
32
33// Check looks inside the JSON file to see if any fields are poorly specified
34func (f *JSONConfigFile) Check() error {
35	return PickFirstError(
36		// Feel free to add others here..
37		func() error {
38			_, err := f.GetRunMode()
39			return err
40		}(),
41	)
42}
43
44func (f *JSONConfigFile) GetDurationAtPath(p string) (time.Duration, bool) {
45	s, ok := f.GetStringAtPath(p)
46	if !ok {
47		return 0, false
48	}
49	d, err := time.ParseDuration(s)
50	if err != nil {
51		f.G().Log.Warning("invalid time duration in config file: %s => %s", p, s)
52		return 0, false
53	}
54	return d, true
55}
56
57func (f *JSONConfigFile) GetTopLevelString(s string) (ret string) {
58	var e error
59	f.jw.AtKey(s).GetStringVoid(&ret, &e)
60	f.G().VDL.Log(VLog1, "Config: mapping %q -> %q", s, ret)
61	return
62}
63
64func (f *JSONConfigFile) GetTopLevelBool(s string) (res, isSet bool) {
65	if w := f.jw.AtKey(s); !w.IsNil() {
66		isSet = true
67		var e error
68		w.GetBoolVoid(&res, &e)
69	}
70	return
71}
72
73func (f *JSONConfigFile) GetUserConfig() (*UserConfig, error) {
74	f.userConfigWrapper.Lock()
75	defer f.userConfigWrapper.Unlock()
76	return f.getUserConfigWithLock()
77}
78
79// GetUserConfig looks for the `current_user` field to see if there's
80// a corresponding user object in the `users` table. There really should be.
81func (f *JSONConfigFile) getUserConfigWithLock() (ret *UserConfig, err error) {
82	var s string
83	if ret = f.userConfigWrapper.userConfig; ret != nil {
84		return
85	}
86	if s, err = f.jw.AtKey("current_user").GetString(); err != nil {
87		return
88	}
89	nu := NewNormalizedUsername(s)
90	if ret, err = f.GetUserConfigForUsername(nu); err != nil {
91		return
92	} else if ret != nil {
93		f.userConfigWrapper.userConfig = ret
94	} else {
95		err = ConfigError{f.filename,
96			fmt.Sprintf("Didn't find a UserConfig for %s", s)}
97	}
98	return
99}
100
101func (f *JSONConfigFile) GetDeviceIDForUsername(nu NormalizedUsername) keybase1.DeviceID {
102	f.userConfigWrapper.Lock()
103	defer f.userConfigWrapper.Unlock()
104	ret, err := f.GetUserConfigForUsername(nu)
105	var empty keybase1.DeviceID
106	if err != nil {
107		return empty
108	}
109	return ret.GetDeviceID()
110}
111
112func (f *JSONConfigFile) GetPassphraseStateForUsername(nu NormalizedUsername) (ret *keybase1.PassphraseState) {
113	f.userConfigWrapper.Lock()
114	defer f.userConfigWrapper.Unlock()
115	userConfig, err := f.GetUserConfigForUsername(nu)
116	if err != nil || userConfig == nil {
117		return nil
118	}
119	return userConfig.GetPassphraseState()
120}
121
122func (f *JSONConfigFile) GetDeviceIDForUID(u keybase1.UID) keybase1.DeviceID {
123	f.userConfigWrapper.Lock()
124	defer f.userConfigWrapper.Unlock()
125	ret, err := f.GetUserConfigForUID(u)
126	var empty keybase1.DeviceID
127	if err != nil || ret == nil {
128		return empty
129	}
130	return ret.GetDeviceID()
131}
132
133func (f *JSONConfigFile) GetUsernameForUID(u keybase1.UID) NormalizedUsername {
134	f.userConfigWrapper.Lock()
135	defer f.userConfigWrapper.Unlock()
136	ret, err := f.GetUserConfigForUID(u)
137	var empty NormalizedUsername
138	if err != nil || ret == nil {
139		return empty
140	}
141	return ret.GetUsername()
142}
143
144func (f *JSONConfigFile) GetUIDForUsername(n NormalizedUsername) keybase1.UID {
145	f.userConfigWrapper.Lock()
146	defer f.userConfigWrapper.Unlock()
147	ret, err := f.GetUserConfigForUsername(n)
148	var empty keybase1.UID
149	if err != nil || ret == nil {
150		return empty
151	}
152	return ret.GetUID()
153}
154
155func (f *JSONConfigFile) SwitchUser(nu NormalizedUsername) error {
156	f.userConfigWrapper.Lock()
157	defer f.userConfigWrapper.Unlock()
158
159	if cu := f.getCurrentUser(); cu.Eq(nu) {
160		f.G().Log.Debug("| Already configured as user=%s", nu)
161		return nil
162	}
163
164	var err error
165	var val *jsonw.Wrapper
166	if f.jw.AtKey("users").AtKey(nu.String()).IsNil() {
167		val = jsonw.NewNil()
168		err = UserNotFoundError{Msg: nu.String()}
169	} else {
170		val = jsonw.NewString(nu.String())
171	}
172
173	setKeyErr := f.jw.SetKey("current_user", val)
174	if err == nil {
175		err = setKeyErr
176	}
177	f.userConfigWrapper.userConfig = nil
178	saveErr := f.Save()
179	if err == nil {
180		err = saveErr
181	}
182	return err
183}
184
185// NukeUser deletes the given user from the config file, or if
186// the given user is empty, deletes the current user from the
187// config file.
188func (f *JSONConfigFile) NukeUser(nu NormalizedUsername) error {
189	f.userConfigWrapper.Lock()
190	defer f.userConfigWrapper.Unlock()
191
192	if cu := f.getCurrentUser(); nu.IsNil() || cu.Eq(nu) {
193		err := f.jw.DeleteValueAtPath("current_user")
194		f.userConfigWrapper.userConfig = nil
195		if err != nil {
196			return err
197		}
198		if nu.IsNil() {
199			nu = cu
200		}
201	}
202
203	if !f.jw.AtKey("users").AtKey(nu.String()).IsNil() {
204		err := f.jw.DeleteValueAtPath("users." + nu.String())
205		if err != nil {
206			return err
207		}
208	}
209
210	return f.Save()
211}
212
213// GetUserConfigForUsername sees if there's a UserConfig object for the given
214// username previously stored.
215func (f *JSONConfigFile) GetUserConfigForUsername(nu NormalizedUsername) (*UserConfig, error) {
216	if uc := f.copyUserConfigIfForUsername(nu); uc != nil {
217		return uc, nil
218	}
219	return ImportUserConfigFromJSONWrapper(f.jw.AtKey("users").AtKey(nu.String()))
220}
221
222func (f *JSONConfigFile) copyUserConfigIfForUsername(u NormalizedUsername) *UserConfig {
223	if f.userConfigWrapper == nil || f.userConfigWrapper.userConfig == nil {
224		return nil
225	}
226	if f.userConfigWrapper.userConfig.GetUsername().IsNil() {
227		return nil
228	}
229	if f.userConfigWrapper.userConfig.GetUsername().Eq(u) {
230		tmp := *f.userConfigWrapper.userConfig
231		return &tmp
232	}
233	return nil
234}
235
236func (f *JSONConfigFile) copyUserConfigIfForUID(u keybase1.UID) *UserConfig {
237	if f.userConfigWrapper == nil || f.userConfigWrapper.userConfig == nil {
238		return nil
239	}
240	if f.userConfigWrapper.userConfig.GetUID().IsNil() {
241		return nil
242	}
243	if f.userConfigWrapper.userConfig.GetUID().Equal(u) {
244		tmp := *f.userConfigWrapper.userConfig
245		return &tmp
246	}
247	return nil
248}
249
250// GetUserConfigForUID sees if there's a UserConfig object for the given UIDs previously stored.
251func (f *JSONConfigFile) GetUserConfigForUID(u keybase1.UID) (*UserConfig, error) {
252
253	if uc := f.copyUserConfigIfForUID(u); uc != nil {
254		return uc, nil
255	}
256
257	d := f.jw.AtKey("users")
258	keys, _ := d.Keys()
259	for _, key := range keys {
260		uc, err := f.GetUserConfigForUsername(NewNormalizedUsername(key))
261		if err == nil && uc != nil && uc.GetUID().Equal(u) {
262			return uc, nil
263		}
264	}
265	return nil, nil
266}
267
268func (f *JSONConfigFile) GetAllUserConfigs() (current *UserConfig, others []UserConfig, err error) {
269
270	currentUsername, allUsernames, err := f.GetAllUsernames()
271	if err != nil {
272		return nil, nil, err
273	}
274
275	if !currentUsername.IsNil() {
276		current, _ = f.GetUserConfigForUsername(currentUsername)
277	}
278
279	for _, u := range allUsernames {
280		tmp, err := f.GetUserConfigForUsername(u)
281		if err == nil && tmp != nil {
282			others = append(others, *tmp)
283		}
284	}
285
286	return current, others, nil
287}
288
289func (f *JSONConfigFile) GetAllUsernames() (current NormalizedUsername, others []NormalizedUsername, err error) {
290	current = f.getCurrentUser()
291	uw := f.jw.AtKey("users")
292	if uw.IsNil() {
293		return
294	}
295	keys, e := uw.Keys()
296	if e != nil {
297		err = e
298		return
299	}
300	for _, k := range keys {
301		u := uw.AtKey(k)
302		if u == nil {
303			continue
304		}
305		name, e := u.AtKey("name").GetString()
306		if e != nil {
307			err = e
308			return
309		}
310		nu := NewNormalizedUsername(name)
311		if !nu.Eq(current) {
312			others = append(others, nu)
313		}
314	}
315	return
316}
317
318// SetDeviceID sets the device field of the UserConfig object
319func (f *JSONConfigFile) SetDeviceID(did keybase1.DeviceID) (err error) {
320	f.userConfigWrapper.Lock()
321	defer f.userConfigWrapper.Unlock()
322
323	f.G().Log.Debug("| Setting DeviceID to %v\n", did)
324	var u *UserConfig
325	if u, err = f.getUserConfigWithLock(); err != nil {
326	} else if u == nil {
327		err = NoUserConfigError{}
328	} else {
329		u.SetDevice(did)
330		err = f.setUserConfigWithLock(u, true)
331	}
332	return
333}
334
335func (f *JSONConfigFile) getCurrentUser() NormalizedUsername {
336	s, _ := f.jw.AtKey("current_user").GetString()
337	return NormalizedUsername(s)
338}
339
340// SetUserConfig writes this UserConfig to the config file and updates the
341// currently active UserConfig in memory.  If the given UserConfig is nil, then
342// just empty everything out and clear the `current_user` field.  Note that
343// we never actually overwrite users.<username>, we just write it if it
344// doesn't already exist, and we update the `current_user` pointer.
345func (f *JSONConfigFile) SetUserConfig(u *UserConfig, overwrite bool) error {
346	f.userConfigWrapper.Lock()
347	defer f.userConfigWrapper.Unlock()
348	return f.setUserConfigWithLock(u, overwrite)
349}
350
351func (f *JSONConfigFile) setUserConfigWithLock(u *UserConfig, overwrite bool) error {
352
353	if u == nil {
354		f.G().Log.Debug("| SetUserConfig(nil)")
355		err := f.jw.DeleteKey("current_user")
356		if err != nil {
357			return err
358		}
359		f.userConfigWrapper.userConfig = nil
360		return f.Save()
361	}
362
363	if u.IsOneshot() {
364		f.userConfigWrapper.userConfig = u
365		return nil
366	}
367
368	parent := f.jw.AtKey("users")
369	un := u.GetUsername()
370	f.G().Log.Debug("| SetUserConfig(%s)", un)
371	if parent.IsNil() {
372		parent = jsonw.NewDictionary()
373		err := f.jw.SetKey("users", parent)
374		if err != nil {
375			return err
376		}
377	}
378	if parent.AtKey(un.String()).IsNil() || overwrite {
379		uWrapper, err := jsonw.NewObjectWrapper(*u)
380		if err != nil {
381			return err
382		}
383		err = parent.SetKey(un.String(), uWrapper)
384		if err != nil {
385			return err
386		}
387		f.userConfigWrapper.userConfig = u
388	}
389
390	if !f.getCurrentUser().Eq(un) {
391		err := f.jw.SetKey("current_user", jsonw.NewString(un.String()))
392		if err != nil {
393			return err
394		}
395		f.userConfigWrapper.userConfig = nil
396	}
397
398	return f.Save()
399}
400
401func (f *JSONConfigFile) Reset() {
402	f.jw = jsonw.NewDictionary()
403	_ = f.Save()
404}
405
406func (f *JSONConfigFile) GetHome() string {
407	return f.GetTopLevelString("home")
408}
409func (f *JSONConfigFile) GetMobileSharedHome() string {
410	return f.GetTopLevelString("mobile_shared_home")
411}
412func (f *JSONConfigFile) GetServerURI() (string, error) {
413	return f.GetTopLevelString("server"), nil
414}
415func (f *JSONConfigFile) GetConfigFilename() string {
416	return f.GetTopLevelString("config_file")
417}
418func (f *JSONConfigFile) GetUpdaterConfigFilename() string {
419	return f.GetTopLevelString("updater_config_file")
420}
421func (f *JSONConfigFile) GetGUIConfigFilename() string {
422	return f.GetTopLevelString("gui_config_file")
423}
424func (f *JSONConfigFile) GetDeviceCloneStateFilename() string {
425	return f.GetTopLevelString("device_clone_state_file")
426}
427func (f *JSONConfigFile) GetSecretKeyringTemplate() string {
428	return f.GetTopLevelString("secret_keyring")
429}
430func (f *JSONConfigFile) GetSessionFilename() string {
431	return f.GetTopLevelString("session_file")
432}
433func (f *JSONConfigFile) GetDbFilename() string {
434	return f.GetTopLevelString("db")
435}
436func (f *JSONConfigFile) GetChatDbFilename() string {
437	return f.GetTopLevelString("chat_db")
438}
439func (f *JSONConfigFile) GetPvlKitFilename() string {
440	return f.GetTopLevelString("pvl_kit")
441}
442func (f *JSONConfigFile) GetParamProofKitFilename() string {
443	return f.GetTopLevelString("paramproof_kit")
444}
445func (f *JSONConfigFile) GetExternalURLKitFilename() string {
446	return f.GetTopLevelString("externalurl_kit")
447}
448func (f *JSONConfigFile) GetProveBypass() (bool, bool) {
449	return f.GetBoolAtPath("prove_bypass")
450}
451func (f *JSONConfigFile) GetPinentry() string {
452	res, _ := f.GetStringAtPath("pinentry.path")
453	return res
454}
455func (f *JSONConfigFile) GetGpg() string {
456	res, _ := f.GetStringAtPath("gpg.command")
457	return res
458}
459func (f *JSONConfigFile) GetLocalRPCDebug() string {
460	return f.GetTopLevelString("local_rpc_debug")
461}
462func (f *JSONConfigFile) GetTimers() string {
463	return f.GetTopLevelString("timers")
464}
465func (f *JSONConfigFile) GetGpgOptions() []string {
466	var ret []string
467	if f.jw == nil {
468		// noop
469	} else if v := f.jw.AtPath("gpg.options"); v == nil {
470		// noop
471	} else if l, e := v.Len(); e != nil || l == 0 {
472		// noop
473	} else {
474		ret = make([]string, 0, l)
475		for i := 0; i < l; i++ {
476			if s, e := v.AtIndex(i).GetString(); e == nil {
477				ret = append(ret, s)
478			}
479		}
480	}
481	return ret
482}
483func (f *JSONConfigFile) GetRunMode() (ret RunMode, err error) {
484	ret = NoRunMode
485	if s, isSet := f.GetStringAtPath("run_mode"); isSet {
486		ret, err = StringToRunMode(s)
487	}
488	return ret, err
489}
490func (f *JSONConfigFile) GetFeatureFlags() (ret FeatureFlags, err error) {
491	if s, isSet := f.GetStringAtPath("features"); isSet {
492		ret = StringToFeatureFlags(s)
493	}
494	return ret, err
495}
496func (f *JSONConfigFile) GetNoPinentry() (bool, bool) {
497	return f.GetBoolAtPath("pinentry.disabled")
498}
499func (f *JSONConfigFile) GetUsername() (ret NormalizedUsername) {
500	if uc, _ := f.GetUserConfig(); uc != nil {
501		ret = uc.GetUsername()
502	}
503	return ret
504}
505func (f *JSONConfigFile) GetUID() (ret keybase1.UID) {
506	if uc, _ := f.GetUserConfig(); uc != nil {
507		ret = uc.GetUID()
508	}
509	return ret
510}
511func (f *JSONConfigFile) GetDeviceID() (ret keybase1.DeviceID) {
512	if uc, _ := f.GetUserConfig(); uc != nil {
513		ret = uc.GetDeviceID()
514	}
515	return ret
516}
517
518func (f *JSONConfigFile) GetPassphraseState() (ret *keybase1.PassphraseState) {
519	if uc, _ := f.GetUserConfig(); uc != nil {
520		ret = uc.GetPassphraseState()
521	}
522	return ret
523}
524
525func (f *JSONConfigFile) SetPassphraseState(passphraseState keybase1.PassphraseState) (err error) {
526	f.userConfigWrapper.Lock()
527	defer f.userConfigWrapper.Unlock()
528
529	f.G().Log.Debug("| Setting PassphraseState to %v\n", passphraseState)
530	var u *UserConfig
531	if u, err = f.getUserConfigWithLock(); err != nil {
532	} else if u == nil {
533		err = NoUserConfigError{}
534	} else {
535		u.SetPassphraseState(passphraseState)
536		err = f.setUserConfigWithLock(u, true)
537	}
538	return
539}
540
541func (f *JSONConfigFile) GetTorMode() (ret TorMode, err error) {
542	if s, isSet := f.GetStringAtPath("tor.mode"); isSet {
543		ret, err = StringToTorMode(s)
544	}
545	return ret, err
546}
547
548func (f *JSONConfigFile) GetTorHiddenAddress() string {
549	s, _ := f.GetStringAtPath("tor.hidden_address")
550	return s
551}
552func (f *JSONConfigFile) GetTorProxy() string {
553	s, _ := f.GetStringAtPath("tor.proxy")
554	return s
555}
556
557func (f *JSONConfigFile) GetProxy() string {
558	return f.GetTopLevelString("proxy")
559}
560func (f *JSONConfigFile) GetProxyType() string {
561	return f.GetTopLevelString("proxy-type")
562}
563func (f *JSONConfigFile) IsCertPinningEnabled() bool {
564	res, isSet := f.GetTopLevelBool("disable-cert-pinning")
565	if !isSet {
566		// Enable SSL pinning if the flag is not set
567		return true
568	}
569	return !res
570}
571func (f *JSONConfigFile) GetDebug() (bool, bool) {
572	return f.GetTopLevelBool("debug")
573}
574func (f *JSONConfigFile) GetDebugJourneycard() (bool, bool) {
575	return f.GetTopLevelBool("debug_journeycard")
576}
577func (f *JSONConfigFile) GetDisplayRawUntrustedOutput() (bool, bool) {
578	return f.GetTopLevelBool("display_raw_untrusted_output")
579}
580func (f *JSONConfigFile) GetVDebugSetting() string {
581	return f.GetTopLevelString("vdebug")
582}
583func (f *JSONConfigFile) GetAutoFork() (bool, bool) {
584	return f.GetTopLevelBool("auto_fork")
585}
586
587func (f *JSONConfigFile) GetRememberPassphrase(username NormalizedUsername) (bool, bool) {
588	const legacyRememberPassphraseKey = "remember_passphrase"
589
590	if username.IsNil() {
591		return f.GetTopLevelBool(legacyRememberPassphraseKey)
592	}
593	if m, ok := f.jw.AtKey("remember_passphrase_map").GetDataOrNil().(map[string]interface{}); ok {
594		if ret, mOk := m[username.String()]; mOk {
595			if boolRet, boolOk := ret.(bool); boolOk {
596				return boolRet, true
597			}
598		}
599	}
600	return f.GetTopLevelBool(legacyRememberPassphraseKey)
601}
602func (f *JSONConfigFile) GetStayLoggedOut() (bool, bool) {
603	return f.GetBoolAtPath("stay_logged_out")
604}
605func (f *JSONConfigFile) SetStayLoggedOut(stayLoggedOut bool) error {
606	return f.SetBoolAtPath("stay_logged_out", stayLoggedOut)
607}
608func (f *JSONConfigFile) GetLogFormat() string {
609	return f.GetTopLevelString("log_format")
610}
611func (f *JSONConfigFile) GetStandalone() (bool, bool) {
612	return f.GetTopLevelBool("standalone")
613}
614func (f *JSONConfigFile) GetGregorURI() string {
615	s, _ := f.GetStringAtPath("push.server_uri")
616	return s
617}
618func (f *JSONConfigFile) GetGregorDisabled() (bool, bool) {
619	return f.GetBoolAtPath("push.disabled")
620}
621func (f *JSONConfigFile) GetSecretStorePrimingDisabled() (bool, bool) {
622	// SecretStorePrimingDisabled is only for tests
623	return false, false
624}
625func (f *JSONConfigFile) GetBGIdentifierDisabled() (bool, bool) {
626	return f.GetBoolAtPath("bg_identifier.disabled")
627}
628func (f *JSONConfigFile) GetGregorSaveInterval() (time.Duration, bool) {
629	return f.GetDurationAtPath("push.save_interval")
630}
631
632func (f *JSONConfigFile) GetGregorPingInterval() (time.Duration, bool) {
633	return f.GetDurationAtPath("push.ping_interval")
634}
635
636func (f *JSONConfigFile) GetGregorPingTimeout() (time.Duration, bool) {
637	return f.GetDurationAtPath("push.ping_timeout")
638}
639
640func (f *JSONConfigFile) GetChatDelivererInterval() (time.Duration, bool) {
641	return f.GetDurationAtPath("chat.deliverer_interval")
642}
643
644func (f *JSONConfigFile) getCacheSize(w string) (int, bool) {
645	return f.jw.AtPathGetInt(w)
646}
647
648func (f *JSONConfigFile) GetUserCacheMaxAge() (time.Duration, bool) {
649	return f.GetDurationAtPath("cache.maxage.users")
650}
651func (f *JSONConfigFile) GetAPITimeout() (time.Duration, bool) {
652	return f.GetDurationAtPath("timeouts.api")
653}
654func (f *JSONConfigFile) GetScraperTimeout() (time.Duration, bool) {
655	return f.GetDurationAtPath("timeouts.scraper")
656}
657func (f *JSONConfigFile) GetProofCacheSize() (int, bool) {
658	return f.getCacheSize("cache.limits.proofs")
659}
660
661func (f *JSONConfigFile) GetProofCacheLongDur() (time.Duration, bool) {
662	return f.GetDurationAtPath("cache.long_duration.proofs")
663}
664
665func (f *JSONConfigFile) GetProofCacheMediumDur() (time.Duration, bool) {
666	return f.GetDurationAtPath("cache.medium_duration.proofs")
667}
668
669func (f *JSONConfigFile) GetProofCacheShortDur() (time.Duration, bool) {
670	return f.GetDurationAtPath("cache.short_duration.proofs")
671}
672
673func (f *JSONConfigFile) GetLinkCacheSize() (int, bool) {
674	return f.getCacheSize("cache.limits.links")
675}
676
677func (f *JSONConfigFile) GetLinkCacheCleanDur() (time.Duration, bool) {
678	return f.GetDurationAtPath("cache.clean_duration.links")
679}
680
681func (f *JSONConfigFile) GetUPAKCacheSize() (int, bool) {
682	return f.getCacheSize("cache.limits.upak")
683}
684
685func (f *JSONConfigFile) GetUIDMapFullNameCacheSize() (int, bool) {
686	return f.getCacheSize("cache.limits.uid_map_full_name")
687}
688
689func (f *JSONConfigFile) GetPayloadCacheSize() (int, bool) {
690	return f.getCacheSize("cache.limits.payloads")
691}
692
693func (f *JSONConfigFile) GetLevelDBNumFiles() (int, bool) {
694	return f.GetIntAtPath("leveldb.num_files")
695}
696
697func (f *JSONConfigFile) GetLevelDBWriteBufferMB() (int, bool) {
698	return f.GetIntAtPath("leveldb.write_buffer_mb")
699}
700
701func (f *JSONConfigFile) GetChatInboxSourceLocalizeThreads() (int, bool) {
702	return f.GetIntAtPath("chat.inboxsource.localizethreads")
703}
704
705func (f *JSONConfigFile) getStringArray(v *jsonw.Wrapper) []string {
706	n, err := v.Len()
707	if err != nil {
708		return nil
709	}
710
711	if n == 0 {
712		return nil
713	}
714
715	ret := make([]string, n)
716	for i := 0; i < n; i++ {
717		s, err := v.AtIndex(i).GetString()
718		if err != nil {
719			return nil
720		}
721		ret[i] = s
722	}
723	return ret
724}
725
726func (f *JSONConfigFile) GetMerkleKIDs() []string {
727	if f.jw == nil {
728		return nil
729	}
730
731	v, err := f.jw.AtKey("keys").AtKey("merkle").ToArray()
732	if err != nil || v == nil {
733		return nil
734	}
735
736	return f.getStringArray(v)
737}
738
739func (f *JSONConfigFile) GetCodeSigningKIDs() []string {
740	if f.jw == nil {
741		return nil
742	}
743
744	v, err := f.jw.AtKey("keys").AtKey("codesigning").ToArray()
745	if err != nil || v == nil {
746		return nil
747	}
748	return f.getStringArray(v)
749}
750
751func (f *JSONConfigFile) GetGpgHome() (ret string) {
752	ret, _ = f.GetStringAtPath("gpg.home")
753	return ret
754}
755
756func (f *JSONConfigFile) GetBundledCA(host string) (ret string) {
757	var err error
758	f.jw.AtKey("bundled_ca").AtKey(host).GetStringVoid(&ret, &err)
759	if err == nil {
760		f.G().Log.Debug("Read bundled CA for %s", host)
761	}
762	return ret
763}
764
765func (f *JSONConfigFile) GetSocketFile() string {
766	return f.GetTopLevelString("socket_file")
767}
768func (f *JSONConfigFile) GetPidFile() string {
769	return f.GetTopLevelString("pid_file")
770}
771
772func (f *JSONConfigFile) GetProxyCACerts() (ret []string, err error) {
773	jw := f.jw.AtKey("proxy_ca_certs")
774	if l, e := jw.Len(); e == nil {
775		for i := 0; i < l; i++ {
776			s, e2 := jw.AtIndex(i).GetString()
777			if e2 != nil {
778				err = ConfigError{f.filename,
779					fmt.Sprintf("Error reading proxy CA file @ index %d: %s", i, e2)}
780				return
781			}
782
783			ret = append(ret, s)
784		}
785	} else if s, e := jw.GetString(); e == nil {
786		ret = strings.Split(s, ":")
787	} else if !jw.IsNil() {
788		err = ConfigError{f.filename, fmt.Sprintf("Can't read Proxy CA certs: %s", e)}
789	}
790	return
791}
792
793func (f *JSONConfigFile) GetLogFile() string {
794	return f.GetTopLevelString("log_file")
795}
796func (f *JSONConfigFile) GetEKLogFile() string {
797	return f.GetTopLevelString("ek_log_file")
798}
799func (f *JSONConfigFile) GetPerfLogFile() string {
800	return f.GetTopLevelString("perf_log_file")
801}
802func (f *JSONConfigFile) GetGUILogFile() string {
803	return f.GetTopLevelString("gui_log_file")
804}
805
806func (f *JSONConfigFile) GetUseDefaultLogFile() (bool, bool) {
807	return f.GetTopLevelBool("use_default_log_file")
808}
809
810func (f *JSONConfigFile) GetUseRootConfigFile() (bool, bool) {
811	return false, false
812}
813
814func (f *JSONConfigFile) GetLogPrefix() string {
815	return f.GetTopLevelString("log_prefix")
816}
817
818func (f *JSONConfigFile) GetSecurityAccessGroupOverride() (bool, bool) {
819	return false, false
820}
821
822func (f *JSONConfigFile) GetUpdatePreferenceAuto() (bool, bool) {
823	return f.GetBoolAtPath("updates.auto")
824}
825
826func (f *JSONConfigFile) GetUpdatePreferenceSnoozeUntil() keybase1.Time {
827	return f.GetTimeAtPath("updates.snooze")
828}
829
830func (f *JSONConfigFile) GetUpdateLastChecked() keybase1.Time {
831	return f.GetTimeAtPath("updates.last_checked")
832}
833
834func (f *JSONConfigFile) GetUpdatePreferenceSkip() string {
835	s, _ := f.GetStringAtPath("updates.skip")
836	return s
837}
838
839func (f *JSONConfigFile) SetUpdatePreferenceAuto(b bool) error {
840	return f.SetBoolAtPath("updates.auto", b)
841}
842
843func (f *JSONConfigFile) SetUpdatePreferenceSkip(v string) error {
844	return f.SetStringAtPath("updates.skip", v)
845}
846
847func (f *JSONConfigFile) SetUpdatePreferenceSnoozeUntil(t keybase1.Time) error {
848	return f.SetTimeAtPath("updates.snooze", t)
849}
850
851func (f *JSONConfigFile) SetUpdateLastChecked(t keybase1.Time) error {
852	return f.SetTimeAtPath("updates.last_checked", t)
853}
854
855func (f *JSONConfigFile) GetUpdateURL() string {
856	s, _ := f.GetStringAtPath("updates.url")
857	return s
858}
859
860func (f *JSONConfigFile) GetUpdateDisabled() (bool, bool) {
861	return f.GetBoolAtPath("updates.disabled")
862}
863
864func (f *JSONConfigFile) GetTimeAtPath(path string) keybase1.Time {
865	var ret keybase1.Time
866	s, _ := f.GetStringAtPath(path)
867	if len(s) == 0 {
868		return ret
869	}
870	u, err := strconv.ParseUint(s, 10, 64)
871	if err != nil {
872		return ret
873	}
874	ret = keybase1.Time(u)
875	return ret
876}
877
878func (f *JSONConfigFile) SetTimeAtPath(path string, t keybase1.Time) error {
879	if t == keybase1.Time(0) {
880		return f.SetNullAtPath(path)
881	}
882	return f.SetStringAtPath(path, fmt.Sprintf("%d", t))
883}
884
885func (f *JSONConfigFile) GetLocalTrackMaxAge() (time.Duration, bool) {
886	return f.GetDurationAtPath("local_track_max_age")
887}
888
889func (f *JSONConfigFile) GetMountDir() string {
890	return f.GetTopLevelString("mountdir")
891}
892
893func (f *JSONConfigFile) GetMountDirDefault() string {
894	return f.GetTopLevelString("mountdirdefault")
895}
896
897func bug3964path(un NormalizedUsername) string {
898	return fmt.Sprintf("maintenance.%s.bug_3964_repair_time", un)
899}
900
901func (f *JSONConfigFile) GetBug3964RepairTime(un NormalizedUsername) (time.Time, error) {
902	if un == "" {
903		return time.Time{}, NoUserConfigError{}
904	}
905	s, _ := f.GetStringAtPath(bug3964path(un))
906	if s == "" {
907		return time.Time{}, nil
908	}
909	i, err := strconv.ParseUint(s, 10, 64)
910	if err != nil {
911		return time.Time{}, err
912	}
913	return keybase1.FromTime(keybase1.Time(i)), nil
914}
915
916func (f *JSONConfigFile) SetBug3964RepairTime(un NormalizedUsername, t time.Time) (err error) {
917	return f.SetStringAtPath(bug3964path(un), fmt.Sprintf("%d", int64(keybase1.ToTime(t))))
918}
919
920func (f *JSONConfigFile) GetAppType() AppType {
921	return AppType(f.GetTopLevelString("app_type"))
922}
923
924func (f *JSONConfigFile) IsMobileExtension() (bool, bool) {
925	return f.GetBoolAtPath("mobile_extension")
926}
927
928func (f *JSONConfigFile) GetSlowGregorConn() (bool, bool) {
929	return f.GetBoolAtPath("slow_gregor_conn")
930}
931
932func (f *JSONConfigFile) GetReadDeletedSigChain() (bool, bool) {
933	return f.GetBoolAtPath("read_deleted_sigchain")
934}
935
936func (f *JSONConfigFile) SetRememberPassphrase(username NormalizedUsername, remember bool) error {
937	if username.IsNil() {
938		return f.SetBoolAtPath("remember_passphrase", remember)
939	}
940	return f.SetBoolAtPath(fmt.Sprintf("remember_passphrase_map.%s", username.String()), remember)
941}
942
943func (f *JSONConfigFile) GetAttachmentHTTPStartPort() (int, bool) {
944	return f.GetIntAtPath("attachment_httpsrv_port")
945}
946
947func (f *JSONConfigFile) GetAttachmentDisableMulti() (bool, bool) {
948	return f.GetBoolAtPath("attachment_disable_multi")
949}
950
951func (f *JSONConfigFile) GetDisableTeamAuditor() (bool, bool) {
952	return f.GetBoolAtPath("disable_team_auditor")
953}
954
955func (f *JSONConfigFile) GetDisableTeamBoxAuditor() (bool, bool) {
956	return f.GetBoolAtPath("disable_team_box_auditor")
957}
958
959func (f *JSONConfigFile) GetDisableEKBackgroundKeygen() (bool, bool) {
960	return f.GetBoolAtPath("disable_ek_background_keygen")
961}
962
963func (f *JSONConfigFile) GetDisableMerkleAuditor() (bool, bool) {
964	return f.GetBoolAtPath("disable_merkle_auditor")
965}
966
967func (f *JSONConfigFile) GetDisableSearchIndexer() (bool, bool) {
968	return f.GetBoolAtPath("disable_search_indexer")
969}
970
971func (f *JSONConfigFile) GetDisableBgConvLoader() (bool, bool) {
972	return f.GetBoolAtPath("disable_bg_conv_loader")
973}
974
975func (f *JSONConfigFile) GetEnableBotLiteMode() (bool, bool) {
976	return f.GetBoolAtPath("enable_bot_lite_mode")
977}
978
979func (f *JSONConfigFile) GetExtraNetLogging() (bool, bool) {
980	return f.GetBoolAtPath("extra_net_logging")
981}
982
983func (f *JSONConfigFile) GetForceLinuxKeyring() (bool, bool) {
984	return f.GetBoolAtPath("force_linux_keyring")
985}
986
987func (f *JSONConfigFile) GetForceSecretStoreFile() (bool, bool) {
988	return f.GetBoolAtPath("force_less_safe_secret_store_file")
989}
990
991func (f *JSONConfigFile) GetRuntimeStatsEnabled() (bool, bool) {
992	return f.GetBoolAtPath("runtime_stats_enabled")
993}
994
995func (f *JSONConfigFile) GetAndroidInstallReferrerChecked() bool {
996	val, isSet := f.GetBoolAtPath("android_install_referrer_checked")
997	// default to false when value is not set
998	return isSet && val
999}
1000
1001func (f *JSONConfigFile) SetAndroidInstallReferrerChecked(b bool) error {
1002	return f.SetBoolAtPath("android_install_referrer_checked", b)
1003}
1004