<commit_before>package trousseau

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/oleiade/trousseau/crypto"
	"github.com/oleiade/trousseau/dsn"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"time"
)

func CreateAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'configure' command")
	}

	recipients := strings.Split(c.Args()[0], ",")

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
		Recipients: recipients,
	}

	meta := Meta{
		CreatedAt:        time.Now().String(),
		LastModifiedAt:   time.Now().String(),
		Recipients:       recipients,
		TrousseauVersion: TROUSSEAU_VERSION,
	}

	// Create and write empty store file
	err := CreateStoreFile(gStorePath, opts, &meta)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Trousseau data store succesfully created")
}

func PushAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'push' command")
	}

	endpointDsn, err := dsn.Parse(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	switch endpointDsn.Scheme {
	case "s3":
		err := endpointDsn.SetDefaults(gS3Defaults)
		if err != nil {
			log.Fatal(err)
		}

		err = uploadUsingS3(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pushed to s3")
	case "scp":
		privateKey := c.String("ssh-private-key")

		err := endpointDsn.SetDefaults(gScpDefaults)
		if err != nil {
			log.Fatal(err)
		}

		if c.Bool("ask-password") == true {
			password := PromptForPassword()
			endpointDsn.Secret = password
		}

		err = uploadUsingScp(endpointDsn, privateKey)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pushed to ssh remote storage")
	case "gist":
		err = uploadUsingGist(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pushed to gist")
	}
}

func PullAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'pull' command")
	}

	endpointDsn, err := dsn.Parse(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	switch endpointDsn.Scheme {
	case "s3":
		err := endpointDsn.SetDefaults(gS3Defaults)
		if err != nil {
			log.Fatal(err)
		}

		err = DownloadUsingS3(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pulled from S3")
	case "scp":
		privateKey := c.String("ssh-private-key")

		err := endpointDsn.SetDefaults(gScpDefaults)
		if err != nil {
			log.Fatal(err)
		}

		if c.Bool("ask-password") == true {
			password := PromptForPassword()
			endpointDsn.Secret = password
		}

		err = DownloadUsingScp(endpointDsn, privateKey)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pulled from ssh remote storage")
	case "gist":
		err = DownloadUsingGist(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pulled from gist")
	default:
		if endpointDsn.Scheme == "" {
			log.Fatalf("No dsn scheme supplied")
		} else {
			log.Fatalf("Invalid dsn scheme supplied: %s", endpointDsn.Scheme)
		}
	}

	fmt.Printf("Trousseau data store succesfully pulled from remote storage")
}

func ExportAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'export' command")
	}

	var err error
	var inputFilePath string = gStorePath
	var outputFilePath string = c.Args()[0]

	inputFile, err := os.Open(inputFilePath)
	defer inputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	outputFile, err := os.Create(outputFilePath)
	defer outputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	_, err = io.Copy(outputFile, inputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Trousseau data store exported to: %s\n", outputFilePath)
}

func ImportAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'import' command")
	}

	var err error
	var importedFilePath string = c.Args()[0]
	var localFilePath string = gStorePath
	var strategy *ImportStrategy = new(ImportStrategy)

	// Transform provided merging startegy flags
	// into a proper ImportStrategy byte.
	err = strategy.FromCliContext(c)
	if err != nil {
		log.Fatal(err)
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	localStore, err := LoadStore(localFilePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	importedStore, err := LoadStore(importedFilePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = ImportStore(importedStore, localStore, *strategy)
	if err != nil {
		log.Fatal(err)
	}

	err = localStore.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Trousseau data store imported: %s\n", importedFilePath)
}

func AddRecipientAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'add-recipient' command")
	}

	recipient := c.Args()[0]

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.DataStore.Meta.AddRecipient(recipient)

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Recipient added to trousseau data store: %s\n", recipient)
}

func RemoveRecipientAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'remove-recipient' command")
	}

	recipient := c.Args()[0]

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Meta.RemoveRecipient(recipient)

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Recipient removed from trousseau data store: %s\n", recipient)
}

func GetAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'get' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	value, err := store.Get(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s\n", value)
}

func SetAction(c *cli.Context) {
	var key string
	var value interface{}
	var err error

	// If the --file flag is provided
	if c.String("file") != "" && hasExpectedArgs(c.Args(), 1) {
		// And the file actually exists on file system
		if pathExists(c.String("file")) {
			// Then load it's content
			value, err = ioutil.ReadFile(c.String("file"))
			if err != nil {
				log.Fatal(err)
			}
		} else {
			log.Fatalf("Cannot open %s because it doesn't exist", c.String("file"))
		}
	} else if c.String("file") == "" && hasExpectedArgs(c.Args(), 2) {
		value = c.Args()[1]
	} else {
		log.Fatal("Incorrect number of arguments to 'set' command")
	}

	key = c.Args()[0]

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Set(key, value)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s:%s\n", key, value)
}

func DelAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'del' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Del(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("deleted: %s\n", c.Args()[0])
}

func KeysAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'keys' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	keys, err := store.Keys()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, k := range keys {
			fmt.Println(k)
		}
	}
}

func ShowAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'show' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Items()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, pair := range pairs {
			fmt.Printf("%s : %s\n", pair.Key, pair.Value)
		}
	}
}

func MetaAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'meta' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Metadata()
	if err != nil {
		log.Fatal(err)
	}

	for _, pair := range pairs {
		fmt.Printf("%s : %s\n", pair.Key, pair.Value)
	}
}

// hasExpectedArgs checks whether the number of args are as expected.
func hasExpectedArgs(args []string, expected int) bool {
	switch expected {
	case -1:
		if len(args) > 0 {
			return true
		} else {
			return false
		}
	default:
		if len(args) == expected {
			return true
		} else {
			return false
		}
	}
}
<commit_msg>Fix set action from file value storage<commit_after>package trousseau

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/oleiade/trousseau/crypto"
	"github.com/oleiade/trousseau/dsn"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"time"
)

func CreateAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'configure' command")
	}

	recipients := strings.Split(c.Args()[0], ",")

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
		Recipients: recipients,
	}

	meta := Meta{
		CreatedAt:        time.Now().String(),
		LastModifiedAt:   time.Now().String(),
		Recipients:       recipients,
		TrousseauVersion: TROUSSEAU_VERSION,
	}

	// Create and write empty store file
	err := CreateStoreFile(gStorePath, opts, &meta)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Trousseau data store succesfully created")
}

func PushAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'push' command")
	}

	endpointDsn, err := dsn.Parse(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	switch endpointDsn.Scheme {
	case "s3":
		err := endpointDsn.SetDefaults(gS3Defaults)
		if err != nil {
			log.Fatal(err)
		}

		err = uploadUsingS3(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pushed to s3")
	case "scp":
		privateKey := c.String("ssh-private-key")

		err := endpointDsn.SetDefaults(gScpDefaults)
		if err != nil {
			log.Fatal(err)
		}

		if c.Bool("ask-password") == true {
			password := PromptForPassword()
			endpointDsn.Secret = password
		}

		err = uploadUsingScp(endpointDsn, privateKey)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pushed to ssh remote storage")
	case "gist":
		err = uploadUsingGist(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pushed to gist")
	}
}

func PullAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'pull' command")
	}

	endpointDsn, err := dsn.Parse(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	switch endpointDsn.Scheme {
	case "s3":
		err := endpointDsn.SetDefaults(gS3Defaults)
		if err != nil {
			log.Fatal(err)
		}

		err = DownloadUsingS3(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pulled from S3")
	case "scp":
		privateKey := c.String("ssh-private-key")

		err := endpointDsn.SetDefaults(gScpDefaults)
		if err != nil {
			log.Fatal(err)
		}

		if c.Bool("ask-password") == true {
			password := PromptForPassword()
			endpointDsn.Secret = password
		}

		err = DownloadUsingScp(endpointDsn, privateKey)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pulled from ssh remote storage")
	case "gist":
		err = DownloadUsingGist(endpointDsn)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Trousseau data store succesfully pulled from gist")
	default:
		if endpointDsn.Scheme == "" {
			log.Fatalf("No dsn scheme supplied")
		} else {
			log.Fatalf("Invalid dsn scheme supplied: %s", endpointDsn.Scheme)
		}
	}

	fmt.Printf("Trousseau data store succesfully pulled from remote storage")
}

func ExportAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'export' command")
	}

	var err error
	var inputFilePath string = gStorePath
	var outputFilePath string = c.Args()[0]

	inputFile, err := os.Open(inputFilePath)
	defer inputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	outputFile, err := os.Create(outputFilePath)
	defer outputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	_, err = io.Copy(outputFile, inputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Trousseau data store exported to: %s\n", outputFilePath)
}

func ImportAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'import' command")
	}

	var err error
	var importedFilePath string = c.Args()[0]
	var localFilePath string = gStorePath
	var strategy *ImportStrategy = new(ImportStrategy)

	// Transform provided merging startegy flags
	// into a proper ImportStrategy byte.
	err = strategy.FromCliContext(c)
	if err != nil {
		log.Fatal(err)
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	localStore, err := LoadStore(localFilePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	importedStore, err := LoadStore(importedFilePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = ImportStore(importedStore, localStore, *strategy)
	if err != nil {
		log.Fatal(err)
	}

	err = localStore.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Trousseau data store imported: %s\n", importedFilePath)
}

func AddRecipientAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'add-recipient' command")
	}

	recipient := c.Args()[0]

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.DataStore.Meta.AddRecipient(recipient)

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Recipient added to trousseau data store: %s\n", recipient)
}

func RemoveRecipientAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'remove-recipient' command")
	}

	recipient := c.Args()[0]

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Meta.RemoveRecipient(recipient)

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Recipient removed from trousseau data store: %s\n", recipient)
}

func GetAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'get' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	value, err := store.Get(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s", value)
}

func SetAction(c *cli.Context) {
	var key string
	var value string
	var err error

	// If the --file flag is provided
	if c.String("file") != "" && hasExpectedArgs(c.Args(), 1) {
		// And the file actually exists on file system
		if pathExists(c.String("file")) {
			// Then load it's content
			fileContent, err := ioutil.ReadFile(c.String("file"))
			if err != nil {
				log.Fatal(err)
			}

			value = string(fileContent)
		} else {
			log.Fatalf("Cannot open %s because it doesn't exist", c.String("file"))
		}
	} else if c.String("file") == "" && hasExpectedArgs(c.Args(), 2) {
		value = c.Args()[1]
	} else {
		log.Fatal("Incorrect number of arguments to 'set' command")
	}

	key = c.Args()[0]

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Set(key, value)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s:%s", key, value)
}

func DelAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'del' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Del(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	err = store.Sync()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("deleted: %s", c.Args()[0])
}

func KeysAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'keys' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	keys, err := store.Keys()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, k := range keys {
			fmt.Println(k)
		}
	}
}

func ShowAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'show' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Items()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, pair := range pairs {
			fmt.Printf("%s : %s\n", pair.Key, pair.Value)
		}
	}
}

func MetaAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'meta' command")
	}

	opts := &crypto.Options{
		Algorithm:  crypto.GPG_ENCRYPTION,
		Passphrase: gPasshphrase,
	}

	store, err := LoadStore(gStorePath, opts)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Metadata()
	if err != nil {
		log.Fatal(err)
	}

	for _, pair := range pairs {
		fmt.Printf("%s : %s\n", pair.Key, pair.Value)
	}
}

// hasExpectedArgs checks whether the number of args are as expected.
func hasExpectedArgs(args []string, expected int) bool {
	switch expected {
	case -1:
		if len(args) > 0 {
			return true
		} else {
			return false
		}
	default:
		if len(args) == expected {
			return true
		} else {
			return false
		}
	}
}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/hashicorp/hcl"
	"github.com/hashicorp/nomad/helper"
	"github.com/hashicorp/nomad/nomad/structs/config"
)

func ParseConfigFile(path string) (*Config, error) {
	// slurp
	var buf bytes.Buffer
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	if _, err := io.Copy(&buf, f); err != nil {
		return nil, err
	}

	// parse
	c := &Config{
		Client:    &ClientConfig{ServerJoin: &ServerJoin{}},
		ACL:       &ACLConfig{},
		Audit:     &config.AuditConfig{},
		Server:    &ServerConfig{ServerJoin: &ServerJoin{}},
		Consul:    &config.ConsulConfig{},
		Autopilot: &config.AutopilotConfig{},
		Telemetry: &Telemetry{},
		Vault:     &config.VaultConfig{},
	}

	err = hcl.Decode(c, buf.String())
	if err != nil {
		return nil, err
	}

	// convert strings to time.Durations
	tds := []td{
		{"gc_interval", &c.Client.GCInterval, &c.Client.GCIntervalHCL},
		{"acl.token_ttl", &c.ACL.TokenTTL, &c.ACL.TokenTTLHCL},
		{"acl.policy_ttl", &c.ACL.PolicyTTL, &c.ACL.PolicyTTLHCL},
		{"client.server_join.retry_interval", &c.Client.ServerJoin.RetryInterval, &c.Client.ServerJoin.RetryIntervalHCL},
		{"server.heartbeat_grace", &c.Server.HeartbeatGrace, &c.Server.HeartbeatGraceHCL},
		{"server.min_heartbeat_ttl", &c.Server.MinHeartbeatTTL, &c.Server.MinHeartbeatTTLHCL},
		{"server.retry_interval", &c.Server.RetryInterval, &c.Server.RetryIntervalHCL},
		{"server.server_join.retry_interval", &c.Server.ServerJoin.RetryInterval, &c.Server.ServerJoin.RetryIntervalHCL},
		{"consul.timeout", &c.Consul.Timeout, &c.Consul.TimeoutHCL},
		{"autopilot.server_stabilization_time", &c.Autopilot.ServerStabilizationTime, &c.Autopilot.ServerStabilizationTimeHCL},
		{"autopilot.last_contact_threshold", &c.Autopilot.LastContactThreshold, &c.Autopilot.LastContactThresholdHCL},
		{"telemetry.collection_interval", &c.Telemetry.collectionInterval, &c.Telemetry.CollectionInterval},
	}

	// Add enterprise audit sinks for time.Duration parsing
	for i, sink := range c.Audit.Sinks {
		tds = append(tds, td{
			fmt.Sprintf("audit.sink.%d", i), &sink.RotateDuration, &sink.RotateDurationHCL,
		})
	}

	// convert strings to time.Durations
	err = durations(tds)
	if err != nil {
		return nil, err
	}

	// report unexpected keys
	err = extraKeys(c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

// td holds args for one duration conversion
type td struct {
	path string
	td   *time.Duration
	str  *string
}

// durations parses the duration strings specified in the config files
// into time.Durations
func durations(xs []td) error {
	for _, x := range xs {
		if x.td != nil && x.str != nil && "" != *x.str {
			d, err := time.ParseDuration(*x.str)
			if err != nil {
				return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
			}

			*x.td = d
		}
	}

	return nil
}

func extraKeys(c *Config) error {
	// hcl leaves behind extra keys when parsing JSON. These keys
	// are kept on the top level, taken from slices or the keys of
	// structs contained in slices. Clean up before looking for
	// extra keys.
	for range c.HTTPAPIResponseHeaders {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "http_api_response_headers")
	}

	for _, p := range c.Plugins {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, p.Name)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "config")
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "plugin")
	}

	for _, k := range []string{"options", "meta", "chroot_env", "servers", "server_join"} {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, k)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "client")
	}

	// stats is an unused key, continue to silently ignore it
	helper.RemoveEqualFold(&c.Client.ExtraKeysHCL, "stats")

	// Remove HostVolume extra keys
	for _, hv := range c.Client.HostVolumes {
		helper.RemoveEqualFold(&c.Client.ExtraKeysHCL, hv.Name)
		helper.RemoveEqualFold(&c.Client.ExtraKeysHCL, "host_volume")
	}

	// Remove AuditConfig extra keys
	for _, f := range c.Audit.Filters {
		removeEqualFold(&c.Audit.ExtraKeysHCL, f.Name)
		removeEqualFold(&c.Audit.ExtraKeysHCL, "filter")
	}

	for _, s := range c.Audit.Sinks {
		removeEqualFold(&c.Audit.ExtraKeysHCL, s.Name)
		removeEqualFold(&c.Audit.ExtraKeysHCL, "sink")
	}

	for _, k := range []string{"enabled_schedulers", "start_join", "retry_join", "server_join"} {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, k)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "server")
	}

	for _, k := range []string{"datadog_tags"} {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, k)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "telemetry")
	}

	return helper.UnusedKeys(c)
}
<commit_msg>fix compilation with  correct func<commit_after>package agent

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/hashicorp/hcl"
	"github.com/hashicorp/nomad/helper"
	"github.com/hashicorp/nomad/nomad/structs/config"
)

func ParseConfigFile(path string) (*Config, error) {
	// slurp
	var buf bytes.Buffer
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	if _, err := io.Copy(&buf, f); err != nil {
		return nil, err
	}

	// parse
	c := &Config{
		Client:    &ClientConfig{ServerJoin: &ServerJoin{}},
		ACL:       &ACLConfig{},
		Audit:     &config.AuditConfig{},
		Server:    &ServerConfig{ServerJoin: &ServerJoin{}},
		Consul:    &config.ConsulConfig{},
		Autopilot: &config.AutopilotConfig{},
		Telemetry: &Telemetry{},
		Vault:     &config.VaultConfig{},
	}

	err = hcl.Decode(c, buf.String())
	if err != nil {
		return nil, err
	}

	// convert strings to time.Durations
	tds := []td{
		{"gc_interval", &c.Client.GCInterval, &c.Client.GCIntervalHCL},
		{"acl.token_ttl", &c.ACL.TokenTTL, &c.ACL.TokenTTLHCL},
		{"acl.policy_ttl", &c.ACL.PolicyTTL, &c.ACL.PolicyTTLHCL},
		{"client.server_join.retry_interval", &c.Client.ServerJoin.RetryInterval, &c.Client.ServerJoin.RetryIntervalHCL},
		{"server.heartbeat_grace", &c.Server.HeartbeatGrace, &c.Server.HeartbeatGraceHCL},
		{"server.min_heartbeat_ttl", &c.Server.MinHeartbeatTTL, &c.Server.MinHeartbeatTTLHCL},
		{"server.retry_interval", &c.Server.RetryInterval, &c.Server.RetryIntervalHCL},
		{"server.server_join.retry_interval", &c.Server.ServerJoin.RetryInterval, &c.Server.ServerJoin.RetryIntervalHCL},
		{"consul.timeout", &c.Consul.Timeout, &c.Consul.TimeoutHCL},
		{"autopilot.server_stabilization_time", &c.Autopilot.ServerStabilizationTime, &c.Autopilot.ServerStabilizationTimeHCL},
		{"autopilot.last_contact_threshold", &c.Autopilot.LastContactThreshold, &c.Autopilot.LastContactThresholdHCL},
		{"telemetry.collection_interval", &c.Telemetry.collectionInterval, &c.Telemetry.CollectionInterval},
	}

	// Add enterprise audit sinks for time.Duration parsing
	for i, sink := range c.Audit.Sinks {
		tds = append(tds, td{
			fmt.Sprintf("audit.sink.%d", i), &sink.RotateDuration, &sink.RotateDurationHCL,
		})
	}

	// convert strings to time.Durations
	err = durations(tds)
	if err != nil {
		return nil, err
	}

	// report unexpected keys
	err = extraKeys(c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

// td holds args for one duration conversion
type td struct {
	path string
	td   *time.Duration
	str  *string
}

// durations parses the duration strings specified in the config files
// into time.Durations
func durations(xs []td) error {
	for _, x := range xs {
		if x.td != nil && x.str != nil && "" != *x.str {
			d, err := time.ParseDuration(*x.str)
			if err != nil {
				return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
			}

			*x.td = d
		}
	}

	return nil
}

func extraKeys(c *Config) error {
	// hcl leaves behind extra keys when parsing JSON. These keys
	// are kept on the top level, taken from slices or the keys of
	// structs contained in slices. Clean up before looking for
	// extra keys.
	for range c.HTTPAPIResponseHeaders {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "http_api_response_headers")
	}

	for _, p := range c.Plugins {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, p.Name)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "config")
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "plugin")
	}

	for _, k := range []string{"options", "meta", "chroot_env", "servers", "server_join"} {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, k)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "client")
	}

	// stats is an unused key, continue to silently ignore it
	helper.RemoveEqualFold(&c.Client.ExtraKeysHCL, "stats")

	// Remove HostVolume extra keys
	for _, hv := range c.Client.HostVolumes {
		helper.RemoveEqualFold(&c.Client.ExtraKeysHCL, hv.Name)
		helper.RemoveEqualFold(&c.Client.ExtraKeysHCL, "host_volume")
	}

	// Remove AuditConfig extra keys
	for _, f := range c.Audit.Filters {
		helper.RemoveEqualFold(&c.Audit.ExtraKeysHCL, f.Name)
		helper.RemoveEqualFold(&c.Audit.ExtraKeysHCL, "filter")
	}

	for _, s := range c.Audit.Sinks {
		helper.RemoveEqualFold(&c.Audit.ExtraKeysHCL, s.Name)
		helper.RemoveEqualFold(&c.Audit.ExtraKeysHCL, "sink")
	}

	for _, k := range []string{"enabled_schedulers", "start_join", "retry_join", "server_join"} {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, k)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "server")
	}

	for _, k := range []string{"datadog_tags"} {
		helper.RemoveEqualFold(&c.ExtraKeysHCL, k)
		helper.RemoveEqualFold(&c.ExtraKeysHCL, "telemetry")
	}

	return helper.UnusedKeys(c)
}
<|endoftext|>


<commit_before>// package idletiming provides mechanisms for adding idle timeouts to net.Conn
// and net.Listener.
package idletiming

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

	"github.com/getlantern/golog"
)

var (
	epoch = time.Unix(0, 0)
	log   = golog.LoggerFor("idletiming")

	// ErrIdled is return when attempting to use a network connection that was
	// closed because of idling.
	ErrIdled = errors.New("Use of idled network connection")
)

// Conn creates a new net.Conn wrapping the given net.Conn that times out after
// the specified period. Once a connection has timed out, any pending reads or
// writes will return io.EOF and the underlying connection will be closed.
//
// idleTimeout specifies how long to wait for inactivity before considering
// connection idle.
//
// If onIdle is specified, it will be called to indicate when the connection has
// idled and been closed.
func Conn(conn net.Conn, idleTimeout time.Duration, onIdle func()) *IdleTimingConn {
	c := &IdleTimingConn{
		conn:             conn,
		idleTimeout:      idleTimeout,
		halfIdleTimeout:  time.Duration(idleTimeout.Nanoseconds() / 2),
		activeCh:         make(chan bool, 1),
		closedCh:         make(chan bool, 1),
		lastActivityTime: int64(time.Now().UnixNano()),
	}

	go func() {
		timer := time.NewTimer(idleTimeout)
		defer timer.Stop()
		for {
			select {
			case <-c.activeCh:
				// We're active, continue
				timer.Reset(idleTimeout)
				atomic.StoreInt64(&c.lastActivityTime, time.Now().UnixNano())
				continue
			case <-timer.C:
				c.Close()
				if onIdle != nil {
					onIdle()
				}
				return
			case <-c.closedCh:
				return
			}
		}
	}()

	return c
}

// IdleTimingConn is a net.Conn that wraps another net.Conn and that times out
// if idle for more than idleTimeout.
type IdleTimingConn struct {
	// Keep them at the top to make sure 64-bit alignment, see
	// https://golang.org/pkg/sync/atomic/#pkg-note-BUG
	readDeadline     int64
	writeDeadline    int64
	lastActivityTime int64

	conn             net.Conn
	idleTimeout      time.Duration
	halfIdleTimeout  time.Duration
	activeCh         chan bool
	closedCh         chan bool
	closeMutex       sync.RWMutex // prevents Close() from interfering with io operations
	closed           bool
	hasReadAfterIdle int32
}

// TimesOutIn returns how much time is left before this connection will time
// out, assuming there is no further activity.
func (c *IdleTimingConn) TimesOutIn() time.Duration {
	return c.TimesOutAt().Sub(time.Now())
}

// TimesOutAt returns the time at which this connection will time out, assuming
// there is no further activity
func (c *IdleTimingConn) TimesOutAt() time.Time {
	return time.Unix(0, atomic.LoadInt64(&c.lastActivityTime)).Add(c.idleTimeout)
}

// Read implements the method from io.Reader
func (c *IdleTimingConn) Read(b []byte) (int, error) {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosedFirstTime(&c.hasReadAfterIdle, io.EOF); err != nil {
		return 0, err
	}

	totalN := 0
	readDeadline := time.Unix(0, atomic.LoadInt64(&c.readDeadline))

	// Continually read while we can, always setting a deadline that's less than
	// our idleTimeout so that we can update our active status before we hit the
	// idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if readDeadline != epoch && !maxDeadline.Before(readDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetReadDeadline(readDeadline); err != nil {
				log.Tracef("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetReadDeadline(maxDeadline); err != nil {
				log.Tracef("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Write implements the method from io.Reader
func (c *IdleTimingConn) Write(b []byte) (int, error) {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return 0, err
	}

	totalN := 0
	writeDeadline := time.Unix(0, atomic.LoadInt64(&c.writeDeadline))

	// Continually write while we can, always setting a deadline that's less
	// than our idleTimeout so that we can update our active status before we
	// hit the idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if writeDeadline != epoch && !maxDeadline.Before(writeDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetWriteDeadline(writeDeadline); err != nil {
				log.Tracef("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetWriteDeadline(maxDeadline); err != nil {
				log.Tracef("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Close this IdleTimingConn. This will close the underlying net.Conn as well,
// returning the error from calling its Close method.
func (c *IdleTimingConn) Close() error {
	c.closeMutex.Lock()
	defer c.closeMutex.Unlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	c.closed = true

	select {
	case c.closedCh <- true:
		// close accepted
	default:
		// already closing, ignore
	}
	return c.conn.Close()
}

func (c *IdleTimingConn) LocalAddr() net.Addr {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	return c.conn.LocalAddr()
}

func (c *IdleTimingConn) RemoteAddr() net.Addr {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	return c.conn.RemoteAddr()
}

func (c *IdleTimingConn) SetDeadline(t time.Time) error {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	if err := c.SetReadDeadline(t); err != nil {
		log.Tracef("Unable to set read deadline: %v", err)
	}
	if err := c.SetWriteDeadline(t); err != nil {
		log.Tracef("Unable to set write deadline: %v", err)
	}
	return nil
}

func (c *IdleTimingConn) SetReadDeadline(t time.Time) error {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	atomic.StoreInt64(&c.readDeadline, t.UnixNano())
	return nil
}

func (c *IdleTimingConn) SetWriteDeadline(t time.Time) error {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	atomic.StoreInt64(&c.writeDeadline, t.UnixNano())
	return nil
}

func (c *IdleTimingConn) markActive(n int) bool {
	if n > 0 {
		select {
		case c.activeCh <- true:
			// ok
		default:
			// still waiting to process previous markActive
		}
		return true
	}
	return false
}

func (c *IdleTimingConn) checkClosed() error {
	return c.checkClosedFirstTime(nil, nil)
}

func (c *IdleTimingConn) checkClosedFirstTime(hasDone *int32, firstTimeError error) error {
	if c.closed {
		if hasDone != nil && atomic.CompareAndSwapInt32(hasDone, 0, 1) {
			return firstTimeError
		}
		return ErrIdled
	}
	return nil
}

func isTimeout(err error) bool {
	if netErr, ok := err.(net.Error); ok {
		return netErr.Timeout()
	}
	return false
}
<commit_msg>replace int64 with guarded time to store deadlines<commit_after>// package idletiming provides mechanisms for adding idle timeouts to net.Conn
// and net.Listener.
package idletiming

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

	"github.com/getlantern/golog"
)

var (
	log = golog.LoggerFor("idletiming")

	// ErrIdled is return when attempting to use a network connection that was
	// closed because of idling.
	ErrIdled = errors.New("Use of idled network connection")
)

// Conn creates a new net.Conn wrapping the given net.Conn that times out after
// the specified period. Once a connection has timed out, any pending reads or
// writes will return io.EOF and the underlying connection will be closed.
//
// idleTimeout specifies how long to wait for inactivity before considering
// connection idle.
//
// If onIdle is specified, it will be called to indicate when the connection has
// idled and been closed.
func Conn(conn net.Conn, idleTimeout time.Duration, onIdle func()) *IdleTimingConn {
	c := &IdleTimingConn{
		conn:             conn,
		idleTimeout:      idleTimeout,
		halfIdleTimeout:  time.Duration(idleTimeout.Nanoseconds() / 2),
		activeCh:         make(chan bool, 1),
		closedCh:         make(chan bool, 1),
		lastActivityTime: int64(time.Now().UnixNano()),
	}

	go func() {
		timer := time.NewTimer(idleTimeout)
		defer timer.Stop()
		for {
			select {
			case <-c.activeCh:
				// We're active, continue
				timer.Reset(idleTimeout)
				atomic.StoreInt64(&c.lastActivityTime, time.Now().UnixNano())
				continue
			case <-timer.C:
				c.Close()
				if onIdle != nil {
					onIdle()
				}
				return
			case <-c.closedCh:
				return
			}
		}
	}()

	return c
}

// IdleTimingConn is a net.Conn that wraps another net.Conn and that times out
// if idle for more than idleTimeout.
type IdleTimingConn struct {
	// Keep it at the top to make sure 64-bit alignment, see
	// https://golang.org/pkg/sync/atomic/#pkg-note-BUG
	lastActivityTime int64
	readDeadline     guardedTime
	writeDeadline    guardedTime

	conn             net.Conn
	idleTimeout      time.Duration
	halfIdleTimeout  time.Duration
	activeCh         chan bool
	closedCh         chan bool
	closeMutex       sync.RWMutex // prevents Close() from interfering with io operations
	closed           bool
	hasReadAfterIdle int32
}

// TimesOutIn returns how much time is left before this connection will time
// out, assuming there is no further activity.
func (c *IdleTimingConn) TimesOutIn() time.Duration {
	return c.TimesOutAt().Sub(time.Now())
}

// TimesOutAt returns the time at which this connection will time out, assuming
// there is no further activity
func (c *IdleTimingConn) TimesOutAt() time.Time {
	return time.Unix(0, atomic.LoadInt64(&c.lastActivityTime)).Add(c.idleTimeout)
}

// Read implements the method from io.Reader
func (c *IdleTimingConn) Read(b []byte) (int, error) {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosedFirstTime(&c.hasReadAfterIdle, io.EOF); err != nil {
		return 0, err
	}

	totalN := 0
	readDeadline := c.readDeadline.Get()

	// Continually read while we can, always setting a deadline that's less than
	// our idleTimeout so that we can update our active status before we hit the
	// idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if !readDeadline.IsZero() && !maxDeadline.Before(readDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetReadDeadline(readDeadline); err != nil {
				log.Tracef("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetReadDeadline(maxDeadline); err != nil {
				log.Tracef("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Write implements the method from io.Reader
func (c *IdleTimingConn) Write(b []byte) (int, error) {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return 0, err
	}

	totalN := 0
	writeDeadline := c.writeDeadline.Get()

	// Continually write while we can, always setting a deadline that's less
	// than our idleTimeout so that we can update our active status before we
	// hit the idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if !writeDeadline.IsZero() && !maxDeadline.Before(writeDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetWriteDeadline(writeDeadline); err != nil {
				log.Tracef("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetWriteDeadline(maxDeadline); err != nil {
				log.Tracef("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Close this IdleTimingConn. This will close the underlying net.Conn as well,
// returning the error from calling its Close method.
func (c *IdleTimingConn) Close() error {
	c.closeMutex.Lock()
	defer c.closeMutex.Unlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	c.closed = true

	select {
	case c.closedCh <- true:
		// close accepted
	default:
		// already closing, ignore
	}
	return c.conn.Close()
}

func (c *IdleTimingConn) LocalAddr() net.Addr {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	return c.conn.LocalAddr()
}

func (c *IdleTimingConn) RemoteAddr() net.Addr {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	return c.conn.RemoteAddr()
}

func (c *IdleTimingConn) SetDeadline(t time.Time) error {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	if err := c.SetReadDeadline(t); err != nil {
		log.Tracef("Unable to set read deadline: %v", err)
	}
	if err := c.SetWriteDeadline(t); err != nil {
		log.Tracef("Unable to set write deadline: %v", err)
	}
	return nil
}

func (c *IdleTimingConn) SetReadDeadline(t time.Time) error {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	c.readDeadline.Set(t)
	return nil
}

func (c *IdleTimingConn) SetWriteDeadline(t time.Time) error {
	c.closeMutex.RLock()
	defer c.closeMutex.RUnlock()

	if err := c.checkClosed(); err != nil {
		return err
	}

	c.writeDeadline.Set(t)
	return nil
}

func (c *IdleTimingConn) markActive(n int) bool {
	if n > 0 {
		select {
		case c.activeCh <- true:
			// ok
		default:
			// still waiting to process previous markActive
		}
		return true
	}
	return false
}

func (c *IdleTimingConn) checkClosed() error {
	return c.checkClosedFirstTime(nil, nil)
}

func (c *IdleTimingConn) checkClosedFirstTime(hasDone *int32, firstTimeError error) error {
	if c.closed {
		if hasDone != nil && atomic.CompareAndSwapInt32(hasDone, 0, 1) {
			return firstTimeError
		}
		return ErrIdled
	}
	return nil
}

func isTimeout(err error) bool {
	if netErr, ok := err.(net.Error); ok {
		return netErr.Timeout()
	}
	return false
}

type guardedTime struct {
	sync.RWMutex
	t time.Time
}

func (g *guardedTime) Get() time.Time {
	g.RLock()
	retval := g.t
	g.RUnlock()
	return retval
}

func (g *guardedTime) Set(t time.Time) {
	g.Lock()
	g.t = t
	g.Unlock()
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   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 dao

import (
	"github.com/vmware/harbor/models"

	"errors"
	"fmt"
	"time"

	"github.com/astaxie/beego/orm"
	"github.com/vmware/harbor/utils/log"
)

//TODO:transaction, return err

// AddProject adds a project to the database along with project roles information and access log records.
func AddProject(project models.Project) (int64, error) {

	if isIllegalLength(project.Name, 4, 30) {
		return 0, errors.New("project name is illegal in length. (greater than 4 or less than 30)")
	}
	if isContainIllegalChar(project.Name, []string{"~", "-", "$", "\\", "[", "]", "{", "}", "(", ")", "&", "^", "%", "*", "<", ">", "\"", "'", "/", "?", "@"}) {
		return 0, errors.New("project name contains illegal characters")
	}

	o := orm.NewOrm()

	p, err := o.Raw("insert into project (owner_id, name, creation_time, update_time, deleted, public) values (?, ?, ?, ?, ?, ?)").Prepare()
	if err != nil {
		return 0, err
	}

	now := time.Now()
	r, err := p.Exec(project.OwnerID, project.Name, now, now, project.Deleted, project.Public)
	if err != nil {
		return 0, err
	}

	projectID, err := r.LastInsertId()
	if err != nil {
		return 0, err
	}

	if err = AddProjectMember(projectID, project.OwnerID, models.PROJECTADMIN); err != nil {
		return projectID, err
	}

	accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", RepoTag: "N/A", GUID: "N/A", Operation: "create", OpTime: time.Now()}
	err = AddAccessLog(accessLog)

	return projectID, err
}

// IsProjectPublic ...
func IsProjectPublic(projectName string) bool {
	project, err := GetProjectByName(projectName)
	if err != nil {
		log.Errorf("Error occurred in GetProjectByName: %v", err)
		return false
	}
	if project == nil {
		return false
	}
	return project.Public == 1
}

//ProjectExists returns whether the project exists according to its name of ID.
func ProjectExists(nameOrID interface{}) (bool, error) {
	o := orm.NewOrm()
	type dummy struct{}
	sql := `select project_id from project where deleted = 0 and `
	switch nameOrID.(type) {
	case int64:
		sql += `project_id = ?`
	case string:
		sql += `name = ?`
	default:
		return false, fmt.Errorf("Invalid nameOrId: %v", nameOrID)
	}

	var d []dummy
	num, err := o.Raw(sql, nameOrID).QueryRows(&d)
	if err != nil {
		return false, err
	}
	return num > 0, nil

}

// GetProjectByID ...
func GetProjectByID(id int64) (*models.Project, error) {
	o := orm.NewOrm()

	sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.update_time, p.public
		from project p left join user u on p.owner_id = u.user_id where p.deleted = 0 and p.project_id = ?`
	queryParam := make([]interface{}, 1)
	queryParam = append(queryParam, id)

	p := []models.Project{}
	count, err := o.Raw(sql, queryParam).QueryRows(&p)

	if err != nil {
		return nil, err
	}

	if count == 0 {
		return nil, nil
	}

	return &p[0], nil
}

// GetProjectByName ...
func GetProjectByName(name string) (*models.Project, error) {
	o := orm.NewOrm()
	var p []models.Project
	n, err := o.Raw(`select * from project where name = ? and deleted = 0`, name).QueryRows(&p)
	if err != nil {
		return nil, err
	}

	if n == 0 {
		return nil, nil
	}

	return &p[0], nil
}

// GetPermission gets roles that the user has according to the project.
func GetPermission(username, projectName string) (string, error) {
	o := orm.NewOrm()

	sql := `select r.role_code from role as r
		inner join project_member as pm on r.role_id = pm.role
		inner join user as u on u.user_id = pm.user_id
		inner join project p on p.project_id = pm.project_id
		where u.username = ? and p.name = ? and u.deleted = 0 and p.deleted = 0`

	var r []models.Role
	n, err := o.Raw(sql, username, projectName).QueryRows(&r)
	if err != nil {
		return "", err
	}

	if n == 0 {
		return "", nil
	}

	return r[0].RoleCode, nil
}

// ToggleProjectPublicity toggles the publicity of the project.
func ToggleProjectPublicity(projectID int64, publicity int) error {
	o := orm.NewOrm()
	sql := "update project set public = ? where project_id = ?"
	_, err := o.Raw(sql, publicity, projectID).Exec()
	return err
}

// SearchProjects returns a project list,
// which satisfies the following conditions:
// 1. the project is not deleted
// 2. the prject is public or the user is a member of the project
func SearchProjects(userID int) ([]models.Project, error) {
	o := orm.NewOrm()
	sql := `select distinct p.project_id, p.name, p.public
		from project p
		left join project_member pm on p.project_id = pm.project_id
		where (pm.user_id = ? or p.public = 1) and p.deleted = 0`

	var projects []models.Project

	if _, err := o.Raw(sql, userID).QueryRows(&projects); err != nil {
		return nil, err
	}

	return projects, nil
}

// GetUserRelevantProjects returns the projects of the user which are not deleted and name like projectName
func GetUserRelevantProjects(userID int, projectName string) ([]models.Project, error) {
	o := orm.NewOrm()
	sql := `select distinct
		p.project_id, p.owner_id, p.name,p.creation_time, p.update_time, p.public, pm.role role
	 from project p
		left join project_member pm on p.project_id = pm.project_id
	 where p.deleted = 0 and pm.user_id= ?`

	queryParam := make([]interface{}, 1)
	queryParam = append(queryParam, userID)
	if projectName != "" {
		sql += " and p.name like ? "
		queryParam = append(queryParam, projectName)
	}
	sql += " order by p.name "
	var r []models.Project
	_, err := o.Raw(sql, queryParam).QueryRows(&r)
	if err != nil {
		return nil, err
	}
	return r, nil
}

//GetPublicProjects returns all public projects whose name like projectName
func GetPublicProjects(projectName string) ([]models.Project, error) {
	o := orm.NewOrm()
	var publicProjects []models.Project
	sql := `select project_id, owner_id, creation_time, update_time, name, public
		from project
		where deleted = 0 and public=1`
	queryParam := make([]interface{}, 1)
	if len(projectName) > 0 {
		sql += " and name like ? "
		queryParam = append(queryParam, projectName)
	}
	sql += " order by name "
	_, err := o.Raw(sql, queryParam).QueryRows(&publicProjects)
	if err != nil {
		return nil, err
	}
	return publicProjects, nil
}

// GetAllProjects returns all projects which are not deleted and name like projectName
func GetAllProjects(projectName string) ([]models.Project, error) {
	o := orm.NewOrm()
	sql := `select project_id, owner_id, creation_time, update_time, name, public
		from project
		where deleted = 0`
	queryParam := make([]interface{}, 1)
	if len(projectName) > 0 {
		sql += " and name like ? "
		queryParam = append(queryParam, projectName)
	}
	sql += " order by name "
	var projects []models.Project
	if _, err := o.Raw(sql, queryParam).QueryRows(&projects); err != nil {
		return nil, err
	}
	return projects, nil
}
<commit_msg>add a new func to reuse code<commit_after>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   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 dao

import (
	"github.com/vmware/harbor/models"

	"errors"
	"fmt"
	"time"

	"github.com/astaxie/beego/orm"
	"github.com/vmware/harbor/utils/log"
)

//TODO:transaction, return err

// AddProject adds a project to the database along with project roles information and access log records.
func AddProject(project models.Project) (int64, error) {

	if isIllegalLength(project.Name, 4, 30) {
		return 0, errors.New("project name is illegal in length. (greater than 4 or less than 30)")
	}
	if isContainIllegalChar(project.Name, []string{"~", "-", "$", "\\", "[", "]", "{", "}", "(", ")", "&", "^", "%", "*", "<", ">", "\"", "'", "/", "?", "@"}) {
		return 0, errors.New("project name contains illegal characters")
	}

	o := orm.NewOrm()

	p, err := o.Raw("insert into project (owner_id, name, creation_time, update_time, deleted, public) values (?, ?, ?, ?, ?, ?)").Prepare()
	if err != nil {
		return 0, err
	}

	now := time.Now()
	r, err := p.Exec(project.OwnerID, project.Name, now, now, project.Deleted, project.Public)
	if err != nil {
		return 0, err
	}

	projectID, err := r.LastInsertId()
	if err != nil {
		return 0, err
	}

	if err = AddProjectMember(projectID, project.OwnerID, models.PROJECTADMIN); err != nil {
		return projectID, err
	}

	accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", RepoTag: "N/A", GUID: "N/A", Operation: "create", OpTime: time.Now()}
	err = AddAccessLog(accessLog)

	return projectID, err
}

// IsProjectPublic ...
func IsProjectPublic(projectName string) bool {
	project, err := GetProjectByName(projectName)
	if err != nil {
		log.Errorf("Error occurred in GetProjectByName: %v", err)
		return false
	}
	if project == nil {
		return false
	}
	return project.Public == 1
}

//ProjectExists returns whether the project exists according to its name of ID.
func ProjectExists(nameOrID interface{}) (bool, error) {
	o := orm.NewOrm()
	type dummy struct{}
	sql := `select project_id from project where deleted = 0 and `
	switch nameOrID.(type) {
	case int64:
		sql += `project_id = ?`
	case string:
		sql += `name = ?`
	default:
		return false, fmt.Errorf("Invalid nameOrId: %v", nameOrID)
	}

	var d []dummy
	num, err := o.Raw(sql, nameOrID).QueryRows(&d)
	if err != nil {
		return false, err
	}
	return num > 0, nil

}

// GetProjectByID ...
func GetProjectByID(id int64) (*models.Project, error) {
	o := orm.NewOrm()

	sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.update_time, p.public
		from project p left join user u on p.owner_id = u.user_id where p.deleted = 0 and p.project_id = ?`
	queryParam := make([]interface{}, 1)
	queryParam = append(queryParam, id)

	p := []models.Project{}
	count, err := o.Raw(sql, queryParam).QueryRows(&p)

	if err != nil {
		return nil, err
	}

	if count == 0 {
		return nil, nil
	}

	return &p[0], nil
}

// GetProjectByName ...
func GetProjectByName(name string) (*models.Project, error) {
	o := orm.NewOrm()
	var p []models.Project
	n, err := o.Raw(`select * from project where name = ? and deleted = 0`, name).QueryRows(&p)
	if err != nil {
		return nil, err
	}

	if n == 0 {
		return nil, nil
	}

	return &p[0], nil
}

// GetPermission gets roles that the user has according to the project.
func GetPermission(username, projectName string) (string, error) {
	o := orm.NewOrm()

	sql := `select r.role_code from role as r
		inner join project_member as pm on r.role_id = pm.role
		inner join user as u on u.user_id = pm.user_id
		inner join project p on p.project_id = pm.project_id
		where u.username = ? and p.name = ? and u.deleted = 0 and p.deleted = 0`

	var r []models.Role
	n, err := o.Raw(sql, username, projectName).QueryRows(&r)
	if err != nil {
		return "", err
	}

	if n == 0 {
		return "", nil
	}

	return r[0].RoleCode, nil
}

// ToggleProjectPublicity toggles the publicity of the project.
func ToggleProjectPublicity(projectID int64, publicity int) error {
	o := orm.NewOrm()
	sql := "update project set public = ? where project_id = ?"
	_, err := o.Raw(sql, publicity, projectID).Exec()
	return err
}

// SearchProjects returns a project list,
// which satisfies the following conditions:
// 1. the project is not deleted
// 2. the prject is public or the user is a member of the project
func SearchProjects(userID int) ([]models.Project, error) {
	o := orm.NewOrm()
	sql := `select distinct p.project_id, p.name, p.public
		from project p
		left join project_member pm on p.project_id = pm.project_id
		where (pm.user_id = ? or p.public = 1) and p.deleted = 0`

	var projects []models.Project

	if _, err := o.Raw(sql, userID).QueryRows(&projects); err != nil {
		return nil, err
	}

	return projects, nil
}

// GetUserRelevantProjects returns the projects of the user which are not deleted and name like projectName
func GetUserRelevantProjects(userID int, projectName string) ([]models.Project, error) {
	o := orm.NewOrm()
	sql := `select distinct
		p.project_id, p.owner_id, p.name,p.creation_time, p.update_time, p.public, pm.role role
	 from project p
		left join project_member pm on p.project_id = pm.project_id
	 where p.deleted = 0 and pm.user_id= ?`

	queryParam := make([]interface{}, 1)
	queryParam = append(queryParam, userID)
	if projectName != "" {
		sql += " and p.name like ? "
		queryParam = append(queryParam, projectName)
	}
	sql += " order by p.name "
	var r []models.Project
	_, err := o.Raw(sql, queryParam).QueryRows(&r)
	if err != nil {
		return nil, err
	}
	return r, nil
}

//GetPublicProjects returns all public projects whose name like projectName
func GetPublicProjects(projectName string) ([]models.Project, error) {
	publicProjects, err := getProjects(1, projectName)
	if err != nil {
		return nil, err

	}
	return publicProjects, nil
}

// GetAllProjects returns all projects which are not deleted and name like projectName
func GetAllProjects(projectName string) ([]models.Project, error) {
	allProjects, err := getProjects(0, projectName)
	if err != nil {
		return nil, err
	}
	return allProjects, nil
}

func getProjects(public int, projectName string) ([]models.Project, error) {
	o := orm.NewOrm()
	sql := `select project_id, owner_id, creation_time, update_time, name, public
		from project
		where deleted = 0`
	queryParam := make([]interface{}, 1)
	if public == 1 {
		sql += "and public = ?"
		queryParam = append(queryParam, public)
	}
	if len(projectName) > 0 {
		sql += " and name like ? "
		queryParam = append(queryParam, projectName)
	}
	sql += " order by name "
	var projects []models.Project
	if _, err := o.Raw(sql, queryParam).QueryRows(&projects); err != nil {
		return nil, err
	}
	return projects, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package driver

import (
	"errors"

	"github.com/gopherjs/gopherjs/js"
)

type Player struct {
	sampleRate        int
	channelNum        int
	bytesPerSample    int
	positionInSamples int64
	bufferedData      []byte
	context           *js.Object
}

func NewPlayer(sampleRate, channelNum, bytesPerSample int) (*Player, error) {
	class := js.Global.Get("AudioContext")
	if class == js.Undefined {
		class = js.Global.Get("webkitAudioContext")
	}
	if class == js.Undefined {
		return nil, errors.New("driver: audio couldn't be initialized")
	}
	p := &Player{
		sampleRate:     sampleRate,
		channelNum:     channelNum,
		bytesPerSample: bytesPerSample,
		bufferedData:   []byte{},
		context:        class.New(),
	}
	p.positionInSamples = int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
	return p, nil
}

func toLR(data []byte) ([]int16, []int16) {
	l := make([]int16, len(data)/4)
	r := make([]int16, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = int16(data[4*i]) | int16(data[4*i+1])<<8
		r[i] = int16(data[4*i+2]) | int16(data[4*i+3])<<8
	}
	return l, r
}

func (p *Player) Proceed(data []byte) error {
	p.bufferedData = append(p.bufferedData, data...)
	c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
	if p.positionInSamples < c {
		p.positionInSamples = c
	}
	// Looks like low sample rate (e.g. 22050) requires a quite long buffer (#205).
	// 4096 is not perfect but it reduce noise to some extent.
	const dataSize = 4096
	for dataSize <= len(p.bufferedData) {
		data := p.bufferedData[:dataSize]
		size := len(data) / p.bytesPerSample / p.channelNum
		// TODO: size must be const or you'll get noise (e.g. sample rate is 22050)
		buf := p.context.Call("createBuffer", p.channelNum, size, p.sampleRate)
		l := buf.Call("getChannelData", 0)
		r := buf.Call("getChannelData", 1)
		il, ir := toLR(data)
		const max = 1 << 15
		for i := 0; i < len(il); i++ {
			l.SetIndex(i, float64(il[i])/max)
			r.SetIndex(i, float64(ir[i])/max)
		}
		s := p.context.Call("createBufferSource")
		s.Set("buffer", buf)
		s.Call("connect", p.context.Get("destination"))
		s.Call("start", float64(p.positionInSamples)/float64(p.sampleRate))
		p.positionInSamples += int64(len(il))
		p.bufferedData = p.bufferedData[dataSize:]
	}
	return nil
}

func (p *Player) Close() error {
	return nil
}
<commit_msg>audio: Bug fix: Handle touch event for iOS (#296)<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package driver

import (
	"errors"
	"strings"

	"github.com/gopherjs/gopherjs/js"
)

type Player struct {
	sampleRate        int
	channelNum        int
	bytesPerSample    int
	positionInSamples int64
	bufferedData      []byte
	context           *js.Object
}

func isIOS() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "iPhone") {
		return false
	}
	return true
}

func NewPlayer(sampleRate, channelNum, bytesPerSample int) (*Player, error) {
	class := js.Global.Get("AudioContext")
	if class == js.Undefined {
		class = js.Global.Get("webkitAudioContext")
	}
	if class == js.Undefined {
		return nil, errors.New("driver: audio couldn't be initialized")
	}
	p := &Player{
		sampleRate:     sampleRate,
		channelNum:     channelNum,
		bytesPerSample: bytesPerSample,
		bufferedData:   []byte{},
		context:        class.New(),
	}
	// iOS requires touch event to use AudioContext.
	if isIOS() {
		js.Global.Get("document").Call("addEventListener", "touchend", func() {
			p.context.Call("createBufferSource").Call("start", 0)
			p.positionInSamples = int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
		})
	}
	p.positionInSamples = int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
	return p, nil
}

func toLR(data []byte) ([]int16, []int16) {
	l := make([]int16, len(data)/4)
	r := make([]int16, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = int16(data[4*i]) | int16(data[4*i+1])<<8
		r[i] = int16(data[4*i+2]) | int16(data[4*i+3])<<8
	}
	return l, r
}

func (p *Player) Proceed(data []byte) error {
	p.bufferedData = append(p.bufferedData, data...)
	c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
	if p.positionInSamples < c {
		p.positionInSamples = c
	}
	// Looks like low sample rate (e.g. 22050) requires a quite long buffer (#205).
	// 4096 is not perfect but it reduce noise to some extent.
	const dataSize = 4096
	for dataSize <= len(p.bufferedData) {
		data := p.bufferedData[:dataSize]
		size := len(data) / p.bytesPerSample / p.channelNum
		// TODO: size must be const or you'll get noise (e.g. sample rate is 22050)
		buf := p.context.Call("createBuffer", p.channelNum, size, p.sampleRate)
		l := buf.Call("getChannelData", 0)
		r := buf.Call("getChannelData", 1)
		il, ir := toLR(data)
		const max = 1 << 15
		for i := 0; i < len(il); i++ {
			l.SetIndex(i, float64(il[i])/max)
			r.SetIndex(i, float64(ir[i])/max)
		}
		s := p.context.Call("createBufferSource")
		s.Set("buffer", buf)
		s.Call("connect", p.context.Get("destination"))
		s.Call("start", float64(p.positionInSamples)/float64(p.sampleRate))
		p.positionInSamples += int64(len(il))
		p.bufferedData = p.bufferedData[dataSize:]
	}
	return nil
}

func (p *Player) Close() error {
	return nil
}
<|endoftext|>


<commit_before>package authentication

import (
	"context"
	"fmt"
	"io/ioutil"

	"github.com/Azure/go-autorest/autorest"
	"github.com/hashicorp/go-multierror"
	authWrapper "github.com/manicminer/hamilton-autorest/auth"
	"github.com/manicminer/hamilton/auth"
	"github.com/manicminer/hamilton/environments"
)

type oidcAuth struct {
	auxiliaryTenantIds  []string
	clientId            string
	environment         string
	idToken             string
	idTokenPath         string
	idTokenRequestToken string
	idTokenRequestUrl   string
	tenantId            string
}

func (a oidcAuth) build(b Builder) (authMethod, error) {
	method := oidcAuth{
		auxiliaryTenantIds:  b.AuxiliaryTenantIDs,
		clientId:            b.ClientID,
		environment:         b.Environment,
		idToken:             b.IDToken,
		idTokenPath:         b.IDTokenPath,
		idTokenRequestUrl:   b.IDTokenRequestURL,
		idTokenRequestToken: b.IDTokenRequestToken,
		tenantId:            b.TenantID,
	}
	return method, nil
}

func (a oidcAuth) isApplicable(b Builder) bool {
	return b.SupportsOIDCAuth && b.UseMicrosoftGraph && (b.IDToken != "" || b.IDTokenPath != "" || (b.IDTokenRequestURL != "" && b.IDTokenRequestToken != ""))
}

func (a oidcAuth) name() string {
	return "OIDC"
}

func (a oidcAuth) getADALToken(_ context.Context, _ autorest.Sender, _ *OAuthConfig, _ string) (autorest.Authorizer, error) {
	return nil, fmt.Errorf("ADAL tokens are not supported for OIDC authentication")
}

func (a oidcAuth) getMSALToken(ctx context.Context, api environments.Api, _ autorest.Sender, _ *OAuthConfig, _ string) (autorest.Authorizer, error) {
	environment, err := environments.EnvironmentFromString(a.environment)
	if err != nil {
		return nil, fmt.Errorf("environment config error: %v", err)
	}

	if a.idToken == "" && a.idTokenPath == "" {
		conf := auth.GitHubOIDCConfig{
			Environment:         environment,
			TenantID:            a.tenantId,
			AuxiliaryTenantIDs:  a.auxiliaryTenantIds,
			ClientID:            a.clientId,
			IDTokenRequestURL:   a.idTokenRequestUrl,
			IDTokenRequestToken: a.idTokenRequestToken,
			Scopes:              []string{api.DefaultScope()},
		}
		return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx)}, nil
	}

	idToken := a.idToken

	if a.idTokenPath != "" {
		idToken, err = a.readTokenFile(a.idTokenPath)

		if err != nil {
			return nil, fmt.Errorf("reading token file: %v", err)
		}
	}

	conf := auth.ClientCredentialsConfig{
		Environment:        environment,
		TenantID:           a.tenantId,
		AuxiliaryTenantIDs: a.auxiliaryTenantIds,
		ClientID:           a.clientId,
		FederatedAssertion: idToken,
		Scopes:             []string{api.DefaultScope()},
		TokenVersion:       auth.TokenVersion2,
	}

	return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx, auth.ClientCredentialsAssertionType)}, nil
}

func (a oidcAuth) readTokenFile(f string) (string, error) {
	idTokenData, err := ioutil.ReadFile(f)

	if err != nil {
		return "", fmt.Errorf("reading OIDC Token %q: %v", f, err)
	}

	return string(idTokenData), nil
}

func (a oidcAuth) populateConfig(c *Config) error {
	c.AuthenticatedAsAServicePrincipal = true
	c.AuthenticatedViaOIDC = true
	c.GetAuthenticatedObjectID = buildServicePrincipalObjectIDFunc(c)
	return nil
}

func (a oidcAuth) validate() error {
	var err *multierror.Error

	fmtErrorMessage := "a %s must be configured when authenticating with OIDC"

	if a.tenantId == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "Tenant ID"))
	}

	if a.clientId == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "Client ID"))
	}

	if a.idTokenRequestUrl == "" && a.idToken == "" && a.idTokenPath == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token or ID Token Path or ID Token Request URL"))
	}

	if a.idTokenRequestToken == "" && a.idToken == "" && a.idTokenPath == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token or ID Token Path or ID Token Request Token"))
	}

	return err.ErrorOrNil()
}
<commit_msg>Use 'os.ReadFile' instead of deprecated 'ioutil.ReadFile'<commit_after>package authentication

import (
	"context"
	"fmt"
	"os"

	"github.com/Azure/go-autorest/autorest"
	"github.com/hashicorp/go-multierror"
	authWrapper "github.com/manicminer/hamilton-autorest/auth"
	"github.com/manicminer/hamilton/auth"
	"github.com/manicminer/hamilton/environments"
)

type oidcAuth struct {
	auxiliaryTenantIds  []string
	clientId            string
	environment         string
	idToken             string
	idTokenPath         string
	idTokenRequestToken string
	idTokenRequestUrl   string
	tenantId            string
}

func (a oidcAuth) build(b Builder) (authMethod, error) {
	method := oidcAuth{
		auxiliaryTenantIds:  b.AuxiliaryTenantIDs,
		clientId:            b.ClientID,
		environment:         b.Environment,
		idToken:             b.IDToken,
		idTokenPath:         b.IDTokenPath,
		idTokenRequestUrl:   b.IDTokenRequestURL,
		idTokenRequestToken: b.IDTokenRequestToken,
		tenantId:            b.TenantID,
	}
	return method, nil
}

func (a oidcAuth) isApplicable(b Builder) bool {
	return b.SupportsOIDCAuth && b.UseMicrosoftGraph && (b.IDToken != "" || b.IDTokenPath != "" || (b.IDTokenRequestURL != "" && b.IDTokenRequestToken != ""))
}

func (a oidcAuth) name() string {
	return "OIDC"
}

func (a oidcAuth) getADALToken(_ context.Context, _ autorest.Sender, _ *OAuthConfig, _ string) (autorest.Authorizer, error) {
	return nil, fmt.Errorf("ADAL tokens are not supported for OIDC authentication")
}

func (a oidcAuth) getMSALToken(ctx context.Context, api environments.Api, _ autorest.Sender, _ *OAuthConfig, _ string) (autorest.Authorizer, error) {
	environment, err := environments.EnvironmentFromString(a.environment)
	if err != nil {
		return nil, fmt.Errorf("environment config error: %v", err)
	}

	if a.idToken == "" && a.idTokenPath == "" {
		conf := auth.GitHubOIDCConfig{
			Environment:         environment,
			TenantID:            a.tenantId,
			AuxiliaryTenantIDs:  a.auxiliaryTenantIds,
			ClientID:            a.clientId,
			IDTokenRequestURL:   a.idTokenRequestUrl,
			IDTokenRequestToken: a.idTokenRequestToken,
			Scopes:              []string{api.DefaultScope()},
		}
		return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx)}, nil
	}

	idToken := a.idToken

	if a.idTokenPath != "" {
		idToken, err = a.readTokenFile(a.idTokenPath)

		if err != nil {
			return nil, fmt.Errorf("reading token file: %v", err)
		}
	}

	conf := auth.ClientCredentialsConfig{
		Environment:        environment,
		TenantID:           a.tenantId,
		AuxiliaryTenantIDs: a.auxiliaryTenantIds,
		ClientID:           a.clientId,
		FederatedAssertion: idToken,
		Scopes:             []string{api.DefaultScope()},
		TokenVersion:       auth.TokenVersion2,
	}

	return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx, auth.ClientCredentialsAssertionType)}, nil
}

func (a oidcAuth) readTokenFile(f string) (string, error) {
	idTokenData, err := os.ReadFile(f)

	if err != nil {
		return "", fmt.Errorf("reading OIDC Token %q: %v", f, err)
	}

	return string(idTokenData), nil
}

func (a oidcAuth) populateConfig(c *Config) error {
	c.AuthenticatedAsAServicePrincipal = true
	c.AuthenticatedViaOIDC = true
	c.GetAuthenticatedObjectID = buildServicePrincipalObjectIDFunc(c)
	return nil
}

func (a oidcAuth) validate() error {
	var err *multierror.Error

	fmtErrorMessage := "a %s must be configured when authenticating with OIDC"

	if a.tenantId == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "Tenant ID"))
	}

	if a.clientId == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "Client ID"))
	}

	if a.idTokenRequestUrl == "" && a.idToken == "" && a.idTokenPath == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token or ID Token Path or ID Token Request URL"))
	}

	if a.idTokenRequestToken == "" && a.idToken == "" && a.idTokenPath == "" {
		err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token or ID Token Path or ID Token Request Token"))
	}

	return err.ErrorOrNil()
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"time"

	"github.com/go-sql-driver/mysql"
)

/*
	These are more functions for querying the "users" table,
	but these functions are only used for the website
*/

/*
	Data structures
*/

type StatsSeeded struct {
	TrueSkill float32
	Sigma     float32
	NumRaces  int
	LastRace  mysql.NullTime
}

type StatsUnseeded struct {
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        mysql.NullTime
}

type StatsDiversity struct {
	TrueSkill    float64
	Sigma        float64
	Change       float64
	NumRaces     int
	LastRace     mysql.NullTime
	NewTrueSkill float64 // Only used when doing new TrueSkill calculation
}

// ProfilesRow gets each row for all profiles
type ProfilesRow struct {
	Username        string
	DatetimeCreated time.Time
	StreamURL       string
	NumAchievements int
}

// ProfileData has all data for each racer
type ProfileData struct {
	Username          string
	DatetimeCreated   time.Time
	DatetimeLastLogin time.Time
	Admin             int
	Verified          bool
	StatsSeeded       StatsSeeded
	StatsUnseeded     StatsUnseeded
	StatsDiversity    StatsDiversity
	StreamURL         string
}

/*
type LeaderboardRowMostPlayed struct {
	Name     string
	Total    int
	Verified int
}
*/

/*
	Functions
*/

/*
func (*Users) GetStatsSeeded(username string) (StatsSeeded, error) {
	var stats StatsSeeded
	if err := db.QueryRow(`
		SELECT
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.ELO,
		&stats.NumSeededRaces,
		&stats.LastSeededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}

func (*Users) GetStatsUnseeded(username string) (StatsUnseeded, error) {
	var stats StatsUnseeded
	if err := db.QueryRow(`
		SELECT
			unseeded_adjusted_average,
			unseeded_real_average,
			num_unseeded_races,
			num_forfeits,
			forfeit_penalty,
			lowest_unseeded_time,
			last_unseeded_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.UnseededAdjustedAverage,
		&stats.UnseededRealAverage,
		&stats.NumUnseededRaces,
		&stats.NumForfeits,
		&stats.ForfeitPenalty,
		&stats.LowestUnseededTime,
		&stats.LastUnseededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}
*/

// GetProfileData gets player data to populate the player's profile page
func (*Users) GetProfileData(username string) (ProfileData, error) {
	var profileData ProfileData
	var rawVerified int
	if err := db.QueryRow(`
		SELECT
			username,
			datetime_created,
			datetime_last_login,
			admin,
			verified,
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race,
			unseeded_adjusted_average,
			unseeded_real_average,
			unseeded_num_races,
			unseeded_num_forfeits,
			unseeded_forfeit_penalty,
			unseeded_lowest_time,
			unseeded_last_race,
			stream_url
		FROM
			users
		WHERE
			steam_id > 0 and
			username = ?
	`, username).Scan(
		&profileData.Username,
		&profileData.DatetimeCreated,
		&profileData.DatetimeLastLogin,
		&profileData.Admin,
		&rawVerified,
		&profileData.StatsSeeded.TrueSkill,
		&profileData.StatsSeeded.Sigma,
		&profileData.StatsSeeded.NumRaces,
		&profileData.StatsSeeded.LastRace,
		&profileData.StatsUnseeded.AdjustedAverage,
		&profileData.StatsUnseeded.RealAverage,
		&profileData.StatsUnseeded.NumRaces,
		&profileData.StatsUnseeded.NumForfeits,
		&profileData.StatsUnseeded.ForfeitPenalty,
		&profileData.StatsUnseeded.LowestTime,
		&profileData.StatsUnseeded.LastRace,
		&profileData.StreamURL,
	); err == sql.ErrNoRows {
		return profileData, nil
	} else if err != nil {
		return profileData, err
	} else {
		// Convert the int to a bool
		if rawVerified == 1 {
			profileData.Verified = true
		}
		return profileData, nil
	}
}

// GetUserProfiles gets players data to populate the profiles page
func (*Users) GetUserProfiles(currentPage int, usersPerPage int) ([]ProfilesRow, int, error) {
	usersOffset := (currentPage - 1) * usersPerPage
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.datetime_created,
			u.stream_url,
			count(ua.achievement_id)
		FROM
			users u
		LEFT JOIN
			user_achievements ua
			ON
				u.id = ua.user_id
		WHERE
			u.steam_id > 0
		GROUP BY
			u.username
		ORDER BY
			u.username ASC
		LIMIT
			?
		OFFSET
			?
	`, usersPerPage, usersOffset); err == sql.ErrNoRows {
		return nil, 0, nil
	} else if err != nil {
		return nil, 0, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the user profile results
	profiles := make([]ProfilesRow, 0)
	for rows.Next() {
		var row ProfilesRow
		if err := rows.Scan(
			&row.Username,
			&row.DatetimeCreated,
			&row.StreamURL,
			&row.NumAchievements,
		); err != nil {
			return nil, 0, err
		}

		profiles = append(profiles, row)
	}

	// Find total amount of users
	var allProfilesCount int
	if err := db.QueryRow(`
		SELECT count(id)
		FROM users
		WHERE steam_id > 0
	`).Scan(&allProfilesCount); err != nil {
		return nil, 0, err
	}

	return profiles, allProfilesCount, nil
}

// Make a leaderboard for the unseeded format based on all of the users
type LeaderboardRowUnseeded struct {
	Name            string
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        time.Time
	Verified        int
	StreamURL       string
}

func (*Users) GetLeaderboardUnseeded() ([]LeaderboardRowUnseeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			username,
			verified,
			unseeded_adjusted_average,
			unseeded_real_average,
			unseeded_num_races,
			unseeded_num_forfeits,
			unseeded_forfeit_penalty,
			unseeded_lowest_time,
			unseeded_last_race,
			stream_url
		FROM
			users
		WHERE
			unseeded_num_races >= 20
		ORDER BY
			unseeded_adjusted_average ASC
	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowUnseeded, 0)
	for rows.Next() {
		var row LeaderboardRowUnseeded
		if err := rows.Scan(
			&row.Name,
			&row.Verified,
			&row.AdjustedAverage,
			&row.RealAverage,
			&row.NumRaces,
			&row.NumForfeits,
			&row.ForfeitPenalty,
			&row.LowestTime,
			&row.LastRace,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}

// Make a leaderboard for the seeded format based on all of the users
type LeaderboardRowSeeded struct {
	Name      string
	TrueSkill float64
	NumRaces  int
	LastRace  time.Time
	Verified  int
}

func (*Users) GetLeaderboardSeeded() ([]LeaderboardRowSeeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			username,
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race,
			verified,
		FROM
			users
		WHERE
			seeded_num_races > 1
	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowSeeded, 0)
	for rows.Next() {
		var row LeaderboardRowSeeded
		if err := rows.Scan(
			&row.Name,
			&row.TrueSkill,
			&row.NumRaces,
			&row.LastRace,
			&row.Verified,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}

	return leaderboard, nil
}

type LeaderboardRowDiversity struct {
	Name                string
	DivTrueSkill        float64
	DivTrueSkillDelta   float64
	DivNumRaces         sql.NullInt64
	DivLowestTime       sql.NullInt64
	DivLastRace         time.Time
	Verified            int
	StreamURL           string
}

func (*Users) GetLeaderboardDiversity() ([]LeaderboardRowDiversity, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
		    u.username,
		    u.verified,
		    u.diversity_trueskill,
		    ROUND(u.diversity_trueskill_change, 4),
		    u.diversity_num_races,
		    (SELECT
		            MIN(run_time)
		        FROM
		            race_participants
				LEFT JOIN races
					ON race_participants.race_id = races.id
		        WHERE
		            place > 0
		            AND u.id = user_id
		            AND races.format = 'diversity') as r_time,
		    u.diversity_last_race,
		    u.stream_url
		FROM
		    users u
		        LEFT JOIN
		    race_participants rp ON rp.user_id = u.id
		        LEFT JOIN
		    races r ON r.id = rp.race_id
		WHERE
		    diversity_num_races >= 5
		        AND r.format = 'diversity'
		        AND rp.place > 0
		GROUP BY u.username
		ORDER BY u.diversity_trueskill DESC

	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowDiversity, 0)
	for rows.Next() {
		var row LeaderboardRowDiversity
		if err := rows.Scan(
			&row.Name,
			&row.Verified,
			&row.DivTrueSkill,
			&row.DivTrueSkillDelta,
			&row.DivNumRaces,
			&row.DivLowestTime,
			&row.DivLastRace,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}
<commit_msg>TrueSkill Change Rounding<commit_after>package models

import (
	"database/sql"
	"time"

	"github.com/go-sql-driver/mysql"
)

/*
	These are more functions for querying the "users" table,
	but these functions are only used for the website
*/

/*
	Data structures
*/

type StatsSeeded struct {
	TrueSkill float32
	Sigma     float32
	NumRaces  int
	LastRace  mysql.NullTime
}

type StatsUnseeded struct {
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        mysql.NullTime
}

type StatsDiversity struct {
	TrueSkill    float64
	Sigma        float64
	Change       float64
	NumRaces     int
	LastRace     mysql.NullTime
	NewTrueSkill float64 // Only used when doing new TrueSkill calculation
}

// ProfilesRow gets each row for all profiles
type ProfilesRow struct {
	Username        string
	DatetimeCreated time.Time
	StreamURL       string
	NumAchievements int
}

// ProfileData has all data for each racer
type ProfileData struct {
	Username          string
	DatetimeCreated   time.Time
	DatetimeLastLogin time.Time
	Admin             int
	Verified          bool
	StatsSeeded       StatsSeeded
	StatsUnseeded     StatsUnseeded
	StatsDiversity    StatsDiversity
	StreamURL         string
}

/*
type LeaderboardRowMostPlayed struct {
	Name     string
	Total    int
	Verified int
}
*/

/*
	Functions
*/

/*
func (*Users) GetStatsSeeded(username string) (StatsSeeded, error) {
	var stats StatsSeeded
	if err := db.QueryRow(`
		SELECT
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.ELO,
		&stats.NumSeededRaces,
		&stats.LastSeededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}

func (*Users) GetStatsUnseeded(username string) (StatsUnseeded, error) {
	var stats StatsUnseeded
	if err := db.QueryRow(`
		SELECT
			unseeded_adjusted_average,
			unseeded_real_average,
			num_unseeded_races,
			num_forfeits,
			forfeit_penalty,
			lowest_unseeded_time,
			last_unseeded_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.UnseededAdjustedAverage,
		&stats.UnseededRealAverage,
		&stats.NumUnseededRaces,
		&stats.NumForfeits,
		&stats.ForfeitPenalty,
		&stats.LowestUnseededTime,
		&stats.LastUnseededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}
*/

// GetProfileData gets player data to populate the player's profile page
func (*Users) GetProfileData(username string) (ProfileData, error) {
	var profileData ProfileData
	var rawVerified int
	if err := db.QueryRow(`
		SELECT
			username,
			datetime_created,
			datetime_last_login,
			admin,
			verified,
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race,
			unseeded_adjusted_average,
			unseeded_real_average,
			unseeded_num_races,
			unseeded_num_forfeits,
			unseeded_forfeit_penalty,
			unseeded_lowest_time,
			unseeded_last_race,
			stream_url
		FROM
			users
		WHERE
			steam_id > 0 and
			username = ?
	`, username).Scan(
		&profileData.Username,
		&profileData.DatetimeCreated,
		&profileData.DatetimeLastLogin,
		&profileData.Admin,
		&rawVerified,
		&profileData.StatsSeeded.TrueSkill,
		&profileData.StatsSeeded.Sigma,
		&profileData.StatsSeeded.NumRaces,
		&profileData.StatsSeeded.LastRace,
		&profileData.StatsUnseeded.AdjustedAverage,
		&profileData.StatsUnseeded.RealAverage,
		&profileData.StatsUnseeded.NumRaces,
		&profileData.StatsUnseeded.NumForfeits,
		&profileData.StatsUnseeded.ForfeitPenalty,
		&profileData.StatsUnseeded.LowestTime,
		&profileData.StatsUnseeded.LastRace,
		&profileData.StreamURL,
	); err == sql.ErrNoRows {
		return profileData, nil
	} else if err != nil {
		return profileData, err
	} else {
		// Convert the int to a bool
		if rawVerified == 1 {
			profileData.Verified = true
		}
		return profileData, nil
	}
}

// GetUserProfiles gets players data to populate the profiles page
func (*Users) GetUserProfiles(currentPage int, usersPerPage int) ([]ProfilesRow, int, error) {
	usersOffset := (currentPage - 1) * usersPerPage
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.datetime_created,
			u.stream_url,
			count(ua.achievement_id)
		FROM
			users u
		LEFT JOIN
			user_achievements ua
			ON
				u.id = ua.user_id
		WHERE
			u.steam_id > 0
		GROUP BY
			u.username
		ORDER BY
			u.username ASC
		LIMIT
			?
		OFFSET
			?
	`, usersPerPage, usersOffset); err == sql.ErrNoRows {
		return nil, 0, nil
	} else if err != nil {
		return nil, 0, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the user profile results
	profiles := make([]ProfilesRow, 0)
	for rows.Next() {
		var row ProfilesRow
		if err := rows.Scan(
			&row.Username,
			&row.DatetimeCreated,
			&row.StreamURL,
			&row.NumAchievements,
		); err != nil {
			return nil, 0, err
		}

		profiles = append(profiles, row)
	}

	// Find total amount of users
	var allProfilesCount int
	if err := db.QueryRow(`
		SELECT count(id)
		FROM users
		WHERE steam_id > 0
	`).Scan(&allProfilesCount); err != nil {
		return nil, 0, err
	}

	return profiles, allProfilesCount, nil
}

// Make a leaderboard for the unseeded format based on all of the users
type LeaderboardRowUnseeded struct {
	Name            string
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        time.Time
	Verified        int
	StreamURL       string
}

func (*Users) GetLeaderboardUnseeded() ([]LeaderboardRowUnseeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			username,
			verified,
			unseeded_adjusted_average,
			unseeded_real_average,
			unseeded_num_races,
			unseeded_num_forfeits,
			unseeded_forfeit_penalty,
			unseeded_lowest_time,
			unseeded_last_race,
			stream_url
		FROM
			users
		WHERE
			unseeded_num_races >= 20
		ORDER BY
			unseeded_adjusted_average ASC
	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowUnseeded, 0)
	for rows.Next() {
		var row LeaderboardRowUnseeded
		if err := rows.Scan(
			&row.Name,
			&row.Verified,
			&row.AdjustedAverage,
			&row.RealAverage,
			&row.NumRaces,
			&row.NumForfeits,
			&row.ForfeitPenalty,
			&row.LowestTime,
			&row.LastRace,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}

// Make a leaderboard for the seeded format based on all of the users
type LeaderboardRowSeeded struct {
	Name      string
	TrueSkill float64
	NumRaces  int
	LastRace  time.Time
	Verified  int
}

func (*Users) GetLeaderboardSeeded() ([]LeaderboardRowSeeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			username,
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race,
			verified,
		FROM
			users
		WHERE
			seeded_num_races > 1
	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowSeeded, 0)
	for rows.Next() {
		var row LeaderboardRowSeeded
		if err := rows.Scan(
			&row.Name,
			&row.TrueSkill,
			&row.NumRaces,
			&row.LastRace,
			&row.Verified,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}

	return leaderboard, nil
}

type LeaderboardRowDiversity struct {
	Name                string
	DivTrueSkill        float64
	DivTrueSkillDelta   float64
	DivNumRaces         sql.NullInt64
	DivLowestTime       sql.NullInt64
	DivLastRace         time.Time
	Verified            int
	StreamURL           string
}

func (*Users) GetLeaderboardDiversity() ([]LeaderboardRowDiversity, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
		    u.username,
		    u.verified,
		    u.diversity_trueskill,
		    ROUND(u.diversity_trueskill_change, 2),
		    u.diversity_num_races,
		    (SELECT
		            MIN(run_time)
		        FROM
		            race_participants
				LEFT JOIN races
					ON race_participants.race_id = races.id
		        WHERE
		            place > 0
		            AND u.id = user_id
		            AND races.format = 'diversity') as r_time,
		    u.diversity_last_race,
		    u.stream_url
		FROM
		    users u
		        LEFT JOIN
		    race_participants rp ON rp.user_id = u.id
		        LEFT JOIN
		    races r ON r.id = rp.race_id
		WHERE
		    diversity_num_races >= 5
		        AND r.format = 'diversity'
		        AND rp.place > 0
		GROUP BY u.username
		ORDER BY u.diversity_trueskill DESC

	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowDiversity, 0)
	for rows.Next() {
		var row LeaderboardRowDiversity
		if err := rows.Scan(
			&row.Name,
			&row.Verified,
			&row.DivTrueSkill,
			&row.DivTrueSkillDelta,
			&row.DivNumRaces,
			&row.DivLowestTime,
			&row.DivLastRace,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}
<|endoftext|>


<commit_before>package handler

import (
	"net"

	"github.com/cenkalti/rain/internal/bitfield"
	"github.com/cenkalti/rain/internal/btconn"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/peermanager/peerids"
)

type Handler struct {
	conn        net.Conn
	peerIDs     *peerids.PeerIDs
	bitfield    *bitfield.Bitfield
	peerID      [20]byte
	sKeyHash    [20]byte
	infoHash    [20]byte
	newPeers    chan *peer.Peer
	disconnectC chan net.Conn
	log         logger.Logger
}

func New(conn net.Conn, peerIDs *peerids.PeerIDs, peerID, sKeyHash, infoHash [20]byte, newPeers chan *peer.Peer, disconnectC chan net.Conn, l logger.Logger) *Handler {
	return &Handler{
		conn:        conn,
		peerIDs:     peerIDs,
		peerID:      peerID,
		sKeyHash:    sKeyHash,
		infoHash:    infoHash,
		newPeers:    newPeers,
		disconnectC: disconnectC,
		log:         l,
	}
}

func (h *Handler) Run(stopC chan struct{}) {
	log := logger.New("peer <- " + h.conn.RemoteAddr().String())

	defer func() {
		_ = h.conn.Close()
		select {
		case h.disconnectC <- h.conn:
		case <-stopC:
		}
	}()

	// TODO get this from config
	encryptionForceIncoming := false

	ourExtensions := [8]byte{}
	ourbf := bitfield.NewBytes(ourExtensions[:], 64)
	ourbf.Set(61) // Fast Extension

	// TODO close conn during handshake when stopC is closed
	encConn, cipher, peerExtensions, peerID, _, err := btconn.Accept(
		h.conn, h.getSKey, encryptionForceIncoming, h.checkInfoHash, ourExtensions, h.peerID)
	if err != nil {
		log.Error(err)
		_ = h.conn.Close()
		return
	}
	log.Infof("Connection accepted. (cipher=%s extensions=%x client=%q)", cipher, peerExtensions, peerID[:8])

	ok := h.peerIDs.Add(peerID)
	if !ok {
		return
	}
	defer h.peerIDs.Remove(peerID)

	peerbf := bitfield.NewBytes(peerExtensions[:], 64)
	extensions := ourbf.And(peerbf)

	p := peer.New(encConn, peerID, extensions, log)
	select {
	case h.newPeers <- p:
		p.Run(stopC)
	case <-stopC:
	}
}

func (h *Handler) getSKey(sKeyHash [20]byte) []byte {
	if sKeyHash == h.sKeyHash {
		return h.infoHash[:]
	}
	return nil
}

func (h *Handler) checkInfoHash(infoHash [20]byte) bool {
	return infoHash == h.infoHash
}
<commit_msg>remove done todo<commit_after>package handler

import (
	"net"

	"github.com/cenkalti/rain/internal/bitfield"
	"github.com/cenkalti/rain/internal/btconn"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/peermanager/peerids"
)

type Handler struct {
	conn        net.Conn
	peerIDs     *peerids.PeerIDs
	bitfield    *bitfield.Bitfield
	peerID      [20]byte
	sKeyHash    [20]byte
	infoHash    [20]byte
	newPeers    chan *peer.Peer
	disconnectC chan net.Conn
	log         logger.Logger
}

func New(conn net.Conn, peerIDs *peerids.PeerIDs, peerID, sKeyHash, infoHash [20]byte, newPeers chan *peer.Peer, disconnectC chan net.Conn, l logger.Logger) *Handler {
	return &Handler{
		conn:        conn,
		peerIDs:     peerIDs,
		peerID:      peerID,
		sKeyHash:    sKeyHash,
		infoHash:    infoHash,
		newPeers:    newPeers,
		disconnectC: disconnectC,
		log:         l,
	}
}

func (h *Handler) Run(stopC chan struct{}) {
	log := logger.New("peer <- " + h.conn.RemoteAddr().String())

	defer func() {
		_ = h.conn.Close()
		select {
		case h.disconnectC <- h.conn:
		case <-stopC:
		}
	}()

	// TODO get this from config
	encryptionForceIncoming := false

	ourExtensions := [8]byte{}
	ourbf := bitfield.NewBytes(ourExtensions[:], 64)
	ourbf.Set(61) // Fast Extension

	encConn, cipher, peerExtensions, peerID, _, err := btconn.Accept(
		h.conn, h.getSKey, encryptionForceIncoming, h.checkInfoHash, ourExtensions, h.peerID)
	if err != nil {
		log.Error(err)
		_ = h.conn.Close()
		return
	}
	log.Infof("Connection accepted. (cipher=%s extensions=%x client=%q)", cipher, peerExtensions, peerID[:8])

	ok := h.peerIDs.Add(peerID)
	if !ok {
		return
	}
	defer h.peerIDs.Remove(peerID)

	peerbf := bitfield.NewBytes(peerExtensions[:], 64)
	extensions := ourbf.And(peerbf)

	p := peer.New(encConn, peerID, extensions, log)
	select {
	case h.newPeers <- p:
		p.Run(stopC)
	case <-stopC:
	}
}

func (h *Handler) getSKey(sKeyHash [20]byte) []byte {
	if sKeyHash == h.sKeyHash {
		return h.infoHash[:]
	}
	return nil
}

func (h *Handler) checkInfoHash(infoHash [20]byte) bool {
	return infoHash == h.infoHash
}
<|endoftext|>


<commit_before>package spec_iterator

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/onsi/ginkgo/internal/spec"
)

type ParallelIterator struct {
	specs  []*spec.Spec
	host   string
	client *http.Client
}

func NewParallelIterator(specs []*spec.Spec, host string) *ParallelIterator {
	return &ParallelIterator{
		specs:  specs,
		host:   host,
		client: &http.Client{},
	}
}

func (s *ParallelIterator) Next() (*spec.Spec, error) {
	resp, err := s.client.Get(s.host + "/counter")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(fmt.Sprintf("unexpected status code %d", resp.StatusCode))
	}

	var counter Counter
	err = json.NewDecoder(resp.Body).Decode(&counter)
	if err != nil {
		return nil, err
	}

	if counter.Index >= len(s.specs) {
		return nil, ErrClosed
	}

	return s.specs[counter.Index], nil
}

func (s *ParallelIterator) NumberOfSpecsPriorToIteration() int {
	return len(s.specs)
}

func (s *ParallelIterator) NumberOfSpecsToProcessIfKnown() (int, bool) {
	return -1, false
}

func (s *ParallelIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) {
	return -1, false
}
<commit_msg>Use fmt.Errorf instead of errors.New(fmt.Sprintf<commit_after>package spec_iterator

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/onsi/ginkgo/internal/spec"
)

type ParallelIterator struct {
	specs  []*spec.Spec
	host   string
	client *http.Client
}

func NewParallelIterator(specs []*spec.Spec, host string) *ParallelIterator {
	return &ParallelIterator{
		specs:  specs,
		host:   host,
		client: &http.Client{},
	}
}

func (s *ParallelIterator) Next() (*spec.Spec, error) {
	resp, err := s.client.Get(s.host + "/counter")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
	}

	var counter Counter
	err = json.NewDecoder(resp.Body).Decode(&counter)
	if err != nil {
		return nil, err
	}

	if counter.Index >= len(s.specs) {
		return nil, ErrClosed
	}

	return s.specs[counter.Index], nil
}

func (s *ParallelIterator) NumberOfSpecsPriorToIteration() int {
	return len(s.specs)
}

func (s *ParallelIterator) NumberOfSpecsToProcessIfKnown() (int, bool) {
	return -1, false
}

func (s *ParallelIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) {
	return -1, false
}
<|endoftext|>