package ssh

import (
	
	
	
	

	gossh 
)

// contextKey is a value for use with context.WithValue. It's used as
// a pointer so it fits in an interface{} without allocation.
type contextKey struct {
	name string
}

var (
	// ContextKeyUser is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeyUser = &contextKey{"user"}

	// ContextKeySessionID is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeySessionID = &contextKey{"session-id"}

	// ContextKeyPermissions is a context key for use with Contexts in this package.
	// The associated value will be of type *Permissions.
	ContextKeyPermissions = &contextKey{"permissions"}

	// ContextKeyClientVersion is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeyClientVersion = &contextKey{"client-version"}

	// ContextKeyServerVersion is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeyServerVersion = &contextKey{"server-version"}

	// ContextKeyLocalAddr is a context key for use with Contexts in this package.
	// The associated value will be of type net.Addr.
	ContextKeyLocalAddr = &contextKey{"local-addr"}

	// ContextKeyRemoteAddr is a context key for use with Contexts in this package.
	// The associated value will be of type net.Addr.
	ContextKeyRemoteAddr = &contextKey{"remote-addr"}

	// ContextKeyServer is a context key for use with Contexts in this package.
	// The associated value will be of type *Server.
	ContextKeyServer = &contextKey{"ssh-server"}

	// ContextKeyConn is a context key for use with Contexts in this package.
	// The associated value will be of type gossh.ServerConn.
	ContextKeyConn = &contextKey{"ssh-conn"}

	// ContextKeyPublicKey is a context key for use with Contexts in this package.
	// The associated value will be of type PublicKey.
	ContextKeyPublicKey = &contextKey{"public-key"}
)

// Context is a package specific context interface. It exposes connection
// metadata and allows new values to be easily written to it. It's used in
// authentication handlers and callbacks, and its underlying context.Context is
// exposed on Session in the session Handler. A connection-scoped lock is also
// embedded in the context to make it easier to limit operations per-connection.
type Context interface {
	context.Context
	sync.Locker

	// User returns the username used when establishing the SSH connection.
	User() string

	// SessionID returns the session hash.
	SessionID() string

	// ClientVersion returns the version reported by the client.
	ClientVersion() string

	// ServerVersion returns the version reported by the server.
	ServerVersion() string

	// RemoteAddr returns the remote address for this connection.
	RemoteAddr() net.Addr

	// LocalAddr returns the local address for this connection.
	LocalAddr() net.Addr

	// Permissions returns the Permissions object used for this connection.
	Permissions() *Permissions

	// SetValue allows you to easily write new values into the underlying context.
	SetValue(key, value interface{})
}

type sshContext struct {
	context.Context
	*sync.Mutex

	values   map[interface{}]interface{}
	valuesMu sync.Mutex
}

func newContext( *Server) (*sshContext, context.CancelFunc) {
	,  := context.WithCancel(context.Background())
	 := &sshContext{Context: , Mutex: &sync.Mutex{}, values: make(map[interface{}]interface{})}
	.SetValue(ContextKeyServer, )
	 := &Permissions{&gossh.Permissions{}}
	.SetValue(ContextKeyPermissions, )
	return , 
}

// this is separate from newContext because we will get ConnMetadata
// at different points so it needs to be applied separately
func applyConnMetadata( Context,  gossh.ConnMetadata) {
	if .Value(ContextKeySessionID) != nil {
		return
	}
	.SetValue(ContextKeySessionID, hex.EncodeToString(.SessionID()))
	.SetValue(ContextKeyClientVersion, string(.ClientVersion()))
	.SetValue(ContextKeyServerVersion, string(.ServerVersion()))
	.SetValue(ContextKeyUser, .User())
	.SetValue(ContextKeyLocalAddr, .LocalAddr())
	.SetValue(ContextKeyRemoteAddr, .RemoteAddr())
}

func ( *sshContext) ( interface{}) interface{} {
	.valuesMu.Lock()
	defer .valuesMu.Unlock()
	if ,  := .values[];  {
		return 
	}
	return .Context.Value()
}

func ( *sshContext) (,  interface{}) {
	.valuesMu.Lock()
	defer .valuesMu.Unlock()
	.values[] = 
}

func ( *sshContext) () string {
	return .Value(ContextKeyUser).(string)
}

func ( *sshContext) () string {
	return .Value(ContextKeySessionID).(string)
}

func ( *sshContext) () string {
	return .Value(ContextKeyClientVersion).(string)
}

func ( *sshContext) () string {
	return .Value(ContextKeyServerVersion).(string)
}

func ( *sshContext) () net.Addr {
	if ,  := .Value(ContextKeyRemoteAddr).(net.Addr);  {
		return 
	}
	return nil
}

func ( *sshContext) () net.Addr {
	return .Value(ContextKeyLocalAddr).(net.Addr)
}

func ( *sshContext) () *Permissions {
	return .Value(ContextKeyPermissions).(*Permissions)
}