<commit_before>package libkb

import (
	"fmt"
	"time"

	jsonw "github.com/keybase/go-jsonw"
)

type Session struct {
	Contextified
	file     *JsonFile
	token    string
	csrf     string
	inFile   bool
	loaded   bool
	checked  bool
	valid    bool
	uid      *UID
	username *string
	mtime    int64
}

func NewSession(g *GlobalContext) *Session {
	return &Session{Contextified: Contextified{g}}
}

// NewSessionThin creates a minimal (thin) session of just the uid and username.
// Clients of the daemon that use the session protocol need this.
func NewSessionThin(uid UID, username string, token string) *Session {
	// XXX should this set valid to true?  daemon won't return a
	// session unless valid is true, so...
	return &Session{uid: &uid, username: &username, token: token, valid: true}
}

func (s Session) IsLoggedIn() bool {
	return s.valid
}

func (s Session) GetUsername() *string {
	return s.username
}

func (s Session) GetUID() *UID {
	return s.uid
}

func (s Session) GetToken() string {
	return s.token
}

func (s *Session) SetLoggedIn(lir LoggedInResult) {
	s.valid = true
	s.uid = &lir.Uid
	s.username = &lir.Username
	s.token = lir.SessionId
	s.GetDictionary().SetKey("session", jsonw.NewString(lir.SessionId))
	s.SetCsrf(lir.CsrfToken)
	s.SetDirty()

	// Set up our SecretSyncer to work on the logged in user from here on
	// out.
	s.G().SecretSyncer.setUID(s.uid)
}

func (s *Session) SetDirty() {
	s.file.dirty = true
	s.GetDictionary().SetKey("mtime", jsonw.NewInt64(time.Now().Unix()))
}

func (s *Session) SetCsrf(t string) {
	s.csrf = t
	if s.file != nil {
		s.GetDictionary().SetKey("csrf", jsonw.NewString(t))
		s.SetDirty()
	}
}

func (s *Session) Load() error {
	G.Log.Debug("+ Loading session")
	if s.loaded {
		G.Log.Debug("- Skipped; already loaded")
		return nil
	}
	s.file = NewJsonFile(G.Env.GetSessionFilename(), "session")
	err := s.file.Load(false)
	s.loaded = true

	if err != nil {
		G.Log.Error("Failed to load session file")
		return err
	}

	if s.file.Exists() {
		var tmp error
		var token, csrf string
		ok := true
		s.file.jw.AtKey("session").GetStringVoid(&token, &tmp)
		if tmp != nil {
			G.Log.Warning("Bad 'session' value in session file %s: %s",
				s.file.filename, tmp.Error())
			ok = false<commit_msg>nuke the session file when the config doesn't match it<commit_after>package libkb

import (
	"fmt"
	"os"
	"time"

	jsonw "github.com/keybase/go-jsonw"
)

type Session struct {
	Contextified
	file     *JsonFile
	token    string
	csrf     string
	inFile   bool
	loaded   bool
	checked  bool
	valid    bool
	uid      *UID
	username *string
	mtime    int64
}

func NewSession(g *GlobalContext) *Session {
	return &Session{Contextified: Contextified{g}}
}

// NewSessionThin creates a minimal (thin) session of just the uid and username.
// Clients of the daemon that use the session protocol need this.
func NewSessionThin(uid UID, username string, token string) *Session {
	// XXX should this set valid to true?  daemon won't return a
	// session unless valid is true, so...
	return &Session{uid: &uid, username: &username, token: token, valid: true}
}

func (s Session) IsLoggedIn() bool {
	return s.valid
}

func (s Session) GetUsername() *string {
	return s.username
}

func (s Session) GetUID() *UID {
	return s.uid
}

func (s Session) GetToken() string {
	return s.token
}

func (s *Session) SetLoggedIn(lir LoggedInResult) {
	s.valid = true
	s.uid = &lir.Uid
	s.username = &lir.Username
	s.token = lir.SessionId
	s.GetDictionary().SetKey("session", jsonw.NewString(lir.SessionId))
	s.SetCsrf(lir.CsrfToken)
	s.SetDirty()

	// Set up our SecretSyncer to work on the logged in user from here on
	// out.
	s.G().SecretSyncer.setUID(s.uid)
}

func (s *Session) SetDirty() {
	s.file.dirty = true
	s.GetDictionary().SetKey("mtime", jsonw.NewInt64(time.Now().Unix()))
}

func (s *Session) SetCsrf(t string) {
	s.csrf = t
	if s.file != nil {
		s.GetDictionary().SetKey("csrf", jsonw.NewString(t))
		s.SetDirty()
	}
}

func isConfigLoggedIn() bool {
	reader := G.Env.GetConfig()
	return reader.GetUsername() != "" && reader.GetDeviceID() != nil && reader.GetUID() != nil
}

// The session file can be out of sync with the config file, particularly when
// switching between the node and go clients.
func nukeSessionFileIfOutOfSync() error {
	sessionFile := G.Env.GetSessionFilename()
	// Use stat to check existence.
	_, statErr := os.Lstat(sessionFile)
	if statErr == nil && !isConfigLoggedIn() {
		G.Log.Warning("Session file found but user is not logged in. Deleting session file.")
		return os.Remove(sessionFile)
	}
	return nil
}

func (s *Session) Load() error {
	G.Log.Debug("+ Loading session")
	if s.loaded {
		G.Log.Debug("- Skipped; already loaded")
		return nil
	}

	err := nukeSessionFileIfOutOfSync()
	if err != nil {
		return err
	}

	s.file = NewJsonFile(G.Env.GetSessionFilename(), "session")
	err = s.file.Load(false)
	s.loaded = true

	if err != nil {
		G.Log.Error("Failed to load session file")
		return err
	}

	if s.file.Exists() {
		var tmp error
		var token, csrf string
		ok := true
		s.file.jw.AtKey("session").GetStringVoid(&token, &tmp)
		if tmp != nil {
			G.Log.Warning("Bad 'session' value in session file %s: %s",
				s.file.filename, tmp.Error())
			ok = false
{'fraction_non_alphanumeric': 0.1327990841442473, 'fraction_numerical': 0.0015264262545315779, 'mean_word_length': 3.622574955908289, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4290305', 'n_tokens_mistral': 1947, 'n_tokens_neox': 1888, 'n_words': 556}