<commit_before>package chat

import (
	"errors"
	"fmt"
	"strings"

	"github.com/hashicorp/golang-lru"
	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/chat/types"
	"github.com/keybase/client/go/chat/utils"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/gregor1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	context "golang.org/x/net/context"
)

func getTeamCryptKey(ctx context.Context, team *teams.Team, generation keybase1.PerTeamKeyGeneration,
	public, kbfsEncrypted bool) (res types.CryptKey, err error) {
	if public {
		return publicCryptKey, nil
	}
	if kbfsEncrypted {
		kbfsKeys := team.KBFSCryptKeys(ctx, keybase1.TeamApplication_CHAT)
		for _, key := range kbfsKeys {
			if key.Generation() == int(generation) {
				return key, nil
			}
		}
		return res, NewDecryptionKeyNotFoundError(int(generation), kbfsEncrypted, public)
	}
	return team.ApplicationKeyAtGeneration(ctx, keybase1.TeamApplication_CHAT, generation)
}

// shouldFallbackToSlowLoadAfterFTLError returns trues if the given error should result
// in a retry via slow loading. Right now, it only happens if the server tells us
// that our FTL is outdated, or FTL is feature-flagged off on the server.
func shouldFallbackToSlowLoadAfterFTLError(m libkb.MetaContext, err error) bool {
	if err == nil {
		return false
	}
	switch tErr := err.(type) {
	case libkb.TeamFTLOutdatedError:
		m.CDebugf("Our FTL implementation is too old; falling back to slow loader (%v)", err)
		return true
	case libkb.FeatureFlagError:
		if tErr.Feature() == libkb.FeatureFTL {
			m.CDebugf("FTL feature-flagged off on the server, falling back to regular loader")
			return true
		}
	}
	return false
}

func encryptionKeyViaFTL(m libkb.MetaContext, name string, tlfID chat1.TLFID) (res types.CryptKey, ni types.NameInfo, err error) {
	ftlRes, err := getKeyViaFTL(m, name, tlfID, 0)
	if err != nil {
		return res, ni, err
	}
	ni = types.NameInfo{
		ID:            tlfID,
		CanonicalName: ftlRes.Name.String(),
	}
	return ftlRes.ApplicationKeys[0], ni, nil
}

func decryptionKeyViaFTL(m libkb.MetaContext, tlfID chat1.TLFID, keyGeneration int) (res types.CryptKey, err error) {

	// We don't pass a `name` during decryption.
	ftlRes, err := getKeyViaFTL(m, "" /*name*/, tlfID, keyGeneration)
	if err != nil {
		return nil, err
	}
	return ftlRes.ApplicationKeys[0], nil
}

func getKeyViaFTL(m libkb.MetaContext, name string, tlfID chat1.TLFID, keyGeneration int) (res keybase1.FastTeamLoadRes, err error) {
	defer m.CTrace(fmt.Sprintf("getKeyViaFTL(%s,%v,%d)", name, tlfID, keyGeneration), func() error { return err })()

	teamID, err := keybase1.TeamIDFromString(tlfID.String())
	if err != nil {
		return res, err
	}
	// The `name` parameter is optional since subteams can be renamed and
	// messages with the old name must be successfully decrypted.
	var teamNamePtr *keybase1.TeamName
	if name != "" {
		teamName, err := keybase1.TeamNameFromString(name)
		if err != nil {
			return res, err
		}
		teamNamePtr = &teamName
	}
	arg := keybase1.FastTeamLoadArg{
		ID:             teamID,
		Public:         false,
		Applications:   []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
		AssertTeamName: teamNamePtr,
	}

	if keyGeneration > 0 {
		arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(keyGeneration)}
	} else {
		arg.NeedLatestKey = true
	}

	res, err = m.G().GetFastTeamLoader().Load(m, arg)
	if err != nil {
		return res, err
	}

	n := len(res.ApplicationKeys)
	if n != 1 {
		return res, NewFTLError(fmt.Sprintf("wrong number of keys back from FTL; wanted 1, but got %d", n))
	}

	if keyGeneration > 0 && res.ApplicationKeys[0].KeyGeneration != keybase1.PerTeamKeyGeneration(keyGeneration) {
		return res, NewFTLError(fmt.Sprintf("wrong generation back from FTL; wanted %d but got %d", keyGeneration, res.ApplicationKeys[0].KeyGeneration))
	}

	if res.ApplicationKeys[0].Application != keybase1.TeamApplication_CHAT {
		return res, NewFTLError(fmt.Sprintf("wrong application; wanted %d but got %d", keybase1.TeamApplication_CHAT, res.ApplicationKeys[0].Application))
	}

	return res, nil
}

func loadTeamForDecryption(ctx context.Context, loader *TeamLoader, name string, teamID chat1.TLFID,
	membersType chat1.ConversationMembersType, public bool,
	keyGeneration int, kbfsEncrypted bool) (*teams.Team, error) {

	var refreshers keybase1.TeamRefreshers
	if !public {
		// Only need keys for private teams.
		if !kbfsEncrypted {
			refreshers.NeedApplicationsAtGenerations = map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{
				keybase1.PerTeamKeyGeneration(keyGeneration): []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
			}
		} else {
			refreshers.NeedKBFSKeyGeneration = keybase1.TeamKBFSKeyRefresher{
				Generation: keyGeneration,
				AppType:    keybase1.TeamApplication_CHAT,
			}
		}
	}
	team, err := loader.loadTeam(ctx, teamID, name, membersType, public,
		func(teamID keybase1.TeamID) keybase1.LoadTeamArg {
			return keybase1.LoadTeamArg{
				ID:         teamID,
				Public:     public,
				Refreshers: refreshers,
				StaleOK:    true,
			}
		})
	if err != nil {
		return nil, err
	}
	return team, nil
}

type TeamLoader struct {
	libkb.Contextified
	utils.DebugLabeler
}

func NewTeamLoader(g *libkb.GlobalContext) *TeamLoader {
	return &TeamLoader{
		Contextified: libkb.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "TeamLoader", false),
	}
}

func (t *TeamLoader) validKBFSTLFID(tlfID chat1.TLFID, team *teams.Team) bool {
	tlfIDs := team.KBFSTLFIDs()
	for _, id := range tlfIDs {
		if tlfID.EqString(id) {
			return true
		}
	}
	return false
}

func (t *TeamLoader) validateImpTeamname(ctx context.Context, tlfName string, public bool,
	team *teams.Team) error {
	impTeamName, err := team.ImplicitTeamDisplayNameString(ctx)
	if err != nil {
		return err
	}
	if impTeamName != tlfName {
		// Try resolving given name, maybe there has been a resolution
		resName, err := teams.ResolveImplicitTeamDisplayName(ctx, t.G(), tlfName, public)
		if err != nil {
			return err
		}
		if impTeamName != resName.String() {
			return ImpteamBadteamError{
				Msg: fmt.Sprintf("mismatch TLF name to implicit team name: %s != %s", impTeamName,
					tlfName),
			}
		}
	}
	return nil
}

func (t *TeamLoader) loadTeam(ctx context.Context, tlfID chat1.TLFID,
	tlfName string, membersType chat1.ConversationMembersType, public bool,
	loadTeamArgOverride func(keybase1.TeamID) keybase1.LoadTeamArg) (team *teams.Team, err error) {<commit_msg>use the better impteamname string (#15173)<commit_after>package chat

import (
	"errors"
	"fmt"
	"strings"

	"github.com/hashicorp/golang-lru"
	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/chat/types"
	"github.com/keybase/client/go/chat/utils"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/gregor1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	context "golang.org/x/net/context"
)

func getTeamCryptKey(ctx context.Context, team *teams.Team, generation keybase1.PerTeamKeyGeneration,
	public, kbfsEncrypted bool) (res types.CryptKey, err error) {
	if public {
		return publicCryptKey, nil
	}
	if kbfsEncrypted {
		kbfsKeys := team.KBFSCryptKeys(ctx, keybase1.TeamApplication_CHAT)
		for _, key := range kbfsKeys {
			if key.Generation() == int(generation) {
				return key, nil
			}
		}
		return res, NewDecryptionKeyNotFoundError(int(generation), kbfsEncrypted, public)
	}
	return team.ApplicationKeyAtGeneration(ctx, keybase1.TeamApplication_CHAT, generation)
}

// shouldFallbackToSlowLoadAfterFTLError returns trues if the given error should result
// in a retry via slow loading. Right now, it only happens if the server tells us
// that our FTL is outdated, or FTL is feature-flagged off on the server.
func shouldFallbackToSlowLoadAfterFTLError(m libkb.MetaContext, err error) bool {
	if err == nil {
		return false
	}
	switch tErr := err.(type) {
	case libkb.TeamFTLOutdatedError:
		m.CDebugf("Our FTL implementation is too old; falling back to slow loader (%v)", err)
		return true
	case libkb.FeatureFlagError:
		if tErr.Feature() == libkb.FeatureFTL {
			m.CDebugf("FTL feature-flagged off on the server, falling back to regular loader")
			return true
		}
	}
	return false
}

func encryptionKeyViaFTL(m libkb.MetaContext, name string, tlfID chat1.TLFID) (res types.CryptKey, ni types.NameInfo, err error) {
	ftlRes, err := getKeyViaFTL(m, name, tlfID, 0)
	if err != nil {
		return res, ni, err
	}
	ni = types.NameInfo{
		ID:            tlfID,
		CanonicalName: ftlRes.Name.String(),
	}
	return ftlRes.ApplicationKeys[0], ni, nil
}

func decryptionKeyViaFTL(m libkb.MetaContext, tlfID chat1.TLFID, keyGeneration int) (res types.CryptKey, err error) {

	// We don't pass a `name` during decryption.
	ftlRes, err := getKeyViaFTL(m, "" /*name*/, tlfID, keyGeneration)
	if err != nil {
		return nil, err
	}
	return ftlRes.ApplicationKeys[0], nil
}

func getKeyViaFTL(m libkb.MetaContext, name string, tlfID chat1.TLFID, keyGeneration int) (res keybase1.FastTeamLoadRes, err error) {
	defer m.CTrace(fmt.Sprintf("getKeyViaFTL(%s,%v,%d)", name, tlfID, keyGeneration), func() error { return err })()

	teamID, err := keybase1.TeamIDFromString(tlfID.String())
	if err != nil {
		return res, err
	}
	// The `name` parameter is optional since subteams can be renamed and
	// messages with the old name must be successfully decrypted.
	var teamNamePtr *keybase1.TeamName
	if name != "" {
		teamName, err := keybase1.TeamNameFromString(name)
		if err != nil {
			return res, err
		}
		teamNamePtr = &teamName
	}
	arg := keybase1.FastTeamLoadArg{
		ID:             teamID,
		Public:         false,
		Applications:   []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
		AssertTeamName: teamNamePtr,
	}

	if keyGeneration > 0 {
		arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(keyGeneration)}
	} else {
		arg.NeedLatestKey = true
	}

	res, err = m.G().GetFastTeamLoader().Load(m, arg)
	if err != nil {
		return res, err
	}

	n := len(res.ApplicationKeys)
	if n != 1 {
		return res, NewFTLError(fmt.Sprintf("wrong number of keys back from FTL; wanted 1, but got %d", n))
	}

	if keyGeneration > 0 && res.ApplicationKeys[0].KeyGeneration != keybase1.PerTeamKeyGeneration(keyGeneration) {
		return res, NewFTLError(fmt.Sprintf("wrong generation back from FTL; wanted %d but got %d", keyGeneration, res.ApplicationKeys[0].KeyGeneration))
	}

	if res.ApplicationKeys[0].Application != keybase1.TeamApplication_CHAT {
		return res, NewFTLError(fmt.Sprintf("wrong application; wanted %d but got %d", keybase1.TeamApplication_CHAT, res.ApplicationKeys[0].Application))
	}

	return res, nil
}

func loadTeamForDecryption(ctx context.Context, loader *TeamLoader, name string, teamID chat1.TLFID,
	membersType chat1.ConversationMembersType, public bool,
	keyGeneration int, kbfsEncrypted bool) (*teams.Team, error) {

	var refreshers keybase1.TeamRefreshers
	if !public {
		// Only need keys for private teams.
		if !kbfsEncrypted {
			refreshers.NeedApplicationsAtGenerations = map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{
				keybase1.PerTeamKeyGeneration(keyGeneration): []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
			}
		} else {
			refreshers.NeedKBFSKeyGeneration = keybase1.TeamKBFSKeyRefresher{
				Generation: keyGeneration,
				AppType:    keybase1.TeamApplication_CHAT,
			}
		}
	}
	team, err := loader.loadTeam(ctx, teamID, name, membersType, public,
		func(teamID keybase1.TeamID) keybase1.LoadTeamArg {
			return keybase1.LoadTeamArg{
				ID:         teamID,
				Public:     public,
				Refreshers: refreshers,
				StaleOK:    true,
			}
		})
	if err != nil {
		return nil, err
	}
	return team, nil
}

type TeamLoader struct {
	libkb.Contextified
	utils.DebugLabeler
}

func NewTeamLoader(g *libkb.GlobalContext) *TeamLoader {
	return &TeamLoader{
		Contextified: libkb.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "TeamLoader", false),
	}
}

func (t *TeamLoader) validKBFSTLFID(tlfID chat1.TLFID, team *teams.Team) bool {
	tlfIDs := team.KBFSTLFIDs()
	for _, id := range tlfIDs {
		if tlfID.EqString(id) {
			return true
		}
	}
	return false
}

func (t *TeamLoader) validateImpTeamname(ctx context.Context, tlfName string, public bool,
	team *teams.Team) error {
	impTeamName, err := team.ImplicitTeamDisplayName(ctx)
	if err != nil {
		return err
	}
	if impTeamName.String() != tlfName {
		// Try resolving given name, maybe there has been a resolution
		resName, err := teams.ResolveImplicitTeamDisplayName(ctx, t.G(), tlfName, public)
		if err != nil {
			return err
		}
		if impTeamName.String() != resName.String() {
			return ImpteamBadteamError{
				Msg: fmt.Sprintf("mismatch TLF name to implicit team name: %s != %s (resname:%s)",
					impTeamName, tlfName, resName),
			}
		}
	}
	return nil
}

func (t *TeamLoader) loadTeam(ctx context.Context, tlfID chat1.TLFID,
	tlfName string, membersType chat1.ConversationMembersType, public bool,
	loadTeamArgOverride func(keybase1.TeamID) keybase1.LoadTeamArg) (team *teams.Team, err error) {
{'fraction_non_alphanumeric': 0.11243534967393748, 'fraction_numerical': 0.007870474477175624, 'mean_word_length': 4.410381184103811, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '2707677', 'n_tokens_mistral': 4967, 'n_tokens_neox': 4639, 'n_words': 1129}