<commit_before>// Copyright (c) 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/urfave/cli"
)

type kernelModule struct {
	// description
	desc string

	// maps parameter names to values
	parameters map[string]string
}

const (
	moduleParamDir = "parameters"
	cpuFlagsTag    = "flags"
)

// variables rather than consts to allow tests to modify them
var (
	sysModuleDir = "/sys/module"
	modInfoCmd   = "modinfo"
)

// requiredCPUFlags maps a CPU flag value to search for and a
// human-readable description of that value.
var requiredCPUFlags = map[string]string{
	"vmx":    "Virtualization support",
	"lm":     "64Bit CPU",
	"sse4_1": "SSE4.1",
}

// requiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for
// and a human-readable description of that value.
var requiredCPUAttribs = map[string]string{
	"GenuineIntel": "Intel Architecture CPU",
}

// requiredKernelModules maps a required module name to a human-readable
// description of the modules functionality and an optional list of
// required module parameters.
var requiredKernelModules = map[string]kernelModule{
	"kvm": {
		desc: "Kernel-based Virtual Machine",
	},
	"kvm_intel": {
		desc: "Intel KVM",
		parameters: map[string]string{
			"nested":             "Y",
			"unrestricted_guest": "Y",
		},
	},
	"vhost": {
		desc: "Host kernel accelerator for virtio",
	},
	"vhost_net": {
		desc: "Host kernel accelerator for virtio network",
	},
}

func getCPUInfo(cpuInfoFile string) (string, error) {
	text, err := getFileContents(cpuInfoFile)
	if err != nil {
		return "", err
	}

	cpus := strings.SplitAfter(text, "\n\n")

	if len(cpus) == 0 {
		return "", fmt.Errorf("Cannot determine CPU details")
	}

	// return details of the first CPU only
	return cpus[0], nil
}

func findAnchoredString(haystack, needle string) bool {
	if haystack == "" || needle == "" {
		return false
	}

	// Ensure the search string is anchored
	pattern := regexp.MustCompile(`\b` + needle + `\b`)

	matched := pattern.MatchString(haystack)

	if matched {
		return true
	}

	return false
}

func getCPUFlags(cpuinfo string) string {
	for _, line := range strings.Split(cpuinfo, "\n") {
		if strings.HasPrefix(line, cpuFlagsTag) {
			fields := strings.Split(line, ":")
			if len(fields) == 2 {
				return strings.TrimSpace(fields[1])
			}
		}
	}

	return ""
}

func haveKernelModule(module string) bool {
	// First, check to see if the module is already loaded
	path := filepath.Join(sysModuleDir, module)
	if fileExists(path) {
		return true
	}

	// Now, check if the module is unloaded, but available
	cmd := exec.Command(modInfoCmd, module)
	err := cmd.Run()
	if err == nil {
		return true
	}

	return false
}

func checkCPU(tag, cpuinfo string, attribs map[string]string) error {
	if cpuinfo == "" {
		return fmt.Errorf("Need cpuinfo")
	}

	for attrib, desc := range attribs {
		found := findAnchoredString(cpuinfo, attrib)
		if found {
			ccLog.Infof("Found CPU %v %q (%s)", tag, desc, attrib)
		} else {
			return fmt.Errorf("CPU does not have required %v: %q (%s)", tag, desc, attrib)
		}
	}

	return nil
}
func checkCPUFlags(cpuflags string, required map[string]string) error {
	return checkCPU("flag", cpuflags, required)
}

func checkCPUAttribs(cpuinfo string, attribs map[string]string) error {
	return checkCPU("attribute", cpuinfo, attribs)
}

func checkKernelModules(modules map[string]kernelModule) error {
	for module, details := range modules {
		if !haveKernelModule(module) {
			return fmt.Errorf("kernel module %q (%s) not found", module, details.desc)
		}

		ccLog.Infof("Found kernel module %q (%s)", details.desc, module)

		for param, expected := range details.parameters {
			path := filepath.Join(sysModuleDir, module, moduleParamDir, param)
			value, err := getFileContents(path)
			if err != nil {
				return err
			}

			value = strings.TrimRight(value, "\n\r")

			if value == expected {
				ccLog.Infof("Kernel module %q parameter %q has correct value", details.desc, param)
			} else {
				return fmt.Errorf("kernel module %q parameter %q has value %q (expected %q)", details.desc, param, value, expected)
			}
		}
	}

	return nil
}

// hostIsClearContainersCapable determines if the system is capable of
// running Clear Containers.
func hostIsClearContainersCapable(cpuinfoFile string) error {
	cpuinfo, err := getCPUInfo(cpuinfoFile)
	if err != nil {
		return err
	}

	if err = checkCPUAttribs(cpuinfo, requiredCPUAttribs); err != nil {
		return err
	}

	cpuFlags := getCPUFlags(cpuinfo)
	if cpuFlags == "" {
		return fmt.Errorf("Cannot find CPU flags")
	}

	if err = checkCPUFlags(cpuFlags, requiredCPUFlags); err != nil {
		return err
	}

	if err = checkKernelModules(requiredKernelModules); err != nil {
		return err
	}

	return nil
}

var ccCheckCommand = cli.Command{
	Name:  "cc-check",
	Usage: "tests if system can run " + project,
	Action: func(context *cli.Context) error {
		err := hostIsClearContainersCapable("/proc/cpuinfo")
		if err != nil {
			return fmt.Errorf("ERROR: %v", err)
		}

		ccLog.Info("")
		ccLog.Info("System is capable of running " + project)

		return nil
	},
}
<commit_msg>cc-check: Make the "/proc/cpuinfo" string a const.<commit_after>// Copyright (c) 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/urfave/cli"
)

type kernelModule struct {
	// description
	desc string

	// maps parameter names to values
	parameters map[string]string
}

const (
	procCPUInfo    = "/proc/cpuinfo"
	moduleParamDir = "parameters"
	cpuFlagsTag    = "flags"
)

// variables rather than consts to allow tests to modify them
var (
	sysModuleDir = "/sys/module"
	modInfoCmd   = "modinfo"
)

// requiredCPUFlags maps a CPU flag value to search for and a
// human-readable description of that value.
var requiredCPUFlags = map[string]string{
	"vmx":    "Virtualization support",
	"lm":     "64Bit CPU",
	"sse4_1": "SSE4.1",
}

// requiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for
// and a human-readable description of that value.
var requiredCPUAttribs = map[string]string{
	"GenuineIntel": "Intel Architecture CPU",
}

// requiredKernelModules maps a required module name to a human-readable
// description of the modules functionality and an optional list of
// required module parameters.
var requiredKernelModules = map[string]kernelModule{
	"kvm": {
		desc: "Kernel-based Virtual Machine",
	},
	"kvm_intel": {
		desc: "Intel KVM",
		parameters: map[string]string{
			"nested":             "Y",
			"unrestricted_guest": "Y",
		},
	},
	"vhost": {
		desc: "Host kernel accelerator for virtio",
	},
	"vhost_net": {
		desc: "Host kernel accelerator for virtio network",
	},
}

func getCPUInfo(cpuInfoFile string) (string, error) {
	text, err := getFileContents(cpuInfoFile)
	if err != nil {
		return "", err
	}

	cpus := strings.SplitAfter(text, "\n\n")

	if len(cpus) == 0 {
		return "", fmt.Errorf("Cannot determine CPU details")
	}

	// return details of the first CPU only
	return cpus[0], nil
}

func findAnchoredString(haystack, needle string) bool {
	if haystack == "" || needle == "" {
		return false
	}

	// Ensure the search string is anchored
	pattern := regexp.MustCompile(`\b` + needle + `\b`)

	matched := pattern.MatchString(haystack)

	if matched {
		return true
	}

	return false
}

func getCPUFlags(cpuinfo string) string {
	for _, line := range strings.Split(cpuinfo, "\n") {
		if strings.HasPrefix(line, cpuFlagsTag) {
			fields := strings.Split(line, ":")
			if len(fields) == 2 {
				return strings.TrimSpace(fields[1])
			}
		}
	}

	return ""
}

func haveKernelModule(module string) bool {
	// First, check to see if the module is already loaded
	path := filepath.Join(sysModuleDir, module)
	if fileExists(path) {
		return true
	}

	// Now, check if the module is unloaded, but available
	cmd := exec.Command(modInfoCmd, module)
	err := cmd.Run()
	if err == nil {
		return true
	}

	return false
}

func checkCPU(tag, cpuinfo string, attribs map[string]string) error {
	if cpuinfo == "" {
		return fmt.Errorf("Need cpuinfo")
	}

	for attrib, desc := range attribs {
		found := findAnchoredString(cpuinfo, attrib)
		if found {
			ccLog.Infof("Found CPU %v %q (%s)", tag, desc, attrib)
		} else {
			return fmt.Errorf("CPU does not have required %v: %q (%s)", tag, desc, attrib)
		}
	}

	return nil
}
func checkCPUFlags(cpuflags string, required map[string]string) error {
	return checkCPU("flag", cpuflags, required)
}

func checkCPUAttribs(cpuinfo string, attribs map[string]string) error {
	return checkCPU("attribute", cpuinfo, attribs)
}

func checkKernelModules(modules map[string]kernelModule) error {
	for module, details := range modules {
		if !haveKernelModule(module) {
			return fmt.Errorf("kernel module %q (%s) not found", module, details.desc)
		}

		ccLog.Infof("Found kernel module %q (%s)", details.desc, module)

		for param, expected := range details.parameters {
			path := filepath.Join(sysModuleDir, module, moduleParamDir, param)
			value, err := getFileContents(path)
			if err != nil {
				return err
			}

			value = strings.TrimRight(value, "\n\r")

			if value == expected {
				ccLog.Infof("Kernel module %q parameter %q has correct value", details.desc, param)
			} else {
				return fmt.Errorf("kernel module %q parameter %q has value %q (expected %q)", details.desc, param, value, expected)
			}
		}
	}

	return nil
}

// hostIsClearContainersCapable determines if the system is capable of
// running Clear Containers.
func hostIsClearContainersCapable(cpuinfoFile string) error {
	cpuinfo, err := getCPUInfo(cpuinfoFile)
	if err != nil {
		return err
	}

	if err = checkCPUAttribs(cpuinfo, requiredCPUAttribs); err != nil {
		return err
	}

	cpuFlags := getCPUFlags(cpuinfo)
	if cpuFlags == "" {
		return fmt.Errorf("Cannot find CPU flags")
	}

	if err = checkCPUFlags(cpuFlags, requiredCPUFlags); err != nil {
		return err
	}

	if err = checkKernelModules(requiredKernelModules); err != nil {
		return err
	}

	return nil
}

var ccCheckCommand = cli.Command{
	Name:  "cc-check",
	Usage: "tests if system can run " + project,
	Action: func(context *cli.Context) error {
		err := hostIsClearContainersCapable(procCPUInfo)
		if err != nil {
			return fmt.Errorf("ERROR: %v", err)
		}

		ccLog.Info("")
		ccLog.Info("System is capable of running " + project)

		return nil
	},
}
<|endoftext|>


<commit_before>//
// mod_http.go
//
// http mod panel
//

package srnd

import (
  "github.com/majestrate/srndv2/src/nacl"
  "encoding/hex"
  "log"
)

type httpModUI struct {
  modMessageChan chan *NNTPMessage
  database Database
}

func createHttpModUI(daemon *NNTPDaemon) httpModUI {
  return httpModUI{make(chan *NNTPMessage), daemon.database}
}

func (self httpModUI) CheckKey(privkey string) bool {
  privkey_bytes, err := hex.DecodeString(privkey)
  if err == nil {
    pubkey_bytes := nacl.GetSignPubkey(privkey_bytes)
    pubkey := hex.EncodeToString(pubkey_bytes)
    return self.database.CheckModPubkey(pubkey)
  }
  log.Println("invalid key format for key", privkey)
  return false
}

func (self httpModUI) MessageChan() chan *NNTPMessage {
  return self.modMessageChan
}
<commit_msg>check for invalid key returned from nacl.GetSignPubkey<commit_after>//
// mod_http.go
//
// http mod panel
//

package srnd

import (
  "github.com/majestrate/srndv2/src/nacl"
  "encoding/hex"
  "log"
)

type httpModUI struct {
  modMessageChan chan *NNTPMessage
  database Database
}

func createHttpModUI(daemon *NNTPDaemon) httpModUI {
  return httpModUI{make(chan *NNTPMessage), daemon.database}
}

func (self httpModUI) CheckKey(privkey string) bool {
  privkey_bytes, err := hex.DecodeString(privkey)
  if err == nil {
    pubkey_bytes := nacl.GetSignPubkey(privkey_bytes)
    if pubkey_bytes != nil {
      pubkey := hex.EncodeToString(pubkey_bytes)
      return self.database.CheckModPubkey(pubkey)
    }
  }
  log.Println("invalid key format for key", privkey)
  return false
}

func (self httpModUI) MessageChan() chan *NNTPMessage {
  return self.modMessageChan
}
<|endoftext|>


<commit_before>package gopool

import (
	"errors"

	"github.com/karrick/gorill"
)

// ChanPool implements the Pool interface, maintaining a pool of resources.
type ChanPool struct {
	ch chan interface{}
	pc config
}

// New creates a new Pool. The factory method used to create new items for the Pool must be
// specified using the gopool.Factory method. Optionally, the pool size and a reset function can be
// specified.
//
//        package main
//
//        import (
//        	"log"
//        	"github.com/karrick/gopool"
//        )
//
//        func main() {
//              makeBuffer := func() (interface{}, error) {
//                  return new(bytes.Buffer), nil
//              }
//
//              resetBuffer := func(item interface{}) {
//                  item.(*bytes.Buffer).Reset()
//              }
//
//        	bp, err := gopool.New(gopool.Factory(makeBuffer),
//                             gopool.Size(25), gopool.Reset(resetBuffer))
//        	if err != nil {
//        		log.Fatal(err)
//        	}
//        	for i := 0; i < 100; i++ {
//        		go func() {
//        			for j := 0; j < 1000; j++ {
//        				bb := bp.Get().(*bytes.Buffer)
//        				for k := 0; k < 4096; k++ {
//        					bb.WriteByte(byte(k % 256))
//        				}
//        				bp.Put(bb) // NOTE: bb.Reset() called by resetBuffer
//        			}
//        		}()
//        	}
//        }
func New(setters ...Configurator) (Pool, error) {
	pc := &config{
		size: DefaultSize,
	}
	for _, setter := range setters {
		if err := setter(pc); err != nil {
			return nil, err
		}
	}
	if pc.factory == nil {
		return nil, errors.New("ought to specify factory method")
	}
	pool := &ChanPool{
		ch: make(chan interface{}, pc.size),
		pc: *pc,
	}
	for i := 0; i < pool.pc.size; i++ {
		item, err := pool.pc.factory()
		if err != nil {
			return nil, err
		}
		pool.ch <- item
	}
	return pool, nil
}

// Get acquires and returns an item from the pool of resources.
func (pool *ChanPool) Get() interface{} {
	return <-pool.ch
}

// Put will release a resource back to the pool.  If the Pool was initialized with a Reset function,
// it will be invoked with the resource as its sole argument, prior to the resource being added back
// to the pool.
func (pool *ChanPool) Put(item interface{}) {
	if pool.pc.reset != nil {
		pool.pc.reset(item)
	}
	pool.ch <- item
}

// Close is called when the Pool is no longer needed, and the resources in the Pool ought to be
// released.  If a Pool has a close function, it will be invoked one time for each resource, with
// that resource as its sole argument.
func (pool *ChanPool) Close() error {
	var errors gorill.ErrList
	if pool.pc.close != nil {
		for {
			select {
			case item := <-pool.ch:
				errors.Append(pool.pc.close(item))
			default:
				return errors.Err()
			}
		}
	}
	return nil
}
<commit_msg>uses gopkg.in/karrick/gorill.v1<commit_after>package gopool

import (
	"errors"

	gorill "gopkg.in/karrick/gorill.v1"
)

// ChanPool implements the Pool interface, maintaining a pool of resources.
type ChanPool struct {
	ch chan interface{}
	pc config
}

// New creates a new Pool. The factory method used to create new items for the Pool must be
// specified using the gopool.Factory method. Optionally, the pool size and a reset function can be
// specified.
//
//        package main
//
//        import (
//        	"log"
//        	"github.com/karrick/gopool"
//        )
//
//        func main() {
//              makeBuffer := func() (interface{}, error) {
//                  return new(bytes.Buffer), nil
//              }
//
//              resetBuffer := func(item interface{}) {
//                  item.(*bytes.Buffer).Reset()
//              }
//
//        	bp, err := gopool.New(gopool.Factory(makeBuffer),
//                             gopool.Size(25), gopool.Reset(resetBuffer))
//        	if err != nil {
//        		log.Fatal(err)
//        	}
//        	for i := 0; i < 100; i++ {
//        		go func() {
//        			for j := 0; j < 1000; j++ {
//        				bb := bp.Get().(*bytes.Buffer)
//        				for k := 0; k < 4096; k++ {
//        					bb.WriteByte(byte(k % 256))
//        				}
//        				bp.Put(bb) // NOTE: bb.Reset() called by resetBuffer
//        			}
//        		}()
//        	}
//        }
func New(setters ...Configurator) (Pool, error) {
	pc := &config{
		size: DefaultSize,
	}
	for _, setter := range setters {
		if err := setter(pc); err != nil {
			return nil, err
		}
	}
	if pc.factory == nil {
		return nil, errors.New("ought to specify factory method")
	}
	pool := &ChanPool{
		ch: make(chan interface{}, pc.size),
		pc: *pc,
	}
	for i := 0; i < pool.pc.size; i++ {
		item, err := pool.pc.factory()
		if err != nil {
			return nil, err
		}
		pool.ch <- item
	}
	return pool, nil
}

// Get acquires and returns an item from the pool of resources.
func (pool *ChanPool) Get() interface{} {
	return <-pool.ch
}

// Put will release a resource back to the pool.  If the Pool was initialized with a Reset function,
// it will be invoked with the resource as its sole argument, prior to the resource being added back
// to the pool.
func (pool *ChanPool) Put(item interface{}) {
	if pool.pc.reset != nil {
		pool.pc.reset(item)
	}
	pool.ch <- item
}

// Close is called when the Pool is no longer needed, and the resources in the Pool ought to be
// released.  If a Pool has a close function, it will be invoked one time for each resource, with
// that resource as its sole argument.
func (pool *ChanPool) Close() error {
	var errors gorill.ErrList
	if pool.pc.close != nil {
		for {
			select {
			case item := <-pool.ch:
				errors.Append(pool.pc.close(item))
			default:
				return errors.Err()
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package session

import (
  "fmt"
  "github.com/rkbodenner/parallel_universe/game"
)

type Session struct {
  Id uint `json:"id"`
  Game *game.Game
  Players []*game.Player
  SetupAssignments StepAssignments
  SetupSteps []game.SetupStep
  freeSetupSteps map[game.SetupStep]bool
}

func NewSession(g *game.Game, players []*game.Player) *Session {
  setupSteps := make([]game.SetupStep, 0)
  for _,rule := range g.SetupRules {
    if "Once" == rule.Arity {
      step, err := game.NewGlobalSetupStep(rule)
      if nil != err {
        fmt.Println(err)
      }
      setupSteps = append(setupSteps, step)
    } else if "Each player" == rule.Arity {
      for _,p := range players {
        step, err := game.NewSinglePlayerSetupStep(rule, p)
        if nil != err {
          fmt.Println(err)
        }
        setupSteps = append(setupSteps, step)
      }
    }
  }

  freeSetupSteps := make(map[game.SetupStep]bool)
  for _,step := range setupSteps {
    freeSetupSteps[step] = true
  }

  return &Session{
    Game: g,
    Players: players,
    SetupAssignments: NewStepMap(),
    SetupSteps: setupSteps,
    freeSetupSteps: freeSetupSteps,
  }
}

func (session *Session) PlayerCount() int {
  return len(session.Players)
}

func (session *Session) findNextUndoneSetupStep(player *game.Player) (game.SetupStep, error) {
  for step,_ := range session.freeSetupSteps {
    if step.CanBeOwnedBy(player) && !step.IsDone() {
      return step, nil
    }
  }
  return nil, fmt.Errorf("No undone steps available for %s", player.Name)
}

func (session *Session) Step(player *game.Player) game.SetupStep {
  step,assigned := session.SetupAssignments.Get(player)
  if !assigned || (assigned && step.IsDone()) {
    nextStep,error := session.findNextUndoneSetupStep(player)
    if ( error != nil ) {
      fmt.Println(error.Error())
      return step
    }
    session.SetupAssignments.Set(player, nextStep)
    return nextStep
  }
  return step
}
<commit_msg>Remove unused method<commit_after>package session

import (
  "fmt"
  "github.com/rkbodenner/parallel_universe/game"
)

type Session struct {
  Id uint `json:"id"`
  Game *game.Game
  Players []*game.Player
  SetupAssignments StepAssignments
  SetupSteps []game.SetupStep
  freeSetupSteps map[game.SetupStep]bool
}

func NewSession(g *game.Game, players []*game.Player) *Session {
  setupSteps := make([]game.SetupStep, 0)
  for _,rule := range g.SetupRules {
    if "Once" == rule.Arity {
      step, err := game.NewGlobalSetupStep(rule)
      if nil != err {
        fmt.Println(err)
      }
      setupSteps = append(setupSteps, step)
    } else if "Each player" == rule.Arity {
      for _,p := range players {
        step, err := game.NewSinglePlayerSetupStep(rule, p)
        if nil != err {
          fmt.Println(err)
        }
        setupSteps = append(setupSteps, step)
      }
    }
  }

  freeSetupSteps := make(map[game.SetupStep]bool)
  for _,step := range setupSteps {
    freeSetupSteps[step] = true
  }

  return &Session{
    Game: g,
    Players: players,
    SetupAssignments: NewStepMap(),
    SetupSteps: setupSteps,
    freeSetupSteps: freeSetupSteps,
  }
}

func (session *Session) findNextUndoneSetupStep(player *game.Player) (game.SetupStep, error) {
  for step,_ := range session.freeSetupSteps {
    if step.CanBeOwnedBy(player) && !step.IsDone() {
      return step, nil
    }
  }
  return nil, fmt.Errorf("No undone steps available for %s", player.Name)
}

func (session *Session) Step(player *game.Player) game.SetupStep {
  step,assigned := session.SetupAssignments.Get(player)
  if !assigned || (assigned && step.IsDone()) {
    nextStep,error := session.findNextUndoneSetupStep(player)
    if ( error != nil ) {
      fmt.Println(error.Error())
      return step
    }
    session.SetupAssignments.Set(player, nextStep)
    return nextStep
  }
  return step
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package session

import (
	"encoding/hex"
	"encoding/json"
	"errors"
	"github.com/twinj/uuid"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const (
	// The key for the identity of the session
	SessionIDKey = "_ID"
	// The expiration date of the session
	TimestampKey = "_TS"
	// The value name indicating how long the session should persist - ie should it persist after the browser closes
	// this is set under the TimestampKey if the session data should expire immediately
	SessionValueName = "session"
	// The key container for the json objects of the data, any non strings found in the map will be placed in here
	// serialized by key using JSON
	SessionObjectKeyName = "_object_"
	// The mapped session object
	SessionMapKeyName = "_map_"
	// The suffix of the session cookie
	SessionCookieSuffix = "_SESSION"
)

// Session data, can be any data, there are reserved keywords used by the storage data
// SessionIDKey Is the key name for the session
// TimestampKey Is the time that the session should expire
//
type Session map[string]interface{}

func NewSession() Session {
	return Session{}
}

// ID retrieves from the cookie or creates a time-based UUID identifying this
// session.
func (s Session) ID() string {
	if sessionIDStr, ok := s[SessionIDKey]; ok {
		return sessionIDStr.(string)
	}

	buffer := uuid.NewV4()

	s[SessionIDKey] = hex.EncodeToString(buffer.Bytes())
	return s[SessionIDKey].(string)
}

// getExpiration return a time.Time with the session's expiration date.
// It uses the passed in expireAfterDuration to add with the current time if the timeout is not
// browser dependent (ie session). If previous session has set to "session", the time returned is time.IsZero()
func (s Session) GetExpiration(expireAfterDuration time.Duration) time.Time {
	if expireAfterDuration == 0 || s[TimestampKey] == SessionValueName {
		// Expire after closing browser
		return time.Time{}
	}
	return time.Now().Add(expireAfterDuration)
}

// SetNoExpiration sets session to expire when browser session ends
func (s Session) SetNoExpiration() {
	s[TimestampKey] = SessionValueName
}

// SetDefaultExpiration sets session to expire after default duration
func (s Session) SetDefaultExpiration() {
	delete(s, TimestampKey)
}

// sessionTimeoutExpiredOrMissing returns a boolean of whether the session
// cookie is either not present or present but beyond its time to live; i.e.,
// whether there is not a valid session.
func (s Session) SessionTimeoutExpiredOrMissing() bool {
	if exp, present := s[TimestampKey]; !present {
		return true
	} else if exp == SessionValueName {
		return false
	} else if expInt, _ := strconv.Atoi(exp.(string)); int64(expInt) < time.Now().Unix() {
		return true
	}
	return false
}

// Constant error if session value is not found
var SESSION_VALUE_NOT_FOUND = errors.New("Session value not found")

// Get an object or property from the session
// it may be embedded inside the session.
func (s Session) Get(key string) (newValue interface{}, err error) {
	// First check to see if it is in the session
	if v, found := s[key]; found {
		return v, nil
	}
	return s.GetInto(key, nil, false)
}

// Get into the specified value.
// If value exists in the session it will just return the value
func (s Session) GetInto(key string, target interface{}, force bool) (result interface{}, err error) {
	if v, found := s[key]; found && !force {
		return v, nil
	}
	splitKey := strings.Split(key, ".")
	rootKey := splitKey[0]

	// Force always recreates the object from the session data map
	if force {
		if target == nil {
			if result, err = s.sessionDataFromMap(key); err != nil {
				return
			}
		} else if result, err = s.sessionDataFromObject(rootKey, target); err != nil {
			return
		}

		return s.getNestedProperty(splitKey, result)
	}

	// Attempt to find the key in the session, this is the most generalized form
	v, found := s[rootKey]
	if !found {
		if target == nil {
			// Try to fetch it from the session

			if v, err = s.sessionDataFromMap(rootKey); err != nil {
				return
			}
		} else if v, err = s.sessionDataFromObject(rootKey, target); err != nil {
			return
		}
	}

	return s.getNestedProperty(splitKey, v)
}

// Returns the default value if the key is not found
func (s Session) GetDefault(key string, value interface{}, defaultValue interface{}) interface{} {
	v, e := s.GetInto(key, value, false)
	if e != nil {
		v = defaultValue
	}
	return v
}

// Extract the values from the session
func (s Session) GetProperty(key string, value interface{}) (interface{}, error) {
	// Capitalize the first letter
	key = strings.Title(key)

	sessionLog.Info("getProperty", "key", key, "value", value)

	// For a map it is easy
	if reflect.TypeOf(value).Kind() == reflect.Map {
		val := reflect.ValueOf(value)
		valueOf := val.MapIndex(reflect.ValueOf(key))
		if valueOf == reflect.Zero(reflect.ValueOf(value).Type()) {
			return nil, nil
		}
		//idx := val.MapIndex(reflect.ValueOf(key))
		if !valueOf.IsValid() {
			return nil, nil
		}

		return valueOf.Interface(), nil
	}

	objValue := s.reflectValue(value)
	field := objValue.FieldByName(key)
	if !field.IsValid() {
		return nil, SESSION_VALUE_NOT_FOUND
	}

	return field.Interface(), nil
}

// Places the object into the session, a nil value will cause remove the key from the session
// (or you can use the Session.Del(key) function
func (s Session) Set(key string, value interface{}) error {
	if value == nil {
		s.Del(key)
		return nil
	}

	s[key] = value
	return nil
}

// Delete the key from the sessionObjects and Session
func (s Session) Del(key string) {
	sessionJsonMap := s.getSessionJsonMap()
	delete(sessionJsonMap, key)
	delete(s, key)
}

// Extracts the session as a map of [string keys] and json values
func (s Session) getSessionJsonMap() map[string]string {
	if sessionJson, found := s[SessionObjectKeyName]; found {
		if _, valid := sessionJson.(map[string]string); !valid {
			sessionLog.Error("Session object key corrupted, reset", "was", sessionJson)
			s[SessionObjectKeyName] = map[string]string{}
		}
		// serialized data inside the session _objects
	} else {
		s[SessionObjectKeyName] = map[string]string{}
	}

	return s[SessionObjectKeyName].(map[string]string)
}

// Convert the map to a simple map[string]string map
// this will marshal any non string objects encountered and store them the the jsonMap
// The expiration time will also be assigned
func (s Session) Serialize() map[string]string {
	sessionJsonMap := s.getSessionJsonMap()
	newMap := map[string]string{}
	newObjectMap := map[string]string{}
	for key, value := range sessionJsonMap {
		newObjectMap[key] = value
	}
	for key, value := range s {
		if key == SessionObjectKeyName || key == SessionMapKeyName {
			continue
		}
		if reflect.ValueOf(value).Kind() == reflect.String {
			newMap[key] = value.(string)
			continue
		}
		println("Serialize the data for", key)
		if data, err := json.Marshal(value); err != nil {
			sessionLog.Error("Unable to marshal session ", "key", key, "error", err)
			continue
		} else {
			newObjectMap[key] = string(data)
		}
	}
	if len(newObjectMap) > 0 {
		if data, err := json.Marshal(newObjectMap); err != nil {
			sessionLog.Error("Unable to marshal session ", "key", SessionObjectKeyName, "error", err)

		} else {
			newMap[SessionObjectKeyName] = string(data)
		}
	}

	return newMap
}

// Set the session object from the loaded data
func (s Session) Load(data map[string]string) {
	for key, value := range data {
		if key == SessionObjectKeyName {
			target := map[string]string{}
			if err := json.Unmarshal([]byte(value), &target); err != nil {
				sessionLog.Error("Unable to unmarshal session ", "key", SessionObjectKeyName, "error", err)
			} else {
				s[key] = target
			}
		} else {
			s[key] = value
		}

	}
}

// Checks to see if the session is empty
func (s Session) Empty() bool {
	i := 0
	for k := range s {
		i++
		if k == SessionObjectKeyName || k == SessionMapKeyName {
			continue
		}
	}
	return i == 0
}

func (s *Session) reflectValue(obj interface{}) reflect.Value {
	var val reflect.Value

	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		val = reflect.ValueOf(obj).Elem()
	} else {
		val = reflect.ValueOf(obj)
	}

	return val
}

// Starting at position 1 drill into the object
func (s Session) getNestedProperty(keys []string, newValue interface{}) (result interface{}, err error) {
	for x := 1; x < len(keys); x++ {
		newValue, err = s.GetProperty(keys[x], newValue)
		if err != nil || newValue == nil {
			return newValue, err
		}
	}
	return newValue, nil
}

// Always converts the data from the session mapped objects into the target,
// it will store the results under the session key name SessionMapKeyName
func (s Session) sessionDataFromMap(key string) (result interface{}, err error) {
	var mapValue map[string]interface{}
	uncastMapValue, found := s[SessionMapKeyName]
	if !found {
		mapValue = map[string]interface{}{}
		s[SessionMapKeyName] = mapValue
	} else if mapValue, found = uncastMapValue.(map[string]interface{}); !found {
		// Unusual means that the value in the session was not expected
		sessionLog.Errorf("Unusual means that the value in the session was not expected", "session", uncastMapValue)
		mapValue = map[string]interface{}{}
		s[SessionMapKeyName] = mapValue
	}

	// Try to extract the key from the map
	result, found = mapValue[key]
	if !found {
		result, err = s.convertSessionData(key, nil)
		if err == nil {
			mapValue[key] = result
		}
	}
	return
}

// Unpack the object from the session map and store it in the session when done, if no error occurs
func (s Session) sessionDataFromObject(key string, newValue interface{}) (result interface{}, err error) {
	result, err = s.convertSessionData(key, newValue)
	if err != nil {
		return
	}
	s[key] = result
	return
}

// Converts from the session json map into the target,
func (s Session) convertSessionData(key string, target interface{}) (result interface{}, err error) {
	sessionJsonMap := s.getSessionJsonMap()
	v, found := sessionJsonMap[key]
	if !found {
		return target, SESSION_VALUE_NOT_FOUND
	}

	// Create a target if needed
	if target == nil {
		target = map[string]interface{}{}
		if err := json.Unmarshal([]byte(v), &target); err != nil {
			return target, err
		}
	} else if err := json.Unmarshal([]byte(v), target); err != nil {
		return target, err
	}
	result = target
	return
}
<commit_msg>Remove a stray println call.<commit_after>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package session

import (
	"encoding/hex"
	"encoding/json"
	"errors"
	"github.com/twinj/uuid"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const (
	// The key for the identity of the session
	SessionIDKey = "_ID"
	// The expiration date of the session
	TimestampKey = "_TS"
	// The value name indicating how long the session should persist - ie should it persist after the browser closes
	// this is set under the TimestampKey if the session data should expire immediately
	SessionValueName = "session"
	// The key container for the json objects of the data, any non strings found in the map will be placed in here
	// serialized by key using JSON
	SessionObjectKeyName = "_object_"
	// The mapped session object
	SessionMapKeyName = "_map_"
	// The suffix of the session cookie
	SessionCookieSuffix = "_SESSION"
)

// Session data, can be any data, there are reserved keywords used by the storage data
// SessionIDKey Is the key name for the session
// TimestampKey Is the time that the session should expire
//
type Session map[string]interface{}

func NewSession() Session {
	return Session{}
}

// ID retrieves from the cookie or creates a time-based UUID identifying this
// session.
func (s Session) ID() string {
	if sessionIDStr, ok := s[SessionIDKey]; ok {
		return sessionIDStr.(string)
	}

	buffer := uuid.NewV4()

	s[SessionIDKey] = hex.EncodeToString(buffer.Bytes())
	return s[SessionIDKey].(string)
}

// getExpiration return a time.Time with the session's expiration date.
// It uses the passed in expireAfterDuration to add with the current time if the timeout is not
// browser dependent (ie session). If previous session has set to "session", the time returned is time.IsZero()
func (s Session) GetExpiration(expireAfterDuration time.Duration) time.Time {
	if expireAfterDuration == 0 || s[TimestampKey] == SessionValueName {
		// Expire after closing browser
		return time.Time{}
	}
	return time.Now().Add(expireAfterDuration)
}

// SetNoExpiration sets session to expire when browser session ends
func (s Session) SetNoExpiration() {
	s[TimestampKey] = SessionValueName
}

// SetDefaultExpiration sets session to expire after default duration
func (s Session) SetDefaultExpiration() {
	delete(s, TimestampKey)
}

// sessionTimeoutExpiredOrMissing returns a boolean of whether the session
// cookie is either not present or present but beyond its time to live; i.e.,
// whether there is not a valid session.
func (s Session) SessionTimeoutExpiredOrMissing() bool {
	if exp, present := s[TimestampKey]; !present {
		return true
	} else if exp == SessionValueName {
		return false
	} else if expInt, _ := strconv.Atoi(exp.(string)); int64(expInt) < time.Now().Unix() {
		return true
	}
	return false
}

// Constant error if session value is not found
var SESSION_VALUE_NOT_FOUND = errors.New("Session value not found")

// Get an object or property from the session
// it may be embedded inside the session.
func (s Session) Get(key string) (newValue interface{}, err error) {
	// First check to see if it is in the session
	if v, found := s[key]; found {
		return v, nil
	}
	return s.GetInto(key, nil, false)
}

// Get into the specified value.
// If value exists in the session it will just return the value
func (s Session) GetInto(key string, target interface{}, force bool) (result interface{}, err error) {
	if v, found := s[key]; found && !force {
		return v, nil
	}
	splitKey := strings.Split(key, ".")
	rootKey := splitKey[0]

	// Force always recreates the object from the session data map
	if force {
		if target == nil {
			if result, err = s.sessionDataFromMap(key); err != nil {
				return
			}
		} else if result, err = s.sessionDataFromObject(rootKey, target); err != nil {
			return
		}

		return s.getNestedProperty(splitKey, result)
	}

	// Attempt to find the key in the session, this is the most generalized form
	v, found := s[rootKey]
	if !found {
		if target == nil {
			// Try to fetch it from the session

			if v, err = s.sessionDataFromMap(rootKey); err != nil {
				return
			}
		} else if v, err = s.sessionDataFromObject(rootKey, target); err != nil {
			return
		}
	}

	return s.getNestedProperty(splitKey, v)
}

// Returns the default value if the key is not found
func (s Session) GetDefault(key string, value interface{}, defaultValue interface{}) interface{} {
	v, e := s.GetInto(key, value, false)
	if e != nil {
		v = defaultValue
	}
	return v
}

// Extract the values from the session
func (s Session) GetProperty(key string, value interface{}) (interface{}, error) {
	// Capitalize the first letter
	key = strings.Title(key)

	sessionLog.Info("getProperty", "key", key, "value", value)

	// For a map it is easy
	if reflect.TypeOf(value).Kind() == reflect.Map {
		val := reflect.ValueOf(value)
		valueOf := val.MapIndex(reflect.ValueOf(key))
		if valueOf == reflect.Zero(reflect.ValueOf(value).Type()) {
			return nil, nil
		}
		//idx := val.MapIndex(reflect.ValueOf(key))
		if !valueOf.IsValid() {
			return nil, nil
		}

		return valueOf.Interface(), nil
	}

	objValue := s.reflectValue(value)
	field := objValue.FieldByName(key)
	if !field.IsValid() {
		return nil, SESSION_VALUE_NOT_FOUND
	}

	return field.Interface(), nil
}

// Places the object into the session, a nil value will cause remove the key from the session
// (or you can use the Session.Del(key) function
func (s Session) Set(key string, value interface{}) error {
	if value == nil {
		s.Del(key)
		return nil
	}

	s[key] = value
	return nil
}

// Delete the key from the sessionObjects and Session
func (s Session) Del(key string) {
	sessionJsonMap := s.getSessionJsonMap()
	delete(sessionJsonMap, key)
	delete(s, key)
}

// Extracts the session as a map of [string keys] and json values
func (s Session) getSessionJsonMap() map[string]string {
	if sessionJson, found := s[SessionObjectKeyName]; found {
		if _, valid := sessionJson.(map[string]string); !valid {
			sessionLog.Error("Session object key corrupted, reset", "was", sessionJson)
			s[SessionObjectKeyName] = map[string]string{}
		}
		// serialized data inside the session _objects
	} else {
		s[SessionObjectKeyName] = map[string]string{}
	}

	return s[SessionObjectKeyName].(map[string]string)
}

// Convert the map to a simple map[string]string map
// this will marshal any non string objects encountered and store them the the jsonMap
// The expiration time will also be assigned
func (s Session) Serialize() map[string]string {
	sessionJsonMap := s.getSessionJsonMap()
	newMap := map[string]string{}
	newObjectMap := map[string]string{}
	for key, value := range sessionJsonMap {
		newObjectMap[key] = value
	}
	for key, value := range s {
		if key == SessionObjectKeyName || key == SessionMapKeyName {
			continue
		}
		if reflect.ValueOf(value).Kind() == reflect.String {
			newMap[key] = value.(string)
			continue
		}
		if data, err := json.Marshal(value); err != nil {
			sessionLog.Error("Unable to marshal session ", "key", key, "error", err)
			continue
		} else {
			newObjectMap[key] = string(data)
		}
	}
	if len(newObjectMap) > 0 {
		if data, err := json.Marshal(newObjectMap); err != nil {
			sessionLog.Error("Unable to marshal session ", "key", SessionObjectKeyName, "error", err)

		} else {
			newMap[SessionObjectKeyName] = string(data)
		}
	}

	return newMap
}

// Set the session object from the loaded data
func (s Session) Load(data map[string]string) {
	for key, value := range data {
		if key == SessionObjectKeyName {
			target := map[string]string{}
			if err := json.Unmarshal([]byte(value), &target); err != nil {
				sessionLog.Error("Unable to unmarshal session ", "key", SessionObjectKeyName, "error", err)
			} else {
				s[key] = target
			}
		} else {
			s[key] = value
		}

	}
}

// Checks to see if the session is empty
func (s Session) Empty() bool {
	i := 0
	for k := range s {
		i++
		if k == SessionObjectKeyName || k == SessionMapKeyName {
			continue
		}
	}
	return i == 0
}

func (s *Session) reflectValue(obj interface{}) reflect.Value {
	var val reflect.Value

	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		val = reflect.ValueOf(obj).Elem()
	} else {
		val = reflect.ValueOf(obj)
	}

	return val
}

// Starting at position 1 drill into the object
func (s Session) getNestedProperty(keys []string, newValue interface{}) (result interface{}, err error) {
	for x := 1; x < len(keys); x++ {
		newValue, err = s.GetProperty(keys[x], newValue)
		if err != nil || newValue == nil {
			return newValue, err
		}
	}
	return newValue, nil
}

// Always converts the data from the session mapped objects into the target,
// it will store the results under the session key name SessionMapKeyName
func (s Session) sessionDataFromMap(key string) (result interface{}, err error) {
	var mapValue map[string]interface{}
	uncastMapValue, found := s[SessionMapKeyName]
	if !found {
		mapValue = map[string]interface{}{}
		s[SessionMapKeyName] = mapValue
	} else if mapValue, found = uncastMapValue.(map[string]interface{}); !found {
		// Unusual means that the value in the session was not expected
		sessionLog.Errorf("Unusual means that the value in the session was not expected", "session", uncastMapValue)
		mapValue = map[string]interface{}{}
		s[SessionMapKeyName] = mapValue
	}

	// Try to extract the key from the map
	result, found = mapValue[key]
	if !found {
		result, err = s.convertSessionData(key, nil)
		if err == nil {
			mapValue[key] = result
		}
	}
	return
}

// Unpack the object from the session map and store it in the session when done, if no error occurs
func (s Session) sessionDataFromObject(key string, newValue interface{}) (result interface{}, err error) {
	result, err = s.convertSessionData(key, newValue)
	if err != nil {
		return
	}
	s[key] = result
	return
}

// Converts from the session json map into the target,
func (s Session) convertSessionData(key string, target interface{}) (result interface{}, err error) {
	sessionJsonMap := s.getSessionJsonMap()
	v, found := sessionJsonMap[key]
	if !found {
		return target, SESSION_VALUE_NOT_FOUND
	}

	// Create a target if needed
	if target == nil {
		target = map[string]interface{}{}
		if err := json.Unmarshal([]byte(v), &target); err != nil {
			return target, err
		}
	} else if err := json.Unmarshal([]byte(v), target); err != nil {
		return target, err
	}
	result = target
	return
}
<|endoftext|>


<commit_before>// Copyright (c) nano Author. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package session

import (
	"errors"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/lonnng/nano/service"
)

type NetworkEntity interface {
	Push(route string, v interface{}) error
	Response(v interface{}) error
	Close() error
	RemoteAddr() net.Addr
}

var (
	ErrIllegalUID = errors.New("illegal uid")
)

// Session represents a client session which could storage temp data during low-level
// keep connected, all data will be released when the low-level connection was broken.
// Session instance related to the client will be passed to Handler method as the first
// parameter.
type Session struct {
	sync.RWMutex                        // protect data
	id           int64                  // session global unique id
	uid          int64                  // binding user id
	LastRID      uint                   // last request id
	lastTime     int64                  // last heartbeat time
	Entity       NetworkEntity          // low-level network entity
	data         map[string]interface{} // session data store
}

// New returns a new session instance
// a NetworkEntity represent low-level network instace
func New(entity NetworkEntity) *Session {
	return &Session{
		id:       service.Connections.SessionID(),
		Entity:   entity,
		data:     make(map[string]interface{}),
		lastTime: time.Now().Unix(),
	}
}

// Push message to client
func (s *Session) Push(route string, v interface{}) error {
	return s.Entity.Push(route, v)
}

// Response message to client
func (s *Session) Response(v interface{}) error {
	return s.Entity.Response(v)
}

// ID returns the session id
func (s *Session) ID() int64 {
	return s.id
}

// Uid returns UID that bind to current session
func (s *Session) Uid() int64 {
	return atomic.LoadInt64(&s.uid)
}

// Bind bind UID to current session
func (s *Session) Bind(uid int64) error {
	if uid < 1 {
		return ErrIllegalUID
	}

	atomic.StoreInt64(&s.uid, uid)
	return nil
}

// Close terminate current session, session related data will not be released,
// all related data should be Clear explicitly in Session closed callback
func (s *Session) Close() {
	s.Entity.Close()
}

// Remove delete data associated with the key from session storage
func (s *Session) Remove(key string) {
	s.Lock()
	defer s.Unlock()

	delete(s.data, key)
}

// Set associates value with the key in session storage
func (s *Session) Set(key string, value interface{}) {
	s.Lock()
	defer s.Unlock()

	s.data[key] = value
}

// HasKey decides whether a key has associated value
func (s *Session) HasKey(key string) bool {
	s.RLock()
	defer s.RUnlock()

	_, has := s.data[key]
	return has
}

// Int returns the value associated with the key as a int.
func (s *Session) Int(key string) int {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int)
	if !ok {
		return 0
	}
	return value
}

// Int8 returns the value associated with the key as a int8.
func (s *Session) Int8(key string) int8 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int8)
	if !ok {
		return 0
	}
	return value
}

// Int16 returns the value associated with the key as a int16.
func (s *Session) Int16(key string) int16 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int16)
	if !ok {
		return 0
	}
	return value
}

// Int32 returns the value associated with the key as a int32.
func (s *Session) Int32(key string) int32 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int32)
	if !ok {
		return 0
	}
	return value
}

// Int64 returns the value associated with the key as a int64.
func (s *Session) Int64(key string) int64 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int64)
	if !ok {
		return 0
	}
	return value
}

// Uint returns the value associated with the key as a uint.
func (s *Session) Uint(key string) uint {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint)
	if !ok {
		return 0
	}
	return value
}

// Uint8 returns the value associated with the key as a uint8.
func (s *Session) Uint8(key string) uint8 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint8)
	if !ok {
		return 0
	}
	return value
}

// Uint16 returns the value associated with the key as a uint16.
func (s *Session) Uint16(key string) uint16 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint16)
	if !ok {
		return 0
	}
	return value
}

// Uint32 returns the value associated with the key as a uint32.
func (s *Session) Uint32(key string) uint32 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint32)
	if !ok {
		return 0
	}
	return value
}

// Uint64 returns the value associated with the key as a uint64.
func (s *Session) Uint64(key string) uint64 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint64)
	if !ok {
		return 0
	}
	return value
}

// Float32 returns the value associated with the key as a float32.
func (s *Session) Float32(key string) float32 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(float32)
	if !ok {
		return 0
	}
	return value
}

// Float64 returns the value associated with the key as a float64.
func (s *Session) Float64(key string) float64 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(float64)
	if !ok {
		return 0
	}
	return value
}

// String returns the value associated with the key as a string.
func (s *Session) String(key string) string {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return ""
	}

	value, ok := v.(string)
	if !ok {
		return ""
	}
	return value
}

// String returns the value associated with the key as a interface{}.
func (s *Session) Value(key string) interface{} {
	s.RLock()
	defer s.RUnlock()

	return s.data[key]
}

// State returns all session state
func (s *Session) State() map[string]interface{} {
	s.RLock()
	defer s.RUnlock()

	return s.data
}

// Restore session state after reconnect
func (s *Session) Restore(data map[string]interface{}) {
	s.data = data
}

// Clear releases all data related to current session
func (s *Session) Clear() {
	s.Lock()
	defer s.Unlock()

	s.data = map[string]interface{}{}
}
<commit_msg>feature: add RemoteAddr method on Session<commit_after>// Copyright (c) nano Author. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package session

import (
	"errors"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/lonnng/nano/service"
)

type NetworkEntity interface {
	Push(route string, v interface{}) error
	Response(v interface{}) error
	Close() error
	RemoteAddr() net.Addr
}

var (
	ErrIllegalUID = errors.New("illegal uid")
)

// Session represents a client session which could storage temp data during low-level
// keep connected, all data will be released when the low-level connection was broken.
// Session instance related to the client will be passed to Handler method as the first
// parameter.
type Session struct {
	sync.RWMutex                    // protect data
	id       int64                  // session global unique id
	uid      int64                  // binding user id
	LastRID  uint                   // last request id
	lastTime int64                  // last heartbeat time
	entity   NetworkEntity          // low-level network entity
	data     map[string]interface{} // session data store
}

// New returns a new session instance
// a NetworkEntity represent low-level network instace
func New(entity NetworkEntity) *Session {
	return &Session{
		id:       service.Connections.SessionID(),
		entity:   entity,
		data:     make(map[string]interface{}),
		lastTime: time.Now().Unix(),
	}
}

// Push message to client
func (s *Session) Push(route string, v interface{}) error {
	return s.entity.Push(route, v)
}

// Response message to client
func (s *Session) Response(v interface{}) error {
	return s.entity.Response(v)
}

// ID returns the session id
func (s *Session) ID() int64 {
	return s.id
}

// Uid returns UID that bind to current session
func (s *Session) Uid() int64 {
	return atomic.LoadInt64(&s.uid)
}

// Bind bind UID to current session
func (s *Session) Bind(uid int64) error {
	if uid < 1 {
		return ErrIllegalUID
	}

	atomic.StoreInt64(&s.uid, uid)
	return nil
}

// Close terminate current session, session related data will not be released,
// all related data should be Clear explicitly in Session closed callback
func (s *Session) Close() {
	s.entity.Close()
}

// RemoteAddr returns the remote network address.
func (s *Session) RemoteAddr()net.Addr {
	return s.entity.RemoteAddr()
}

// Remove delete data associated with the key from session storage
func (s *Session) Remove(key string) {
	s.Lock()
	defer s.Unlock()

	delete(s.data, key)
}

// Set associates value with the key in session storage
func (s *Session) Set(key string, value interface{}) {
	s.Lock()
	defer s.Unlock()

	s.data[key] = value
}

// HasKey decides whether a key has associated value
func (s *Session) HasKey(key string) bool {
	s.RLock()
	defer s.RUnlock()

	_, has := s.data[key]
	return has
}

// Int returns the value associated with the key as a int.
func (s *Session) Int(key string) int {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int)
	if !ok {
		return 0
	}
	return value
}

// Int8 returns the value associated with the key as a int8.
func (s *Session) Int8(key string) int8 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int8)
	if !ok {
		return 0
	}
	return value
}

// Int16 returns the value associated with the key as a int16.
func (s *Session) Int16(key string) int16 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int16)
	if !ok {
		return 0
	}
	return value
}

// Int32 returns the value associated with the key as a int32.
func (s *Session) Int32(key string) int32 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int32)
	if !ok {
		return 0
	}
	return value
}

// Int64 returns the value associated with the key as a int64.
func (s *Session) Int64(key string) int64 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(int64)
	if !ok {
		return 0
	}
	return value
}

// Uint returns the value associated with the key as a uint.
func (s *Session) Uint(key string) uint {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint)
	if !ok {
		return 0
	}
	return value
}

// Uint8 returns the value associated with the key as a uint8.
func (s *Session) Uint8(key string) uint8 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint8)
	if !ok {
		return 0
	}
	return value
}

// Uint16 returns the value associated with the key as a uint16.
func (s *Session) Uint16(key string) uint16 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint16)
	if !ok {
		return 0
	}
	return value
}

// Uint32 returns the value associated with the key as a uint32.
func (s *Session) Uint32(key string) uint32 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint32)
	if !ok {
		return 0
	}
	return value
}

// Uint64 returns the value associated with the key as a uint64.
func (s *Session) Uint64(key string) uint64 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(uint64)
	if !ok {
		return 0
	}
	return value
}

// Float32 returns the value associated with the key as a float32.
func (s *Session) Float32(key string) float32 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(float32)
	if !ok {
		return 0
	}
	return value
}

// Float64 returns the value associated with the key as a float64.
func (s *Session) Float64(key string) float64 {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return 0
	}

	value, ok := v.(float64)
	if !ok {
		return 0
	}
	return value
}

// String returns the value associated with the key as a string.
func (s *Session) String(key string) string {
	s.RLock()
	defer s.RUnlock()

	v, ok := s.data[key]
	if !ok {
		return ""
	}

	value, ok := v.(string)
	if !ok {
		return ""
	}
	return value
}

// String returns the value associated with the key as a interface{}.
func (s *Session) Value(key string) interface{} {
	s.RLock()
	defer s.RUnlock()

	return s.data[key]
}

// State returns all session state
func (s *Session) State() map[string]interface{} {
	s.RLock()
	defer s.RUnlock()

	return s.data
}

// Restore session state after reconnect
func (s *Session) Restore(data map[string]interface{}) {
	s.data = data
}

// Clear releases all data related to current session
func (s *Session) Clear() {
	s.Lock()
	defer s.Unlock()

	s.uid = 0
	s.data = map[string]interface{}{}
}
<|endoftext|>


<commit_before>//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"math"
)

// A three component cartesian vector
type Vector []float64

// Create new zero vector
func NewZeroVector() Vector {

	return Vector(make([]float64, 3))
}

// Create new vector
func NewVector(x, y, z float64) Vector {

	v := NewZeroVector()

	v[0], v[1], v[2] = x, y, z

	return v
}

// Copy the vector
func (v Vector) Copy() (u Vector) {

	u = NewZeroVector()

	for i, vComp := range v {

		u[i] = vComp
	}

	return u
}

// Add two vectors
func (a Vector) Plus(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] = a[i] + b[i]
	}

	return c
}

// Invert a vector's direction
func (a Vector) Negate() (minusA Vector) {

	minusA = NewZeroVector()

	for i, aComponent := range a {

		minusA[i] = -aComponent
	}

	return minusA
}

// Subtract two vectors
func (a Vector) Minus(b Vector) Vector {

	return a.Plus(b.Negate())
}

// Take the dot product of two vectors
func (a Vector) Dot(b Vector) float64 {

	sum := 0.0

	for i, _ := range a {

		sum += a[i] * b[i]
	}

	return sum
}

// Calculate the norm of a vector
func (a Vector) Norm() float64 {

	return math.Sqrt(a.Dot(a))
}

// Scale a vector by s
func (a Vector) Scale(s float64) (b Vector) {

	b = a.Copy()

	for i, _ := range a {

		b[i] *= s
	}

	return b
}

// Produce the unit vector oriented the same as a
func (a Vector) Unit() Vector {

	return a.Scale(1 / a.Norm())
}

// Take the cross product of two vectors
func (a Vector) Cross(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] += a[(i+1)%3] * b[(i+2)%3]

		c[i] -= a[(i+2)%3] * b[(i+1)%3]
	}

	return c
}

// Compare two vectors for equality
func (a Vector) Equal(b Vector) bool {

	for i, _ := range a {

		if a[i] != b[i] {

			return false
		}
	}

	return true
}

// Decompose a vector into the part parallel and orthogonal to another one.
func (a Vector) ProjectionRejection(wRespectTo Vector) (prj, rej Vector) {

	prj = wRespectTo.Unit().Scale(a.Dot(wRespectTo))

	rej = a.Minus(prj)

	return prj, rej
}

// Component vector parallel to another one.
func (a Vector) Projection(onto Vector) (prj Vector) {

	prj, _ = a.ProjectionRejection(onto)

	return prj
}

// Component vector orthogonal to another one.
func (a Vector) Rejection(ofOf Vector) (rej Vector) {

	_, rej = a.ProjectionRejection(ofOf)

	return rej
}
<commit_msg>Add Vector.UnitAndNorm<commit_after>package main

import (
	"math"
)

// A three component cartesian vector
type Vector []float64

// Create new zero vector
func NewZeroVector() Vector {

	return Vector(make([]float64, 3))
}

// Create new vector
func NewVector(x, y, z float64) Vector {

	v := NewZeroVector()

	v[0], v[1], v[2] = x, y, z

	return v
}

// Copy the vector
func (v Vector) Copy() (u Vector) {

	u = NewZeroVector()

	for i, vComp := range v {

		u[i] = vComp
	}

	return u
}

// Add two vectors
func (a Vector) Plus(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] = a[i] + b[i]
	}

	return c
}

// Invert a vector's direction
func (a Vector) Negate() (minusA Vector) {

	minusA = NewZeroVector()

	for i, aComponent := range a {

		minusA[i] = -aComponent
	}

	return minusA
}

// Subtract two vectors
func (a Vector) Minus(b Vector) Vector {

	return a.Plus(b.Negate())
}

// Take the dot product of two vectors
func (a Vector) Dot(b Vector) float64 {

	sum := 0.0

	for i, _ := range a {

		sum += a[i] * b[i]
	}

	return sum
}

// Calculate the norm of a vector
func (a Vector) Norm() float64 {

	return math.Sqrt(a.Dot(a))
}

// Scale a vector by s
func (a Vector) Scale(s float64) (b Vector) {

	b = a.Copy()

	for i, _ := range a {

		b[i] *= s
	}

	return b
}

// Produce the unit vector oriented the same as a
func (a Vector) Unit() Vector {

	return a.Scale(1 / a.Norm())
}

// Calculate a unit vector and the vector norm at once
func (a Vector) UnitAndNorm() (aU Vector, norm float64) {

	return a.Unit(), a.Norm()
}

// Take the cross product of two vectors
func (a Vector) Cross(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] += a[(i+1)%3] * b[(i+2)%3]

		c[i] -= a[(i+2)%3] * b[(i+1)%3]
	}

	return c
}

// Compare two vectors for equality
func (a Vector) Equal(b Vector) bool {

	for i, _ := range a {

		if a[i] != b[i] {

			return false
		}
	}

	return true
}

// Decompose a vector into the part parallel and orthogonal to another one.
func (a Vector) ProjectionRejection(wRespectTo Vector) (prj, rej Vector) {

	prj = wRespectTo.Unit().Scale(a.Dot(wRespectTo))

	rej = a.Minus(prj)

	return prj, rej
}

// Component vector parallel to another one.
func (a Vector) Projection(onto Vector) (prj Vector) {

	prj, _ = a.ProjectionRejection(onto)

	return prj
}

// Component vector orthogonal to another one.
func (a Vector) Rejection(ofOf Vector) (rej Vector) {

	_, rej = a.ProjectionRejection(ofOf)

	return rej
}
<|endoftext|>


<commit_before>package main

import (
	"board"
	"color"
	"enemy"
	"fmt"
	"io"
	"matrix"
)

func scanMove() (*matrix.Move, error) {
	var file byte
	var rank int

	_, err := fmt.Scanf("%c%d", &file, &rank)
	return matrix.NewMove(file, rank), err
}

func main() {
	chessboard := board.NewBoard()
	finish := false
	now := color.White

	for finish == false {
		success := false

		chessboard.Print()
		if chessboard.IsCheckMate(now) {
			fmt.Println("Checkmate")
			break
		} else if chessboard.IsChecked(now) {
			fmt.Println("Your king is checked")
		}

		var from, to matrix.Point
		var err error
		for success == false {
			if now == color.Black {
				from, to = enemy.NewEnemy(chessboard, now).RandomizedSelect()
			} else {
				var move *matrix.Move
				move, err = scanMove()
				if err == io.EOF {
					finish = true
					break
				} else if err != nil {
					fmt.Println(err)
					continue
				} else {
					from = move.ToPoint()
				}
				move, err = scanMove()
				if err == io.EOF {
					finish = true
					break
				} else if err != nil {
					fmt.Println(err)
					continue
				} else {
					to = move.ToPoint()
				}
			}
			err = chessboard.Move(from, to, now)
			success = err == nil
			if success == false {
				fmt.Println(err)
			}
		}
		fmt.Println()
		now = now.Enemy()
	}
}
<commit_msg>Check movable before move<commit_after>package main

import (
	"board"
	"color"
	"enemy"
	"fmt"
	"io"
	"matrix"
)

func scanMove() (*matrix.Move, error) {
	var file byte
	var rank int

	_, err := fmt.Scanf("%c%d", &file, &rank)
	return matrix.NewMove(file, rank), err
}

func main() {
	chessboard := board.NewBoard()
	finish := false
	now := color.White

	for finish == false {
		success := false

		chessboard.Print()
		if chessboard.IsCheckMate(now) {
			fmt.Println("Checkmate")
			break
		} else if chessboard.IsChecked(now) {
			fmt.Println("Your king is checked")
		}

		var from, to matrix.Point
		var err error
		for success == false {
			if now == color.Black {
				from, to = enemy.NewEnemy(chessboard, now).RandomizedSelect()
			} else {
				var move *matrix.Move
				move, err = scanMove()
				if err == io.EOF {
					finish = true
					break
				} else if err != nil {
					fmt.Println(err)
					continue
				} else {
					from = move.ToPoint()
				}
				move, err = scanMove()
				if err == io.EOF {
					finish = true
					break
				} else if err != nil {
					fmt.Println(err)
					continue
				} else {
					to = move.ToPoint()
				}
			}
			canMove, err := chessboard.CanMove(from, to, now)
			success = canMove
			if canMove {
				success = true
				chessboard.Move(from, to, chessboard.IsCastling(from, to))
			} else {
				fmt.Println(err)
			}
		}
		fmt.Println()
		now = now.Enemy()
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"

	"github.com/hashicorp/go-version"
)

// RequiredVersion is the minimum Git version required
const RequiredVersion = "2.0.0"

var (
	// GitExecutable is the command name of git
	// Could be updated to an absolute path while initialization
	GitExecutable = "git"

	// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
	DefaultContext context.Context

	// SupportProcReceive version >= 2.29.0
	SupportProcReceive bool

	gitVersion *version.Version
)

// loadGitVersion returns current Git version from shell. Internal usage only.
func loadGitVersion() (*version.Version, error) {
	// doesn't need RWMutex because it's executed by Init()
	if gitVersion != nil {
		return gitVersion, nil
	}

	stdout, _, runErr := NewCommand(DefaultContext, "version").RunStdString(nil)
	if runErr != nil {
		return nil, runErr
	}

	fields := strings.Fields(stdout)
	if len(fields) < 3 {
		return nil, fmt.Errorf("invalid git version output: %s", stdout)
	}

	var versionString string

	// Handle special case on Windows.
	i := strings.Index(fields[2], "windows")
	if i >= 1 {
		versionString = fields[2][:i-1]
	} else {
		versionString = fields[2]
	}

	var err error
	gitVersion, err = version.NewVersion(versionString)
	return gitVersion, err
}

// SetExecutablePath changes the path of git executable and checks the file permission and version.
func SetExecutablePath(path string) error {
	// If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
	if path != "" {
		GitExecutable = path
	}
	absPath, err := exec.LookPath(GitExecutable)
	if err != nil {
		return fmt.Errorf("git not found: %w", err)
	}
	GitExecutable = absPath

	_, err = loadGitVersion()
	if err != nil {
		return fmt.Errorf("unable to load git version: %w", err)
	}

	versionRequired, err := version.NewVersion(RequiredVersion)
	if err != nil {
		return err
	}

	if gitVersion.LessThan(versionRequired) {
		moreHint := "get git: https://git-scm.com/download/"
		if runtime.GOOS == "linux" {
			// there are a lot of CentOS/RHEL users using old git, so we add a special hint for them
			if _, err = os.Stat("/etc/redhat-release"); err == nil {
				// ius.io is the recommended official(git-scm.com) method to install git
				moreHint = "get git: https://git-scm.com/download/linux and https://ius.io"
			}
		}
		return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", gitVersion.Original(), RequiredVersion, moreHint)
	}

	return nil
}

// VersionInfo returns git version information
func VersionInfo() string {
	if gitVersion == nil {
		return "(git not found)"
	}
	format := "%s"
	args := []interface{}{gitVersion.Original()}
	// Since git wire protocol has been released from git v2.18
	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
		format += ", Wire Protocol %s Enabled"
		args = append(args, "Version 2") // for focus color
	}

	return fmt.Sprintf(format, args...)
}

func checkInit() error {
	if setting.Git.HomePath == "" {
		return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
	}
	if DefaultContext != nil {
		log.Warn("git module has been initialized already, duplicate init may work but it's better to fix it")
	}
	return nil
}

// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
	if setting.Git.HomePath == "" {
		// strict check, make sure the git module is initialized correctly.
		// attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers.
		// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
		log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
		return ""
	}
	return setting.Git.HomePath
}

// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
func InitSimple(ctx context.Context) error {
	if err := checkInit(); err != nil {
		return err
	}

	DefaultContext = ctx
	globalCommandArgs = nil

	if setting.Git.Timeout.Default > 0 {
		defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
	}

	return SetExecutablePath(setting.Git.Path)
}

// InitFull initializes git module with version check and change global variables, sync gitconfig.
// It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
func InitFull(ctx context.Context) (err error) {
	if err = checkInit(); err != nil {
		return err
	}

	if err = InitSimple(ctx); err != nil {
		return
	}

	// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
	if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
		_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
	}

	// Since git wire protocol has been released from git v2.18
	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
		globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
	}

	// By default partial clones are disabled, enable them from git v2.22
	if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
		globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
	}

	// Explicitly disable credential helper, otherwise Git credentials might leak
	if CheckGitVersionAtLeast("2.9") == nil {
		globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
	}

	SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil

	if setting.LFS.StartServer {
		if CheckGitVersionAtLeast("2.1.2") != nil {
			return errors.New("LFS server support requires Git >= 2.1.2")
		}
		globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
	}

	return syncGitConfig()
}

// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig() (err error) {
	if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
		return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
	}

	// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
	// TODO: need to confirm whether users really need to change these values manually. It seems that these values are dummy only and not really used.
	// If these values are not really used, then they can be set (overwritten) directly without considering about existence.
	for configKey, defaultValue := range map[string]string{
		"user.name":  "Gitea",
		"user.email": "gitea@fake.local",
	} {
		if err := configSetNonExist(configKey, defaultValue); err != nil {
			return err
		}
	}

	// Set git some configurations - these must be set to these values for gitea to work correctly
	if err := configSet("core.quotePath", "false"); err != nil {
		return err
	}

	if CheckGitVersionAtLeast("2.10") == nil {
		if err := configSet("receive.advertisePushOptions", "true"); err != nil {
			return err
		}
	}

	if CheckGitVersionAtLeast("2.18") == nil {
		if err := configSet("core.commitGraph", "true"); err != nil {
			return err
		}
		if err := configSet("gc.writeCommitGraph", "true"); err != nil {
			return err
		}
		if err := configSet("fetch.writeCommitGraph", "true"); err != nil {
			return err
		}
	}

	if SupportProcReceive {
		// set support for AGit flow
		if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
			return err
		}
	} else {
		if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
			return err
		}
	}

	// Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user
	// however, some docker users and samba users find it difficult to configure their systems so that Gitea's git repositories are owned by the Gitea user. (Possibly Windows Service users - but ownership in this case should really be set correctly on the filesystem.)
	// see issue: https://github.com/go-gitea/gitea/issues/19455
	// Fundamentally the problem lies with the uid-gid-mapping mechanism for filesystems in docker on windows (and to a lesser extent samba).
	// Docker's configuration mechanism for local filesystems provides no way of setting this mapping and although there is a mechanism for setting this uid through using cifs mounting it is complicated and essentially undocumented
	// Thus the owner uid/gid for files on these filesystems will be marked as root.
	// As Gitea now always use its internal git config file, and access to the git repositories is managed through Gitea,
	// it is now safe to set "safe.directory=*" for internal usage only.
	// Please note: the wildcard "*" is only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later
	// Although only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later - this setting is tolerated by earlier versions
	if err := configAddNonExist("safe.directory", "*"); err != nil {
		return err
	}
	if runtime.GOOS == "windows" {
		if err := configSet("core.longpaths", "true"); err != nil {
			return err
		}
		if setting.Git.DisableCoreProtectNTFS {
			err = configSet("core.protectNTFS", "false")
		} else {
			err = configUnsetAll("core.protectNTFS", "false")
		}
		if err != nil {
			return err
		}
	}

	return nil
}

// CheckGitVersionAtLeast check git version is at least the constraint version
func CheckGitVersionAtLeast(atLeast string) error {
	if _, err := loadGitVersion(); err != nil {
		return err
	}
	atLeastVersion, err := version.NewVersion(atLeast)
	if err != nil {
		return err
	}
	if gitVersion.Compare(atLeastVersion) < 0 {
		return fmt.Errorf("installed git binary version %s is not at least %s", gitVersion.Original(), atLeast)
	}
	return nil
}

func configSet(key, value string) error {
	stdout, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
	if err != nil && !err.IsExitCode(1) {
		return fmt.Errorf("failed to get git config %s, err: %w", key, err)
	}

	currValue := strings.TrimSpace(stdout)
	if currValue == value {
		return nil
	}

	_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
	if err != nil {
		return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
	}

	return nil
}

func configSetNonExist(key, value string) error {
	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
	if err == nil {
		// already exist
		return nil
	}
	if err.IsExitCode(1) {
		// not exist, set new config
		_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
		if err != nil {
			return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
		}
		return nil
	}

	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}

func configAddNonExist(key, value string) error {
	_, _, err := NewCommand(DefaultContext, "config", "--get", key, regexp.QuoteMeta(value)).RunStdString(nil)
	if err == nil {
		// already exist
		return nil
	}
	if err.IsExitCode(1) {
		// not exist, add new config
		_, _, err = NewCommand(DefaultContext, "config", "--global", "--add", key, value).RunStdString(nil)
		if err != nil {
			return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
		}
		return nil
	}
	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}

func configUnsetAll(key, value string) error {
	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
	if err == nil {
		// exist, need to remove
		_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all", key, regexp.QuoteMeta(value)).RunStdString(nil)
		if err != nil {
			return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
		}
		return nil
	}
	if err.IsExitCode(1) {
		// not exist
		return nil
	}
	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}

// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error {
	return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath})
}
<commit_msg>Set uploadpack.allowFilter etc on gitea serv to enable partial clones with ssh (#20902)<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"

	"github.com/hashicorp/go-version"
)

// RequiredVersion is the minimum Git version required
const RequiredVersion = "2.0.0"

var (
	// GitExecutable is the command name of git
	// Could be updated to an absolute path while initialization
	GitExecutable = "git"

	// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
	DefaultContext context.Context

	// SupportProcReceive version >= 2.29.0
	SupportProcReceive bool

	gitVersion *version.Version
)

// loadGitVersion returns current Git version from shell. Internal usage only.
func loadGitVersion() (*version.Version, error) {
	// doesn't need RWMutex because it's executed by Init()
	if gitVersion != nil {
		return gitVersion, nil
	}

	stdout, _, runErr := NewCommand(DefaultContext, "version").RunStdString(nil)
	if runErr != nil {
		return nil, runErr
	}

	fields := strings.Fields(stdout)
	if len(fields) < 3 {
		return nil, fmt.Errorf("invalid git version output: %s", stdout)
	}

	var versionString string

	// Handle special case on Windows.
	i := strings.Index(fields[2], "windows")
	if i >= 1 {
		versionString = fields[2][:i-1]
	} else {
		versionString = fields[2]
	}

	var err error
	gitVersion, err = version.NewVersion(versionString)
	return gitVersion, err
}

// SetExecutablePath changes the path of git executable and checks the file permission and version.
func SetExecutablePath(path string) error {
	// If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
	if path != "" {
		GitExecutable = path
	}
	absPath, err := exec.LookPath(GitExecutable)
	if err != nil {
		return fmt.Errorf("git not found: %w", err)
	}
	GitExecutable = absPath

	_, err = loadGitVersion()
	if err != nil {
		return fmt.Errorf("unable to load git version: %w", err)
	}

	versionRequired, err := version.NewVersion(RequiredVersion)
	if err != nil {
		return err
	}

	if gitVersion.LessThan(versionRequired) {
		moreHint := "get git: https://git-scm.com/download/"
		if runtime.GOOS == "linux" {
			// there are a lot of CentOS/RHEL users using old git, so we add a special hint for them
			if _, err = os.Stat("/etc/redhat-release"); err == nil {
				// ius.io is the recommended official(git-scm.com) method to install git
				moreHint = "get git: https://git-scm.com/download/linux and https://ius.io"
			}
		}
		return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", gitVersion.Original(), RequiredVersion, moreHint)
	}

	return nil
}

// VersionInfo returns git version information
func VersionInfo() string {
	if gitVersion == nil {
		return "(git not found)"
	}
	format := "%s"
	args := []interface{}{gitVersion.Original()}
	// Since git wire protocol has been released from git v2.18
	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
		format += ", Wire Protocol %s Enabled"
		args = append(args, "Version 2") // for focus color
	}

	return fmt.Sprintf(format, args...)
}

func checkInit() error {
	if setting.Git.HomePath == "" {
		return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
	}
	if DefaultContext != nil {
		log.Warn("git module has been initialized already, duplicate init may work but it's better to fix it")
	}
	return nil
}

// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
	if setting.Git.HomePath == "" {
		// strict check, make sure the git module is initialized correctly.
		// attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers.
		// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
		log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
		return ""
	}
	return setting.Git.HomePath
}

// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
func InitSimple(ctx context.Context) error {
	if err := checkInit(); err != nil {
		return err
	}

	DefaultContext = ctx
	globalCommandArgs = nil

	if setting.Git.Timeout.Default > 0 {
		defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
	}

	return SetExecutablePath(setting.Git.Path)
}

// InitFull initializes git module with version check and change global variables, sync gitconfig.
// It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
func InitFull(ctx context.Context) (err error) {
	if err = checkInit(); err != nil {
		return err
	}

	if err = InitSimple(ctx); err != nil {
		return
	}

	// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
	if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
		_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
	}

	// Since git wire protocol has been released from git v2.18
	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
		globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
	}

	// Explicitly disable credential helper, otherwise Git credentials might leak
	if CheckGitVersionAtLeast("2.9") == nil {
		globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
	}

	SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil

	if setting.LFS.StartServer {
		if CheckGitVersionAtLeast("2.1.2") != nil {
			return errors.New("LFS server support requires Git >= 2.1.2")
		}
		globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
	}

	return syncGitConfig()
}

// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig() (err error) {
	if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
		return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
	}

	// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
	// TODO: need to confirm whether users really need to change these values manually. It seems that these values are dummy only and not really used.
	// If these values are not really used, then they can be set (overwritten) directly without considering about existence.
	for configKey, defaultValue := range map[string]string{
		"user.name":  "Gitea",
		"user.email": "gitea@fake.local",
	} {
		if err := configSetNonExist(configKey, defaultValue); err != nil {
			return err
		}
	}

	// Set git some configurations - these must be set to these values for gitea to work correctly
	if err := configSet("core.quotePath", "false"); err != nil {
		return err
	}

	if CheckGitVersionAtLeast("2.10") == nil {
		if err := configSet("receive.advertisePushOptions", "true"); err != nil {
			return err
		}
	}

	if CheckGitVersionAtLeast("2.18") == nil {
		if err := configSet("core.commitGraph", "true"); err != nil {
			return err
		}
		if err := configSet("gc.writeCommitGraph", "true"); err != nil {
			return err
		}
		if err := configSet("fetch.writeCommitGraph", "true"); err != nil {
			return err
		}
	}

	if SupportProcReceive {
		// set support for AGit flow
		if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
			return err
		}
	} else {
		if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
			return err
		}
	}

	// Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user
	// however, some docker users and samba users find it difficult to configure their systems so that Gitea's git repositories are owned by the Gitea user. (Possibly Windows Service users - but ownership in this case should really be set correctly on the filesystem.)
	// see issue: https://github.com/go-gitea/gitea/issues/19455
	// Fundamentally the problem lies with the uid-gid-mapping mechanism for filesystems in docker on windows (and to a lesser extent samba).
	// Docker's configuration mechanism for local filesystems provides no way of setting this mapping and although there is a mechanism for setting this uid through using cifs mounting it is complicated and essentially undocumented
	// Thus the owner uid/gid for files on these filesystems will be marked as root.
	// As Gitea now always use its internal git config file, and access to the git repositories is managed through Gitea,
	// it is now safe to set "safe.directory=*" for internal usage only.
	// Please note: the wildcard "*" is only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later
	// Although only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later - this setting is tolerated by earlier versions
	if err := configAddNonExist("safe.directory", "*"); err != nil {
		return err
	}
	if runtime.GOOS == "windows" {
		if err := configSet("core.longpaths", "true"); err != nil {
			return err
		}
		if setting.Git.DisableCoreProtectNTFS {
			err = configSet("core.protectNTFS", "false")
		} else {
			err = configUnsetAll("core.protectNTFS", "false")
		}
		if err != nil {
			return err
		}
	}

	// By default partial clones are disabled, enable them from git v2.22
	if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
		if err = configSet("uploadpack.allowfilter", "true"); err != nil {
			return err
		}
		err = configSet("uploadpack.allowAnySHA1InWant", "true")
	} else {
		if err = configUnsetAll("uploadpack.allowfilter", "true"); err != nil {
			return err
		}
		err = configUnsetAll("uploadpack.allowAnySHA1InWant", "true")
	}

	return err
}

// CheckGitVersionAtLeast check git version is at least the constraint version
func CheckGitVersionAtLeast(atLeast string) error {
	if _, err := loadGitVersion(); err != nil {
		return err
	}
	atLeastVersion, err := version.NewVersion(atLeast)
	if err != nil {
		return err
	}
	if gitVersion.Compare(atLeastVersion) < 0 {
		return fmt.Errorf("installed git binary version %s is not at least %s", gitVersion.Original(), atLeast)
	}
	return nil
}

func configSet(key, value string) error {
	stdout, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
	if err != nil && !err.IsExitCode(1) {
		return fmt.Errorf("failed to get git config %s, err: %w", key, err)
	}

	currValue := strings.TrimSpace(stdout)
	if currValue == value {
		return nil
	}

	_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
	if err != nil {
		return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
	}

	return nil
}

func configSetNonExist(key, value string) error {
	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
	if err == nil {
		// already exist
		return nil
	}
	if err.IsExitCode(1) {
		// not exist, set new config
		_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
		if err != nil {
			return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
		}
		return nil
	}

	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}

func configAddNonExist(key, value string) error {
	_, _, err := NewCommand(DefaultContext, "config", "--get", key, regexp.QuoteMeta(value)).RunStdString(nil)
	if err == nil {
		// already exist
		return nil
	}
	if err.IsExitCode(1) {
		// not exist, add new config
		_, _, err = NewCommand(DefaultContext, "config", "--global", "--add", key, value).RunStdString(nil)
		if err != nil {
			return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
		}
		return nil
	}
	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}

func configUnsetAll(key, value string) error {
	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
	if err == nil {
		// exist, need to remove
		_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all", key, regexp.QuoteMeta(value)).RunStdString(nil)
		if err != nil {
			return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
		}
		return nil
	}
	if err.IsExitCode(1) {
		// not exist
		return nil
	}
	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}

// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error {
	return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath})
}
<|endoftext|>