<commit_before>package lib

import (
	"bytes"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestApplyBranch(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.SwitchToBranch("feature"))
	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyBranch(".tmp/repo", "template.tmpl", "feature", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyCommit(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyHead(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.SwitchToBranch("feature"))
	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyHead(".tmp/repo", "template.tmpl", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyLocal(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyLocal(".tmp/repo", "template.tmpl", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestIncorrectTemplatePath(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	output := new(bytes.Buffer)
	err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "foo/template.tmpl", output)

	assert.EqualError(t, err, "mbt: the path 'foo' does not exist in the given tree")
	assert.Equal(t, "", output.String())
}

func TestBadTemplate(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
`))
	check(t, repo.Commit("first"))

	output := new(bytes.Buffer)
	err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output)

	assert.EqualError(t, err, "mbt: template: template:2: unexpected EOF")
	assert.Equal(t, "", output.String())
}

func TestEnvironmentVariables(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", "{{.Env.EXTERNAL_VALUE}}"))
	check(t, repo.Commit("first"))

	os.Setenv("EXTERNAL_VALUE", "FOO")

	output := new(bytes.Buffer)
	check(t, ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output))

	assert.Equal(t, "FOO", output.String())
}

func TestCustomTemplateFuncs(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModuleWithOptions("app-a", &Spec{
		Name: "app-a",
		Properties: map[string]interface{}{
			"tags":    []string{"a", "b", "c"},
			"numbers": []int{1, 2, 3},
			"nested": map[string]interface{}{
				"tags": []string{"a", "b", "c"},
			},
			"foo": "bar",
		},
	}))

	check(t, repo.WriteContent("template.tmpl", `
{{- if contains (property (module "app-a") "tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-b") "tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "dags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "tags") "d"}}foo{{- end}}
{{- if contains (property (module "app-a") "numbers") "1"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested.tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested.bags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "tags.tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested.") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested") "a"}}foo{{- end}}
{{- propertyOr (module "app-a") "foo" "car"}}
{{- propertyOr (module "app-a") "foo.bar" "foo"}}
`))
	check(t, repo.Commit("first"))

	output := new(bytes.Buffer)
	err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output)
	check(t, err)

	assert.Equal(t, "foofoobarfoo\n", output.String())
}
<commit_msg>Tests: Improved coverage<commit_after>package lib

import (
	"bytes"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestApplyBranch(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.SwitchToBranch("feature"))
	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyBranch(".tmp/repo", "template.tmpl", "feature", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyCommit(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyHead(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.SwitchToBranch("feature"))
	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyHead(".tmp/repo", "template.tmpl", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyLocal(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	check(t, repo.InitModule("app-b"))
	check(t, repo.Commit("second"))

	output := new(bytes.Buffer)
	check(t, ApplyLocal(".tmp/repo", "template.tmpl", output))

	assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestIncorrectTemplatePath(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
	check(t, repo.Commit("first"))

	output := new(bytes.Buffer)
	err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "foo/template.tmpl", output)

	assert.EqualError(t, err, "mbt: the path 'foo' does not exist in the given tree")
	assert.Equal(t, "", output.String())
}

func TestBadTemplate(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
`))
	check(t, repo.Commit("first"))

	output := new(bytes.Buffer)
	err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output)

	assert.EqualError(t, err, "mbt: template: template:2: unexpected EOF")
	assert.Equal(t, "", output.String())
}

func TestEnvironmentVariables(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("template.tmpl", "{{.Env.EXTERNAL_VALUE}}"))
	check(t, repo.Commit("first"))

	os.Setenv("EXTERNAL_VALUE", "FOO")

	output := new(bytes.Buffer)
	check(t, ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output))

	assert.Equal(t, "FOO", output.String())
}

func TestCustomTemplateFuncs(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModuleWithOptions("app-a", &Spec{
		Name: "app-a",
		Properties: map[string]interface{}{
			"tags":    []string{"a", "b", "c"},
			"numbers": []int{1, 2, 3},
			"nested": map[string]interface{}{
				"tags": []string{"a", "b", "c"},
			},
			"foo": "bar",
		},
	}))

	check(t, repo.WriteContent("template.tmpl", `
{{- if contains (property (module "app-a") "tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-b") "tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "dags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "tags") "d"}}foo{{- end}}
{{- if contains (property (module "app-a") "numbers") "1"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested.tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested.bags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "tags.tags") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested.") "a"}}foo{{- end}}
{{- if contains (property (module "app-a") "nested") "a"}}foo{{- end}}
{{- propertyOr (module "app-a") "foo" "car"}}
{{- propertyOr (module "app-a") "foo.bar" "foo"}}
{{- propertyOr (module "app-b") "foo" "foo"}}
`))
	check(t, repo.Commit("first"))

	output := new(bytes.Buffer)
	err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output)
	check(t, err)

	assert.Equal(t, "foofoobarfoofoo\n", output.String())
}
<|endoftext|>


<commit_before>package jwt

import (
	"crypto/subtle"
	"time"
)

// Leeway is the amount of clock skew that is allowed for the verification claims around time
var Leeway = time.Duration(30) * time.Second

// Claims represents what the interface that a object but implement to be a valid set of claims about a JWT
type Claims interface {
	Valid() error
}

// StandardClaims is a structured version of the Claims Section, as referenced at
// https://tools.ietf.org/html/rfc7519#section-4.1
type StandardClaims struct {
	Audience  string `json:"aud,omitempty"`
	ExpiresAt int64  `json:"exp,omitempty"`
	Id        string `json:"jti,omitempty"`
	IssuedAt  int64  `json:"iat,omitempty"`
	Issuer    string `json:"iss,omitempty"`
	NotBefore int64  `json:"nbf,omitempty"`
	Subject   string `json:"sub,omitempty"`
}

// Valid checks the time based claims "exp, iat, nbf".
// It uses the exposed variable Leeway to account for clock-skew, by default 30 seconds of skew is allowed.
// As well, if any of the above claims are missing from the token, it will still be considered a valid claim.
func (c StandardClaims) Valid() error {
	vErr := new(ValidationError)
	now := TimeFunc().Unix()

	// The claims below are optional, by default, so if they are set to the
	// default value in Go, let's not fail the verification for them.
	if c.VerifyExpiresAt(now, false) == false {
		vErr.err = "Token is expired"
		vErr.Errors |= ValidationErrorExpired
	}

	if c.VerifyIssuedAt(now, false) == false {
		vErr.err = "Token used before issued, clock skew issue?"
		vErr.Errors |= ValidationErrorIssuedAt
	}

	if c.VerifyNotBefore(now, false) == false {
		vErr.err = "Token is not valid yet"
		vErr.Errors |= ValidationErrorNotValidYet
	}

	if vErr.valid() {
		return nil
	}

	return vErr
}

// Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
	return verifyAud(c.Audience, cmp, req)
}

// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
	return verifyExp(c.ExpiresAt, cmp, req)
}

// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
	return verifyIat(c.IssuedAt, cmp, req)
}

// Compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
	return verifyIss(c.Issuer, cmp, req)
}

// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
	return verifyNbf(c.NotBefore, cmp, req)
}

type MapClaim map[string]interface{}

// Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyAudience(cmp string, req bool) bool {
	aud, _ := m["aud"].(string)
	return verifyAud(aud, cmp, req)
}

// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyExpiresAt(cmp int64, req bool) bool {
	exp, _ := m["exp"].(float64)
	return verifyExp(int64(exp), cmp, req)
}

// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyIssuedAt(cmp int64, req bool) bool {
	iat, _ := m["iat"].(float64)
	return verifyIat(int64(iat), cmp, req)
}

// Compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyIssuer(cmp string, req bool) bool {
	iss, _ := m["iss"].(string)
	return verifyIss(iss, cmp, req)
}

// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyNotBefore(cmp int64, req bool) bool {
	nbf, _ := m["nbf"].(float64)
	return verifyNbf(int64(nbf), cmp, req)
}

// Validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (m MapClaim) Valid() error {
	vErr := new(ValidationError)
	now := TimeFunc().Unix()

	if m.VerifyExpiresAt(now, false) == false {
		vErr.err = "Token is expired"
		vErr.Errors |= ValidationErrorExpired
	}

	if m.VerifyIssuedAt(now, false) == false {
		vErr.err = "Token used before issued, clock skew issue?"
		vErr.Errors |= ValidationErrorIssuedAt
	}

	if m.VerifyNotBefore(now, false) == false {
		vErr.err = "Token is not valid yet"
		vErr.Errors |= ValidationErrorNotValidYet
	}

	if vErr.valid() {
		return nil
	}

	return vErr
}

func verifyAud(aud string, cmp string, required bool) bool {
	if aud == "" {
		return !required
	}
	if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 {
		return true
	} else {
		return false
	}
}

func verifyExp(exp int64, now int64, required bool) bool {
	if exp == 0 {
		return !required
	}
	return now-int64(Leeway.Seconds()) <= exp
}

func verifyIat(iat int64, now int64, required bool) bool {
	if iat == 0 {
		return !required
	}
	return now+int64(Leeway.Seconds()) >= iat
}

func verifyIss(iss string, cmp string, required bool) bool {
	if iss == "" {
		return !required
	}
	if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
		return true
	} else {
		return false
	}
}

func verifyNbf(nbf int64, now int64, required bool) bool {
	if nbf == 0 {
		return !required
	}
	return now+int64(Leeway.Seconds()) >= nbf
}
<commit_msg>Out-dent return statement.<commit_after>package jwt

import (
	"crypto/subtle"
	"time"
)

// Leeway is the amount of clock skew that is allowed for the verification claims around time
var Leeway = time.Duration(30) * time.Second

// Claims represents what the interface that a object but implement to be a valid set of claims about a JWT
type Claims interface {
	Valid() error
}

// StandardClaims is a structured version of the Claims Section, as referenced at
// https://tools.ietf.org/html/rfc7519#section-4.1
type StandardClaims struct {
	Audience  string `json:"aud,omitempty"`
	ExpiresAt int64  `json:"exp,omitempty"`
	Id        string `json:"jti,omitempty"`
	IssuedAt  int64  `json:"iat,omitempty"`
	Issuer    string `json:"iss,omitempty"`
	NotBefore int64  `json:"nbf,omitempty"`
	Subject   string `json:"sub,omitempty"`
}

// Valid checks the time based claims "exp, iat, nbf".
// It uses the exposed variable Leeway to account for clock-skew, by default 30 seconds of skew is allowed.
// As well, if any of the above claims are missing from the token, it will still be considered a valid claim.
func (c StandardClaims) Valid() error {
	vErr := new(ValidationError)
	now := TimeFunc().Unix()

	// The claims below are optional, by default, so if they are set to the
	// default value in Go, let's not fail the verification for them.
	if c.VerifyExpiresAt(now, false) == false {
		vErr.err = "Token is expired"
		vErr.Errors |= ValidationErrorExpired
	}

	if c.VerifyIssuedAt(now, false) == false {
		vErr.err = "Token used before issued, clock skew issue?"
		vErr.Errors |= ValidationErrorIssuedAt
	}

	if c.VerifyNotBefore(now, false) == false {
		vErr.err = "Token is not valid yet"
		vErr.Errors |= ValidationErrorNotValidYet
	}

	if vErr.valid() {
		return nil
	}

	return vErr
}

// Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
	return verifyAud(c.Audience, cmp, req)
}

// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
	return verifyExp(c.ExpiresAt, cmp, req)
}

// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
	return verifyIat(c.IssuedAt, cmp, req)
}

// Compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
	return verifyIss(c.Issuer, cmp, req)
}

// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
	return verifyNbf(c.NotBefore, cmp, req)
}

type MapClaim map[string]interface{}

// Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyAudience(cmp string, req bool) bool {
	aud, _ := m["aud"].(string)
	return verifyAud(aud, cmp, req)
}

// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyExpiresAt(cmp int64, req bool) bool {
	exp, _ := m["exp"].(float64)
	return verifyExp(int64(exp), cmp, req)
}

// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyIssuedAt(cmp int64, req bool) bool {
	iat, _ := m["iat"].(float64)
	return verifyIat(int64(iat), cmp, req)
}

// Compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyIssuer(cmp string, req bool) bool {
	iss, _ := m["iss"].(string)
	return verifyIss(iss, cmp, req)
}

// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaim) VerifyNotBefore(cmp int64, req bool) bool {
	nbf, _ := m["nbf"].(float64)
	return verifyNbf(int64(nbf), cmp, req)
}

// Validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (m MapClaim) Valid() error {
	vErr := new(ValidationError)
	now := TimeFunc().Unix()

	if m.VerifyExpiresAt(now, false) == false {
		vErr.err = "Token is expired"
		vErr.Errors |= ValidationErrorExpired
	}

	if m.VerifyIssuedAt(now, false) == false {
		vErr.err = "Token used before issued, clock skew issue?"
		vErr.Errors |= ValidationErrorIssuedAt
	}

	if m.VerifyNotBefore(now, false) == false {
		vErr.err = "Token is not valid yet"
		vErr.Errors |= ValidationErrorNotValidYet
	}

	if vErr.valid() {
		return nil
	}

	return vErr
}

func verifyAud(aud string, cmp string, required bool) bool {
	if aud == "" {
		return !required
	}
	if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 {
		return true
	} else {
		return false
	}
}

func verifyExp(exp int64, now int64, required bool) bool {
	if exp == 0 {
		return !required
	}
	return now-int64(Leeway.Seconds()) <= exp
}

func verifyIat(iat int64, now int64, required bool) bool {
	if iat == 0 {
		return !required
	}
	return now+int64(Leeway.Seconds()) >= iat
}

func verifyIss(iss string, cmp string, required bool) bool {
	if iss == "" {
		return !required
	}
	if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
		return true
	}

	return false
}

func verifyNbf(nbf int64, now int64, required bool) bool {
	if nbf == 0 {
		return !required
	}
	return now+int64(Leeway.Seconds()) >= nbf
}
<|endoftext|>


<commit_before>// Copyright 2017 The etcd Authors
//
// 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 auth

import (
	"crypto/rsa"
	"io/ioutil"

	jwt "github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"
)

type tokenJWT struct {
	signMethod string
	signKey    *rsa.PrivateKey
	verifyKey  *rsa.PublicKey
}

func (t *tokenJWT) enable()                         {}
func (t *tokenJWT) disable()                        {}
func (t *tokenJWT) invalidateUser(string)           {}
func (t *tokenJWT) genTokenPrefix() (string, error) { return "", nil }

func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInfo, bool) {
	// rev isn't used in JWT, it is only used in simple token
	var (
		username string
		revision uint64
	)

	parsed, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return t.verifyKey, nil
	})

	switch err.(type) {
	case nil:
		if !parsed.Valid {
			plog.Warningf("invalid jwt token: %s", token)
			return nil, false
		}

		claims := parsed.Claims.(jwt.MapClaims)

		username = claims["username"].(string)
		revision = uint64(claims["revision"].(float64))
	default:
		plog.Warningf("failed to parse jwt token: %s", err)
		return nil, false
	}

	return &AuthInfo{Username: username, Revision: revision}, true
}

func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64) (string, error) {
	// Future work: let a jwt token include permission information would be useful for
	// permission checking in proxy side.
	tk := jwt.NewWithClaims(jwt.GetSigningMethod(t.signMethod),
		jwt.MapClaims{
			"username": username,
			"revision": revision,
		})

	token, err := tk.SignedString(t.signKey)
	if err != nil {
		plog.Debugf("failed to sign jwt token: %s", err)
		return "", err
	}

	plog.Debugf("jwt token: %s", token)

	return token, err
}

func prepareOpts(opts map[string]string) (jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath string, err error) {
	for k, v := range opts {
		switch k {
		case "sign-method":
			jwtSignMethod = v
		case "pub-key":
			jwtPubKeyPath = v
		case "priv-key":
			jwtPrivKeyPath = v
		default:
			plog.Errorf("unknown token specific option: %s", k)
			return "", "", "", ErrInvalidAuthOpts
		}
	}

	return jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, nil
}

func newTokenProviderJWT(opts map[string]string) (*tokenJWT, error) {
	jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, err := prepareOpts(opts)
	if err != nil {
		return nil, ErrInvalidAuthOpts
	}

	t := &tokenJWT{}

	t.signMethod = jwtSignMethod

	verifyBytes, err := ioutil.ReadFile(jwtPubKeyPath)
	if err != nil {
		plog.Errorf("failed to read public key (%s) for jwt: %s", jwtPubKeyPath, err)
		return nil, err
	}
	t.verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
	if err != nil {
		plog.Errorf("failed to parse public key (%s): %s", jwtPubKeyPath, err)
		return nil, err
	}

	signBytes, err := ioutil.ReadFile(jwtPrivKeyPath)
	if err != nil {
		plog.Errorf("failed to read private key (%s) for jwt: %s", jwtPrivKeyPath, err)
		return nil, err
	}
	t.signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
	if err != nil {
		plog.Errorf("failed to parse private key (%s): %s", jwtPrivKeyPath, err)
		return nil, err
	}

	return t, nil
}
<commit_msg>auth: reject empty signing method for JWT token provider<commit_after>// Copyright 2017 The etcd Authors
//
// 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 auth

import (
	"crypto/rsa"
	"io/ioutil"

	jwt "github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"
)

type tokenJWT struct {
	signMethod string
	signKey    *rsa.PrivateKey
	verifyKey  *rsa.PublicKey
}

func (t *tokenJWT) enable()                         {}
func (t *tokenJWT) disable()                        {}
func (t *tokenJWT) invalidateUser(string)           {}
func (t *tokenJWT) genTokenPrefix() (string, error) { return "", nil }

func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInfo, bool) {
	// rev isn't used in JWT, it is only used in simple token
	var (
		username string
		revision uint64
	)

	parsed, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return t.verifyKey, nil
	})

	switch err.(type) {
	case nil:
		if !parsed.Valid {
			plog.Warningf("invalid jwt token: %s", token)
			return nil, false
		}

		claims := parsed.Claims.(jwt.MapClaims)

		username = claims["username"].(string)
		revision = uint64(claims["revision"].(float64))
	default:
		plog.Warningf("failed to parse jwt token: %s", err)
		return nil, false
	}

	return &AuthInfo{Username: username, Revision: revision}, true
}

func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64) (string, error) {
	// Future work: let a jwt token include permission information would be useful for
	// permission checking in proxy side.
	tk := jwt.NewWithClaims(jwt.GetSigningMethod(t.signMethod),
		jwt.MapClaims{
			"username": username,
			"revision": revision,
		})

	token, err := tk.SignedString(t.signKey)
	if err != nil {
		plog.Debugf("failed to sign jwt token: %s", err)
		return "", err
	}

	plog.Debugf("jwt token: %s", token)

	return token, err
}

func prepareOpts(opts map[string]string) (jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath string, err error) {
	for k, v := range opts {
		switch k {
		case "sign-method":
			jwtSignMethod = v
		case "pub-key":
			jwtPubKeyPath = v
		case "priv-key":
			jwtPrivKeyPath = v
		default:
			plog.Errorf("unknown token specific option: %s", k)
			return "", "", "", ErrInvalidAuthOpts
		}
	}
	if len(jwtSignMethod) == 0 {
		return "", "", "", ErrInvalidAuthOpts
	}
	return jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, nil
}

func newTokenProviderJWT(opts map[string]string) (*tokenJWT, error) {
	jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, err := prepareOpts(opts)
	if err != nil {
		return nil, ErrInvalidAuthOpts
	}

	t := &tokenJWT{}

	t.signMethod = jwtSignMethod

	verifyBytes, err := ioutil.ReadFile(jwtPubKeyPath)
	if err != nil {
		plog.Errorf("failed to read public key (%s) for jwt: %s", jwtPubKeyPath, err)
		return nil, err
	}
	t.verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
	if err != nil {
		plog.Errorf("failed to parse public key (%s): %s", jwtPubKeyPath, err)
		return nil, err
	}

	signBytes, err := ioutil.ReadFile(jwtPrivKeyPath)
	if err != nil {
		plog.Errorf("failed to read private key (%s) for jwt: %s", jwtPrivKeyPath, err)
		return nil, err
	}
	t.signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
	if err != nil {
		plog.Errorf("failed to parse private key (%s): %s", jwtPrivKeyPath, err)
		return nil, err
	}

	return t, nil
}
<|endoftext|>


<commit_before>package clever

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

const debug = false

// API supports basic auth with an API key or bearer auth with a token
type Auth struct {
	APIKey, Token string
}

type Clever struct {
	Auth
	Url string
}

// Creates a new clever object to make requests with. URL must be a valid base url, e.g. "https://api.clever.com"
func New(auth Auth, url string) *Clever {
	return &Clever{auth, url}
}

type CleverError struct {
	Code    string
	Message string `json:"error"`
}

func (err *CleverError) Error() string {
	if err.Code == "" {
		return err.Message
	}
	return fmt.Sprintf("%s (%s)", err.Error, err.Code)
}

type Paging struct {
	Count   int
	Current int
	Total   int
}

type Link struct {
	Rel string
	Uri string
}

type DistrictResp struct {
	District District `json:"data"`
	Links    []Link
	Uri      string
}

type District struct {
	Id   string
	Name string
}

type SchoolResp struct {
	Links  []Link
	School School `json:"data"`
	Uri    string
}

type School struct {
	Created      string
	District     string
	HighGrade    string `json:"high_grade"`
	Id           string
	LastModified string `json:"last_modified"`
	Location     Location
	LowGrade     string `json:"low_grade"`
	Name         string
	NcesId       string `json:"nces_id"`
	Phone        string
	SchoolNumber string `json:"school_number"`
	SisId        string `json:"sis_id"`
	StateId      string `json:"state_id"`
}

type TeacherResp struct {
	Links   []Link
	Teacher Teacher `json:"data"`
	Uri     string
}

type Teacher struct {
	Created       string
	District      string
	Email         string
	Id            string
	LastModified  string `json:"last_modified"`
	Name          Name
	School        string
	SisId         string `json:"sis_id"`
	TeacherNumber string `json:"teacher_number"`
	Title         string
}

type StudentResp struct {
	Links   []Link
	Student Student `json:"data"`
	Uri     string
}

type Student struct {
	Created           string
	District          string
	Dob               string
	Email             string
	FrlStatus         string `json:"frl_status"`
	Gender            string
	Grade             string
	HispanicEthnicity string `json:"hispanic_ethnicity"`
	Id                string
	LastModified      string `json:"last_modified"`
	Location          Location
	Name              Name
	Race              string
	School            string
	SisId             string `json:"sis_id"`
	StateId           string `json:"state_id"`
	StudentNumber     string `json:"student_number"`
}

type SectionResp struct {
	Links   []Link
	Section Section `json:"data"`
	Uri     string
}

type Section struct {
	CourseName   string `json:"course_name"`
	CourseNumber string `json:"course_number"`
	Created      string
	District     string
	Grade        string
	Id           string `json:"id"`
	LastModified string `json:"last_modified"`
	Name         string
	School       string
	SisId        string `json:"sis_id"`
	Students     []string
	Subject      string
	Teacher      string
	Term
}

type Location struct {
	Address string
	City    string
	State   string
	Zip     string
}

type Name struct {
	First  string
	Middle string
	Last   string
}

type Term struct {
	Name      string
	StartDate string `json:"start_date"`
	EndDate   string `json:"end_date"`
}

func (clever *Clever) Query(path string, params url.Values, resp interface{}) error {
	uri := fmt.Sprintf("%s%s", clever.Url, path)
	if params != nil {
		uri = fmt.Sprintf("%s%s?%s", clever.Url, path, params.Encode())
	}
	client := &http.Client{}
	req, _ := http.NewRequest("GET", uri, nil)
	if clever.Auth.Token != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", clever.Auth.Token))
	} else if clever.Auth.APIKey != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(clever.Auth.APIKey+":"))))
	} else {
		return fmt.Errorf("Must provide either API key or bearer token")
	}
	if debug {
		log.Printf("get { %v } -> {\n", uri)
	}
	r, err := client.Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if debug {
		dump, _ := httputil.DumpResponse(r, true)
		log.Printf("response:\n")
		log.Printf("%v\n}\n", string(dump))
	}
	if r.StatusCode != 200 {
		var error CleverError
		json.NewDecoder(r.Body).Decode(&error)
		return &error
	}
	err = json.NewDecoder(r.Body).Decode(resp)
	return err
}

type PagedResult struct {
	clever         Clever
	nextPagePath   string
	lastData       []map[string]interface{}
	lastDataCursor int
	lastError      error
}

func (clever *Clever) QueryAll(path string, params url.Values) PagedResult {
	paramString := ""
	if params != nil {
		paramString = "?" + params.Encode()
	}

	return PagedResult{clever: *clever, nextPagePath: path + paramString, lastDataCursor: -1}
}

func (r *PagedResult) Next() bool {
	if r.lastDataCursor != -1 && r.lastDataCursor < len(r.lastData)-1 {
		r.lastDataCursor++
		return true
	}

	if r.nextPagePath == "" {
		return false
	}

	resp := &struct {
		Data   []map[string]interface{}
		Links  []Link
		Paging Paging
	}{}
	r.lastError = r.clever.Query(r.nextPagePath, nil, resp)
	if r.lastError != nil {
		return false
	}
	r.lastData = resp.Data
	r.lastDataCursor = 0
	r.nextPagePath = ""
	for _, link := range resp.Links {
		if link.Rel == "next" {
			r.nextPagePath = link.Uri
			break
		}
	}
	return true
}

func (r *PagedResult) Scan(result interface{}) error {
	data, err := json.Marshal(r.lastData[r.lastDataCursor]["data"])
	if err != nil {
		return err
	}
	return json.Unmarshal(data, result)
}

func (r *PagedResult) Error() error {
	return r.lastError
}
<commit_msg>Fix PagedResult.Next() returning true if there are 0 results.<commit_after>package clever

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

const debug = false

// API supports basic auth with an API key or bearer auth with a token
type Auth struct {
	APIKey, Token string
}

type Clever struct {
	Auth
	Url string
}

// Creates a new clever object to make requests with. URL must be a valid base url, e.g. "https://api.clever.com"
func New(auth Auth, url string) *Clever {
	return &Clever{auth, url}
}

type CleverError struct {
	Code    string
	Message string `json:"error"`
}

func (err *CleverError) Error() string {
	if err.Code == "" {
		return err.Message
	}
	return fmt.Sprintf("%s (%s)", err.Error, err.Code)
}

type Paging struct {
	Count   int
	Current int
	Total   int
}

type Link struct {
	Rel string
	Uri string
}

type DistrictResp struct {
	District District `json:"data"`
	Links    []Link
	Uri      string
}

type District struct {
	Id   string
	Name string
}

type SchoolResp struct {
	Links  []Link
	School School `json:"data"`
	Uri    string
}

type School struct {
	Created      string
	District     string
	HighGrade    string `json:"high_grade"`
	Id           string
	LastModified string `json:"last_modified"`
	Location     Location
	LowGrade     string `json:"low_grade"`
	Name         string
	NcesId       string `json:"nces_id"`
	Phone        string
	SchoolNumber string `json:"school_number"`
	SisId        string `json:"sis_id"`
	StateId      string `json:"state_id"`
}

type TeacherResp struct {
	Links   []Link
	Teacher Teacher `json:"data"`
	Uri     string
}

type Teacher struct {
	Created       string
	District      string
	Email         string
	Id            string
	LastModified  string `json:"last_modified"`
	Name          Name
	School        string
	SisId         string `json:"sis_id"`
	TeacherNumber string `json:"teacher_number"`
	Title         string
}

type StudentResp struct {
	Links   []Link
	Student Student `json:"data"`
	Uri     string
}

type Student struct {
	Created           string
	District          string
	Dob               string
	Email             string
	FrlStatus         string `json:"frl_status"`
	Gender            string
	Grade             string
	HispanicEthnicity string `json:"hispanic_ethnicity"`
	Id                string
	LastModified      string `json:"last_modified"`
	Location          Location
	Name              Name
	Race              string
	School            string
	SisId             string `json:"sis_id"`
	StateId           string `json:"state_id"`
	StudentNumber     string `json:"student_number"`
}

type SectionResp struct {
	Links   []Link
	Section Section `json:"data"`
	Uri     string
}

type Section struct {
	CourseName   string `json:"course_name"`
	CourseNumber string `json:"course_number"`
	Created      string
	District     string
	Grade        string
	Id           string `json:"id"`
	LastModified string `json:"last_modified"`
	Name         string
	School       string
	SisId        string `json:"sis_id"`
	Students     []string
	Subject      string
	Teacher      string
	Term
}

type Location struct {
	Address string
	City    string
	State   string
	Zip     string
}

type Name struct {
	First  string
	Middle string
	Last   string
}

type Term struct {
	Name      string
	StartDate string `json:"start_date"`
	EndDate   string `json:"end_date"`
}

func (clever *Clever) Query(path string, params url.Values, resp interface{}) error {
	uri := fmt.Sprintf("%s%s", clever.Url, path)
	if params != nil {
		uri = fmt.Sprintf("%s%s?%s", clever.Url, path, params.Encode())
	}
	client := &http.Client{}
	req, _ := http.NewRequest("GET", uri, nil)
	if clever.Auth.Token != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", clever.Auth.Token))
	} else if clever.Auth.APIKey != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(clever.Auth.APIKey+":"))))
	} else {
		return fmt.Errorf("Must provide either API key or bearer token")
	}
	if debug {
		log.Printf("get { %v } -> {\n", uri)
	}
	r, err := client.Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if debug {
		dump, _ := httputil.DumpResponse(r, true)
		log.Printf("response:\n")
		log.Printf("%v\n}\n", string(dump))
	}
	if r.StatusCode != 200 {
		var error CleverError
		json.NewDecoder(r.Body).Decode(&error)
		return &error
	}
	err = json.NewDecoder(r.Body).Decode(resp)
	return err
}

type PagedResult struct {
	clever         Clever
	nextPagePath   string
	lastData       []map[string]interface{}
	lastDataCursor int
	lastError      error
}

func (clever *Clever) QueryAll(path string, params url.Values) PagedResult {
	paramString := ""
	if params != nil {
		paramString = "?" + params.Encode()
	}

	return PagedResult{clever: *clever, nextPagePath: path + paramString, lastDataCursor: -1}
}

func (r *PagedResult) Next() bool {
	if r.lastDataCursor != -1 && r.lastDataCursor < len(r.lastData)-1 {
		r.lastDataCursor++
		return true
	}

	if r.nextPagePath == "" {
		return false
	}

	resp := &struct {
		Data   []map[string]interface{}
		Links  []Link
		Paging Paging
	}{}
	r.lastError = r.clever.Query(r.nextPagePath, nil, resp)
	if r.lastError != nil {
		return false
	}
	r.lastData = resp.Data
	r.lastDataCursor = 0
	r.nextPagePath = ""
	for _, link := range resp.Links {
		if link.Rel == "next" {
			r.nextPagePath = link.Uri
			break
		}
	}
	return len(r.lastData) > 0
}

func (r *PagedResult) Scan(result interface{}) error {
	data, err := json.Marshal(r.lastData[r.lastDataCursor]["data"])
	if err != nil {
		return err
	}
	return json.Unmarshal(data, result)
}

func (r *PagedResult) Error() error {
	return r.lastError
}
<|endoftext|>


<commit_before>// Implementing Conway's Game Of Life
// ----------------------------------
//
// Using a map for storing the current state of the world.
//
// We are printing the successive populations in a format that can be fed
// to gnuplot and creating in this way an animated view of the population.
//
// This is just an exercise for using maps in go! Do not take this
// too serious...
//
// To see the simulation in gnuplot, call the program like this:
// ./gol.exe | gnuplot --persist

package main

import (
	"fmt"
)

// We are storing the cells (alive or dead) in a map. The keys are the Cartesian
// coordinates of the cells and the values are the properties of the cells,
// namely their state and number of alive neighbours.

// A cell has its state, and its number of life neighbours
type Cell struct {
	alive bool
	n int
}

// The coordinates are plain 2-d cartesian coordinates
type Coord struct {
	x int
	y int
}

// The world is a map of Coord and Cell
type World map[Coord]Cell

// Inflate inflates the world with dead cells surrounding
// the live cells
func (world World) Inflate() World {
	var newWorld World
	newWorld = make(World)

	for coord, cell := range world {
		newWorld[coord] = cell
		for i := -1; i < 2; i++ {
			for j := -1; j < 2; j++ {
				c := Coord{coord.x + i, coord.y + j}
				if _, found := newWorld[c]; !found {
					newWorld[c] = Cell{false, 0}
				}
			}
		}
	}

	return newWorld
}

// Deflate deflates the world: only the live cells remain
func (world World) Deflate() World {
	var newWorld World
	newWorld = make(World)

	for coord, cell := range world {
		if cell.alive {
			newWorld[coord] = cell
		}
	}

	return newWorld
}

// Tick computes the next generation of live cells in the world
func (world World) Tick() World {
	// count live neighbours for each cell
	for coord, cell := range world {
		n := 0
		for i := -1; i < 2; i++ {
			for j := -1; j < 2; j++ {
				c := Coord{coord.x + i, coord.y + j}
				if (i != 0 || j != 0) && world[c].alive {
					n = n+1
				}
			}
		}
		world[coord] = Cell{cell.alive, n}
	}

	var newWorld World
	newWorld = make(World)

	// apply the rules of the game to each cell
	for coord, cell := range world {
		if cell.alive {
			if 1 < cell.n && cell.n < 4 {
				newWorld[coord] = Cell{true, 0}
			}
		} else {
			if cell.n == 3 {
				newWorld[coord] = Cell{true, 0}
			}
		}
	}

	return newWorld
}

// gnuplotHeader prints the header for gnuplot
func gnuplotHeader() {
	fmt.Println("unset key; set xrange[-50:50]")
	fmt.Println("set yrange[-50:50]")
}

// gnuplotCell prints the coordinates of a cell
func gnuplotWorld(world World) {
	fmt.Println("plot '-' with points pointtype 6")

	for coord := range world {
		fmt.Printf("%d, %d\n", coord.x, coord.y)
	}

	fmt.Println("e")
}

func main() {
	// The world
	var world World
	world = make(World)

	// Define a starting world: the r-Pentomino
	world[Coord{1, 0}] = Cell{true, 0}
	world[Coord{0, 1}] = Cell{true, 0}
	world[Coord{1, 1}] = Cell{true, 0}
	world[Coord{1, 2}] = Cell{true, 0}
	world[Coord{2, 2}] = Cell{true, 0}

	gnuplotHeader()

	gnuplotWorld(world)

	for i := 0; i < 1000; i++ {
		world = world.Inflate().Tick().Deflate()
		gnuplotWorld(world)
	}
}
<commit_msg>"Added command line parameters handling using cobra"<commit_after>// Implementing Conway's Game Of Life
// ----------------------------------
//
// Using a map for storing the current state of the world.
//
// We are printing the successive populations in a format that can be fed
// to gnuplot and creating in this way an animated view of the population.
//
// This is just an exercise for using maps in go! Do not take this
// too serious...
//
// To see the simulation in gnuplot, call the program like this:
// ./gol.exe | gnuplot --persist

package main

import (
	"github.com/spf13/cobra"
	"fmt"
)

// We are storing the cells (alive or dead) in a map. The keys are the Cartesian
// coordinates of the cells and the values are the properties of the cells,
// namely their state and number of alive neighbours.

// A cell has its state, and its number of life neighbours
type Cell struct {
	alive bool
	n int
}

// The coordinates are plain 2-d cartesian coordinates
type Coord struct {
	x int
	y int
}

// The world is a map of Coord and Cell
type World map[Coord]Cell

// Inflate inflates the world with dead cells surrounding
// the live cells
func (world World) Inflate() World {
	var newWorld World
	newWorld = make(World)

	for coord, cell := range world {
		newWorld[coord] = cell
		for i := -1; i < 2; i++ {
			for j := -1; j < 2; j++ {
				c := Coord{coord.x + i, coord.y + j}
				if _, found := newWorld[c]; !found {
					newWorld[c] = Cell{false, 0}
				}
			}
		}
	}

	return newWorld
}

// Deflate deflates the world: only the live cells remain
func (world World) Deflate() World {
	var newWorld World
	newWorld = make(World)

	for coord, cell := range world {
		if cell.alive {
			newWorld[coord] = cell
		}
	}

	return newWorld
}

// Tick computes the next generation of live cells in the world
func (world World) Tick() World {
	// count live neighbours for each cell
	for coord, cell := range world {
		n := 0
		for i := -1; i < 2; i++ {
			for j := -1; j < 2; j++ {
				c := Coord{coord.x + i, coord.y + j}
				if (i != 0 || j != 0) && world[c].alive {
					n = n+1
				}
			}
		}
		world[coord] = Cell{cell.alive, n}
	}

	var newWorld World
	newWorld = make(World)

	// apply the rules of the game to each cell
	for coord, cell := range world {
		if cell.alive {
			if 1 < cell.n && cell.n < 4 {
				newWorld[coord] = Cell{true, 0}
			}
		} else {
			if cell.n == 3 {
				newWorld[coord] = Cell{true, 0}
			}
		}
	}

	return newWorld
}

// gnuplotHeader prints the header for gnuplot
func gnuplotHeader() {
	fmt.Println("unset key; set xrange[-50:50]")
	fmt.Println("set yrange[-50:50]")
}

// gnuplotWorld prints the coordinates of the cells in the world
func gnuplotWorld(world World) {
	fmt.Println("plot '-' with points pointtype 6")

	for coord := range world {
		fmt.Printf("%d, %d\n", coord.x, coord.y)
	}

	fmt.Println("e")
}

func main() {
	ticks := 10  // how many ticks the game is running

	// Cobra configuration
	var rootCmd = &cobra.Command{
		Use: "gol",
		Short: "Run Conway's Game of Life",
		Long: `The Game of Life, also known simply as Life, is a
cellular automaton devised by the British mathematician John Horton Conway in
1970.

The "game" is a zero-player game, meaning that its evolution is determined by
its initial state, requiring no further input. One interacts with the Game of
Life by creating an initial configuration and observing how it evolves or, for
advanced players, by creating patterns with particular properties.`,
		Run: func(cmd *cobra.Command, args []string) {
			gol(ticks)
		},
	}

	rootCmd.Flags().IntVarP(&ticks, "ticks", "t", 10, "ticks to run the game")

	rootCmd.Execute()
}

func gol(ticks int) {
	// The world
	var world World
	world = make(World)

	// Define a starting world: the r-Pentomino
	world[Coord{1, 0}] = Cell{true, 0}
	world[Coord{0, 1}] = Cell{true, 0}
	world[Coord{1, 1}] = Cell{true, 0}
	world[Coord{1, 2}] = Cell{true, 0}
	world[Coord{2, 2}] = Cell{true, 0}

	gnuplotHeader()

	gnuplotWorld(world)

	for i := 0; i < ticks; i++ {
		world = world.Inflate().Tick().Deflate()
		gnuplotWorld(world)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
	"net"
	"strconv"
	"sync"
)

type service struct {
	Host string `json:"host"`
	Port int    `json:"port"`
}

type host struct {
	key  string
	addr string
}

type backends struct {
	config     *Config
	path       string
	hosts      []host
	lastIndex  int
	watchIndex uint64
	lock       sync.RWMutex
}

func NewBackends(c *Config) *backends {
	return &backends{config: c}
}

func (b *backends) Init() {
	peers := trimsplit(b.config.etcdAddress, ",")
	client := etcd.NewClient(peers)
	b.Sync(client)
	go b.Watch(client)
}

func (b *backends) Dump(action string) {
	for _, v := range b.hosts {
		glog.V(3).Infof("Dump after %s %s -> %s", action, v.key, v.addr)
	}
}

func (b *backends) Remove(key string) {
	match := -1
	for k, v := range b.hosts {
		if v.key == key {
			match = k
		}
	}

	b.hosts = append(b.hosts[:match], b.hosts[match+1:]...)
	b.Dump("remove")
}

func (b *backends) Update(node *etcd.Node, action string) {
	b.lock.Lock()
	defer b.lock.Unlock()

	glog.V(3).Info("key: %s action: %s value: %s", node.Key, action, string(node.Value))

	s := &service{}
	if action == "delete" || action == "expire" {
		b.Remove(node.Key)
		return
	}

	err := json.Unmarshal([]byte(node.Value), s)
	if err != nil {
		panic(err)
	}

	addr := net.JoinHostPort(s.Host, strconv.Itoa(s.Port))

	for index, v := range b.hosts {
		if v.key == node.Key {
			b.hosts[index] = host{addr: addr, key: node.Key}
			b.Dump(action)
			v.addr = addr
			return
		}
	}

	b.hosts = append(b.hosts, host{addr: addr, key: node.Key})
}

func (b *backends) Watch(client *etcd.Client) {
	receiver := make(chan *etcd.Response)
	go client.Watch(b.config.servicePath, uint64(b.watchIndex), true, receiver, nil)

	for {
		resp := <-receiver
		b.Update(resp.Node, resp.Action)
	}
}

func (b *backends) Sync(client *etcd.Client) error {
	glog.V(3).Info("Synchronizing path : %s", b.config.servicePath)
	resp, err := client.Get(b.config.servicePath, false, true)

	if err != nil {
		return err
	}

	for _, n := range resp.Node.Nodes {
		b.Update(&n, resp.Action)
	}

	// Begin the watch after this sync from the next sync
	b.watchIndex = resp.EtcdIndex + 1

	return nil
}

func (b *backends) Next() string {
	b.lock.RLock()
	defer b.lock.RUnlock()

	if len(b.hosts) == 0 {
		return ""
	}

	index := (b.lastIndex + 1) % len(b.hosts)
	b.lastIndex = index

	return b.hosts[index].addr
}
<commit_msg>NXIO-142: store the hosts as a slice of pointers to avoid copying host objects<commit_after>package main

import (
	"encoding/json"
	"github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
	"net"
	"strconv"
	"sync"
)

type service struct {
	Host string `json:"host"`
	Port int    `json:"port"`
}

type host struct {
	key  string
	addr string
}

type backends struct {
	config     *Config
	path       string
	hosts      []*host
	lastIndex  int
	watchIndex uint64
	lock       sync.RWMutex
}

func NewBackends(c *Config) *backends {
	return &backends{config: c}
}

func (b *backends) Init() {
	peers := trimsplit(b.config.etcdAddress, ",")
	client := etcd.NewClient(peers)
	b.Sync(client)
	go b.Watch(client)
}

func (b *backends) Dump(action string) {
	for _, v := range b.hosts {
		glog.V(3).Infof("Dump after %s %s -> %s", action, v.key, v.addr)
	}
}

func (b *backends) Remove(key string) {
	match := -1
	for k, v := range b.hosts {
		if v.key == key {
			match = k
		}
	}

	b.hosts = append(b.hosts[:match], b.hosts[match+1:]...)
	b.Dump("remove")
}

func (b *backends) Update(node *etcd.Node, action string) {
	b.lock.Lock()
	defer b.lock.Unlock()

	glog.V(3).Info("key: %s action: %s value: %s", node.Key, action, string(node.Value))

	s := &service{}
	if action == "delete" || action == "expire" {
		b.Remove(node.Key)
		return
	}

	err := json.Unmarshal([]byte(node.Value), s)
	if err != nil {
		panic(err)
	}

	addr := net.JoinHostPort(s.Host, strconv.Itoa(s.Port))

	for _, v := range b.hosts {
		if v.key == node.Key {
			v.addr = addr
			b.Dump(action)
			v.addr = addr
			return
		}
	}

	b.hosts = append(b.hosts, &host{addr: addr, key: node.Key})
}

func (b *backends) Watch(client *etcd.Client) {
	receiver := make(chan *etcd.Response)
	go client.Watch(b.config.servicePath, uint64(b.watchIndex), true, receiver, nil)

	for {
		resp := <-receiver
		b.Update(resp.Node, resp.Action)
	}
}

func (b *backends) Sync(client *etcd.Client) error {
	glog.V(3).Info("Synchronizing path : %s", b.config.servicePath)
	resp, err := client.Get(b.config.servicePath, false, true)

	if err != nil {
		return err
	}

	for _, n := range resp.Node.Nodes {
		b.Update(&n, resp.Action)
	}

	// Begin the watch after this sync from the next sync
	b.watchIndex = resp.EtcdIndex + 1

	return nil
}

func (b *backends) Next() string {
	b.lock.RLock()
	defer b.lock.RUnlock()

	if len(b.hosts) == 0 {
		return ""
	}

	index := (b.lastIndex + 1) % len(b.hosts)
	b.lastIndex = index

	return b.hosts[index].addr
}
<|endoftext|>


<commit_before>// Copyright 2016 Davide Muzzarelli. All right reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package gqt is a template engine for SQL queries.

It helps to separate SQL code from Go code and permits to compose the queries
with a simple syntax.

The template engine is the standard package "text/template".

Usage

Create a template directory tree of .sql files. Here an example template with
the definition of three blocks:

	// File /path/to/sql/repository/dir/example.sql
	{{define "allUsers"}}
	SELECT *
	FROM users
	WHERE 1=1
	{{end}}

	{{define "getUser"}}
	SELECT *
	FROM users
	WHERE id=?
	{{end}}

	{{define "allPosts"}}
	SELECT *
	FROM posts
	WHERE date>=?
	{{if ne .Order ""}}ORDER BY date {{.Order}}{{end}}
	{{end}}

Then, with Go, add the directory to the default repository and execute the
queries:

	// Setup
	gqt.Add("/path/to/sql/repository/dir", "*.sql")

	// Simple query without parameters
	db.Query(gqt.Get("allUsers"))
	// Query with parameters
	db.QueryRow(gqt.Get("getuser"), 1)
	// Query with context and parameters
	db.Query(gqt.Exec("allPosts", map[string]interface{
		"Order": "DESC",
	}), date)

The templates are parsed immediately and recursively.

Namespaces

The templates can be organized in namespaces and stored in multiple root
directories.

	templates1/
	|-- roles/
	|	|-- queries.sql
	|-- users/
	|	|-- queries.sql
	|	|-- commands.sql

	templates2/
	|-- posts/
	|	|-- queries.sql
	|	|-- commands.sql
	|-- users/
	|	|-- queries.sql
	|-- queries.sql

The blocks inside the sql files are merged, the blocks with the same namespace
and name will be overridden following the alphabetical order.

The sub-directories are used as namespaces and accessed like:

	gqt.Add("../templates1", "*.sql")
	gqt.Add("../templates2", "*.sql")

	// Will search inside templates1/users/*.sql and templates2/users/*.sql
	gqt.Get("users/allUsers")

Multiple databases

When dealing with multiple databases at the same time, like PostgreSQL and
MySQL, just create two repositories:

	// Use a common directory
	dir := "/path/to/sql/repository/dir"

	// Create the PostgreSQL repository
	pgsql := gqt.NewRepository()
	pgsql.Add(dir, "*.pg.sql")

	// Create a separated MySQL repository
	mysql := gqt.NewRepository()
	mysql.Add(dir, "*.my.sql")

	// Then execute
	pgsql.Get("queryName")
	mysql.Get("queryName")
*/
package gqt

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

// SQL template repository.
type Repository struct {
	templates map[string]*template.Template // namespace: template
}

// Constructor.
func NewRepository() *Repository {
	return &Repository{
		templates: make(map[string]*template.Template),
	}
}

// Add adds a root directory to the repository, recursively. Match only the
// given file extension. Blocks on the same namespace will be overridden. Does
// not follow symbolic links.
func (r *Repository) Add(root string, pattern string) (err error) {
	// List the directories
	dirs := []string{}
	err = filepath.Walk(
		root,
		func(path string, info os.FileInfo, e error) error {
			if e != nil {
				return e
			}
			if info.IsDir() {
				dirs = append(dirs, path)
			}
			return nil
		},
	)
	if err != nil {
		return err
	}

	// Add each sub-directory as namespace
	for _, dir := range dirs {
		//namespace := strings.Replace(dir, root, "", 1)
		d := strings.Split(dir, string(os.PathSeparator))
		ro := strings.Split(root, string(os.PathSeparator))
		namespace := strings.Join(d[len(ro):], "/")
		err = r.addDir(dir, namespace, pattern)
		if err != nil {
			return err
		}
	}
	return nil
}

// addDir parses a directory.
func (r *Repository) addDir(path, namespace, pattern string) (err error) {
	// Create the template if necessary
	if _, ok := r.templates[namespace]; ok == false {
		r.templates[namespace] = template.New("")
	}

	// Parse the template
	t := r.templates[namespace]
	t, err = t.ParseGlob(filepath.Join(path, pattern))
	r.templates[namespace] = t
	if err != nil {
		return err
	}
	return nil
}

// Get is a shortcut for r.Exec(), passing nil as data.
func (r *Repository) Get(name string) string {
	return r.Exec(name, nil)
}

// Exec is a shortcut for r.Parse(), but panics if an error occur.
func (r *Repository) Exec(name string, data interface{}) (s string) {
	var err error
	s, err = Parse(name, data)
	if err != nil {
		panic(err)
	}
	return s
}

// Parse executes the template and returns the resulting SQL or an error.
func (r *Repository) Parse(name string, data interface{}) (string, error) {
	// Prepare namespace and block name
	if name == "" {
		return "", fmt.Errorf("unnamed block")
	}
	path := strings.Split(name, "/")
	namespace := strings.Join(path[0:len(path)-1], "/")
	if namespace == "." {
		namespace = ""
	}
	block := path[len(path)-1]

	// Execute the template
	var b bytes.Buffer
	t, ok := r.templates[namespace]
	if ok == false {
		return "", fmt.Errorf("unknown namespace \"%s\"", namespace)
	}
	err := t.ExecuteTemplate(&b, block, data)
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

var defaultRepository = NewRepository()

// Add method for the default repository.
func Add(root string, ext string) error {
	return defaultRepository.Add(root, ext)
}

// Get method for the default repository.
func Get(name string) string {
	return defaultRepository.Get(name)
}

// Exec method for the default repository.
func Exec(name string, data interface{}) string {
	return defaultRepository.Exec(name, data)
}

// Parse method for the default repository.
func Parse(name string, data interface{}) (string, error) {
	return defaultRepository.Parse(name, data)
}
<commit_msg>Do not store nil in templates on parse error<commit_after>// Copyright 2016 Davide Muzzarelli. All right reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package gqt is a template engine for SQL queries.

It helps to separate SQL code from Go code and permits to compose the queries
with a simple syntax.

The template engine is the standard package "text/template".

Usage

Create a template directory tree of .sql files. Here an example template with
the definition of three blocks:

	// File /path/to/sql/repository/dir/example.sql
	{{define "allUsers"}}
	SELECT *
	FROM users
	WHERE 1=1
	{{end}}

	{{define "getUser"}}
	SELECT *
	FROM users
	WHERE id=?
	{{end}}

	{{define "allPosts"}}
	SELECT *
	FROM posts
	WHERE date>=?
	{{if ne .Order ""}}ORDER BY date {{.Order}}{{end}}
	{{end}}

Then, with Go, add the directory to the default repository and execute the
queries:

	// Setup
	gqt.Add("/path/to/sql/repository/dir", "*.sql")

	// Simple query without parameters
	db.Query(gqt.Get("allUsers"))
	// Query with parameters
	db.QueryRow(gqt.Get("getuser"), 1)
	// Query with context and parameters
	db.Query(gqt.Exec("allPosts", map[string]interface{
		"Order": "DESC",
	}), date)

The templates are parsed immediately and recursively.

Namespaces

The templates can be organized in namespaces and stored in multiple root
directories.

	templates1/
	|-- roles/
	|	|-- queries.sql
	|-- users/
	|	|-- queries.sql
	|	|-- commands.sql

	templates2/
	|-- posts/
	|	|-- queries.sql
	|	|-- commands.sql
	|-- users/
	|	|-- queries.sql
	|-- queries.sql

The blocks inside the sql files are merged, the blocks with the same namespace
and name will be overridden following the alphabetical order.

The sub-directories are used as namespaces and accessed like:

	gqt.Add("../templates1", "*.sql")
	gqt.Add("../templates2", "*.sql")

	// Will search inside templates1/users/*.sql and templates2/users/*.sql
	gqt.Get("users/allUsers")

Multiple databases

When dealing with multiple databases at the same time, like PostgreSQL and
MySQL, just create two repositories:

	// Use a common directory
	dir := "/path/to/sql/repository/dir"

	// Create the PostgreSQL repository
	pgsql := gqt.NewRepository()
	pgsql.Add(dir, "*.pg.sql")

	// Create a separated MySQL repository
	mysql := gqt.NewRepository()
	mysql.Add(dir, "*.my.sql")

	// Then execute
	pgsql.Get("queryName")
	mysql.Get("queryName")
*/
package gqt

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

// SQL template repository.
type Repository struct {
	templates map[string]*template.Template // namespace: template
}

// Constructor.
func NewRepository() *Repository {
	return &Repository{
		templates: make(map[string]*template.Template),
	}
}

// Add adds a root directory to the repository, recursively. Match only the
// given file extension. Blocks on the same namespace will be overridden. Does
// not follow symbolic links.
func (r *Repository) Add(root string, pattern string) (err error) {
	// List the directories
	dirs := []string{}
	err = filepath.Walk(
		root,
		func(path string, info os.FileInfo, e error) error {
			if e != nil {
				return e
			}
			if info.IsDir() {
				dirs = append(dirs, path)
			}
			return nil
		},
	)
	if err != nil {
		return err
	}

	// Add each sub-directory as namespace
	for _, dir := range dirs {
		//namespace := strings.Replace(dir, root, "", 1)
		d := strings.Split(dir, string(os.PathSeparator))
		ro := strings.Split(root, string(os.PathSeparator))
		namespace := strings.Join(d[len(ro):], "/")
		err = r.addDir(dir, namespace, pattern)
		if err != nil {
			return err
		}
	}
	return nil
}

// addDir parses a directory.
func (r *Repository) addDir(path, namespace, pattern string) error {
	// Parse the template
	t, err := t.ParseGlob(filepath.Join(path, pattern))
	if err != nil {
		r.templates[namespace] = template.New("")
		return err
	}
	r.templates[namespace] = t
	return nil
}

// Get is a shortcut for r.Exec(), passing nil as data.
func (r *Repository) Get(name string) string {
	return r.Exec(name, nil)
}

// Exec is a shortcut for r.Parse(), but panics if an error occur.
func (r *Repository) Exec(name string, data interface{}) (s string) {
	var err error
	s, err = Parse(name, data)
	if err != nil {
		panic(err)
	}
	return s
}

// Parse executes the template and returns the resulting SQL or an error.
func (r *Repository) Parse(name string, data interface{}) (string, error) {
	// Prepare namespace and block name
	if name == "" {
		return "", fmt.Errorf("unnamed block")
	}
	path := strings.Split(name, "/")
	namespace := strings.Join(path[0:len(path)-1], "/")
	if namespace == "." {
		namespace = ""
	}
	block := path[len(path)-1]

	// Execute the template
	var b bytes.Buffer
	t, ok := r.templates[namespace]
	if ok == false {
		return "", fmt.Errorf("unknown namespace \"%s\"", namespace)
	}
	err := t.ExecuteTemplate(&b, block, data)
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

var defaultRepository = NewRepository()

// Add method for the default repository.
func Add(root string, ext string) error {
	return defaultRepository.Add(root, ext)
}

// Get method for the default repository.
func Get(name string) string {
	return defaultRepository.Get(name)
}

// Exec method for the default repository.
func Exec(name string, data interface{}) string {
	return defaultRepository.Exec(name, data)
}

// Parse method for the default repository.
func Parse(name string, data interface{}) (string, error) {
	return defaultRepository.Parse(name, data)
}
<|endoftext|>


<commit_before>package uic

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"github.com/Cepave/fe/g"
	"github.com/Cepave/fe/http/base"
	. "github.com/Cepave/fe/model/uic"
	"github.com/Cepave/fe/utils"
	"github.com/toolkits/str"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type AuthController struct {
	base.BaseController
}

func (this *AuthController) Logout() {
	u := this.Ctx.Input.GetData("CurrentUser").(*User)
	RemoveSessionByUid(u.Id)
	this.Ctx.SetCookie("sig", "", 0, "/")
	this.Ctx.SetCookie("sig", "", 0, "/", ".owlemon.com")
	this.Redirect("/auth/login", 302)
}

func (this *AuthController) LoginGet() {
	appSig := this.GetString("sig", "")
	callback := this.GetString("callback", "")

	cookieSig := this.Ctx.GetCookie("sig")
	if cookieSig == "" {
		this.renderLoginPage(appSig, callback)
		return
	}

	sessionObj := ReadSessionBySig(cookieSig)
	if sessionObj == nil {
		this.renderLoginPage(appSig, callback)
		return
	}

	if int64(sessionObj.Expired) < time.Now().Unix() {
		RemoveSessionByUid(sessionObj.Uid)
		this.renderLoginPage(appSig, callback)
		return
	}

	if appSig != "" && callback != "" {
		this.Redirect(callback, 302)
	} else {
		this.Redirect("/me/info", 302)
	}
}

func (this *AuthController) LoginPost() {
	name := this.GetString("name", "")
	password := this.GetString("password", "")

	if name == "" || password == "" {
		this.ServeErrJson("name or password is blank")
		return
	}

	var u *User

	ldapEnabled := this.MustGetBool("ldap", false)

	if ldapEnabled {
		sucess, err := utils.LdapBind(g.Config().Ldap.Addr, name, password)
		if err != nil {
			this.ServeErrJson(err.Error())
			return
		}

		if !sucess {
			this.ServeErrJson("name or password error")
			return
		}

		arr := strings.Split(name, "@")
		var userName, userEmail string
		if len(arr) == 2 {
			userName = arr[0]
			userEmail = name
		} else {
			userName = name
			userEmail = ""
		}

		u = ReadUserByName(userName)
		if u == nil {
			// 说明用户不存在
			u = &User{
				Name:   userName,
				Passwd: "",
				Email:  userEmail,
			}
			_, err = u.Save()
			if err != nil {
				this.ServeErrJson("insert user fail " + err.Error())
				return
			}
		}
	} else {
		u = ReadUserByName(name)
		if u == nil {
			this.ServeErrJson("no such user")
			return
		}

		if u.Passwd != str.Md5Encode(g.Config().Salt+password) {
			this.ServeErrJson("password error")
			return
		}
	}

	appSig := this.GetString("sig", "")
	callback := this.GetString("callback", "")
	if appSig != "" && callback != "" {
		SaveSessionAttrs(u.Id, appSig, int(time.Now().Unix())+3600*24*30)
	} else {
		this.CreateSession(u.Id, 3600*24*30)
	}

	this.ServeDataJson(callback)
}

func (this *AuthController) renderLoginPage(sig, callback string) {
	this.Data["CanRegister"] = g.Config().CanRegister
	this.Data["LdapEnabled"] = g.Config().Ldap.Enabled
	this.Data["Sig"] = sig
	this.Data["Callback"] = callback
	this.Data["Shortcut"] = g.Config().Shortcut
	this.TplNames = "auth/login.html"
}

func (this *AuthController) RegisterGet() {
	this.Data["CanRegister"] = g.Config().CanRegister
	this.Data["Shortcut"] = g.Config().Shortcut
	this.TplNames = "auth/register.html"
}

func (this *AuthController) RegisterPost() {
	if !g.Config().CanRegister {
		this.ServeErrJson("registration system is not open")
		return
	}

	name := strings.TrimSpace(this.GetString("name", ""))
	password := strings.TrimSpace(this.GetString("password", ""))
	repeatPassword := strings.TrimSpace(this.GetString("repeat_password", ""))

	if password != repeatPassword {
		this.ServeErrJson("password not equal the repeart one")
		return
	}

	if !utils.IsUsernameValid(name) {
		this.ServeErrJson("name pattern is invalid")
		return
	}

	if ReadUserIdByName(name) > 0 {
		this.ServeErrJson("name is already existent")
		return
	}

	lastId, err := InsertRegisterUser(name, str.Md5Encode(g.Config().Salt+password))
	if err != nil {
		this.ServeErrJson("insert user fail " + err.Error())
		return
	}

	this.CreateSession(lastId, 3600*24*30)

	this.ServeOKJson()
}

func (this *AuthController) CreateSession(uid int64, maxAge int) int {
	sig := utils.GenerateUUID()
	expired := int(time.Now().Unix()) + maxAge
	SaveSessionAttrs(uid, sig, expired)
	this.Ctx.SetCookie("sig", sig, maxAge, "/")
	this.Ctx.SetCookie("sig", sig, maxAge, "/", ".owlemon.com")
	return expired
}

/**
 * @function name:   func (this *AuthController) LoginThirdParty()
 * @description:     This function returns third party login URL.
 * @related issues:  OWL-206
 * @param:           void
 * @return:          void
 * @author:          Don Hsieh
 * @since:           12/17/2015
 * @last modified:   12/17/2015
 * @called by:       beego.Router("/auth/third-party", &AuthController{}, "post:LoginThirdParty")
 *                    in fe/http/uic/uic_routes.go
 */
func (this *AuthController) LoginThirdParty() {
	s := g.Config().Api.Redirect
	s = base64.StdEncoding.EncodeToString([]byte(s))
	strEncoded := url.QueryEscape(s)
	loginUrl := g.Config().Api.Login + "/" + strEncoded
	this.ServeDataJson(loginUrl)
}

/**
 * @function name:   func getRequest(url string) map[string]interface{}
 * @description:     This function sends GET request to given URL.
 * @related issues:  OWL-206, OWL-159
 * @param:           url string
 * @return:          map[string]interface{}
 * @author:          Don Hsieh
 * @since:           12/17/2015
 * @last modified:   12/17/2015
 * @called by:       func (this *AuthController) LoginWithToken()
 *                    in fe/http/uic/auth_controller.go
 */
func getRequest(url string) map[string]interface{} {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println("Error =", err.Error())
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println("Error =", err.Error())
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	var nodes = make(map[string]interface{})
	if err := json.Unmarshal(body, &nodes); err != nil {
		log.Println("Error =", err.Error())
	}
	return nodes
}

/**
 * @function name:   func (this *AuthController) LoginWithToken()
 * @description:     This function logins user with third party token.
 * @related issues:  OWL-206
 * @param:           void
 * @return:          void
 * @author:          Don Hsieh
 * @since:           12/16/2015
 * @last modified:   12/17/2015
 * @called by:       beego.Router("/auth/login/:token", &AuthController{}, "get:LoginWithToken")
 *                    in fe/http/uic/uic_routes.go
 */
func (this *AuthController) LoginWithToken() {
	token := this.Ctx.Input.Param(":token")
	key := g.Config().Api.Key
	authUrl := g.Config().Api.Access + "/" + token + "/" + key

	nodes := getRequest(authUrl)
	if status, ok := nodes["status"]; ok {
		if int(status.(float64)) == 1 {
			data := nodes["data"].(map[string]interface {})
			access_key := data["access_key"].(string)
			username := data["username"].(string)
			email := data["email"].(string)
			log.Println("access_key =", access_key)

			urlRole := g.Config().Api.Role + "/" + access_key
			nodes := getRequest(urlRole)
			role := 3
			if int(nodes["status"].(float64)) == 1 {
				permission := nodes["data"]
				log.Println("permission =", permission)
				if permission == "admin" {
					role = 0
				} else if permission == "operator" {
					role = 1
				} else if permission == "observer" {
					role = 2
				} else if permission == "deny" {
					role = 3
				}
			}
			user := ReadUserByName(username)
			if user == nil {		// create third party user
				InsertRegisterUser(username, "")
				user = ReadUserByName(username)
			}
			user.Passwd = ""
			user.Email = email
			user.Role = role
			user.Update()
			appSig := this.GetString("sig", "")
			callback := this.GetString("callback", "")
			if appSig != "" && callback != "" {
				SaveSessionAttrs(user.Id, appSig, int(time.Now().Unix())+3600*24*30)
			} else {
				this.CreateSession(user.Id, 3600*24*30)
			}
			maxAge := 3600*24*30
			this.Ctx.SetCookie("token", token, maxAge, "/")
			this.Ctx.SetCookie("token", token, maxAge, "/", ".owlemon.com")
			this.Redirect("/me/info", 302)
		}
	}
	// not logged in. redirect to login page.
	appSig := this.GetString("sig", "")
	callback := this.GetString("callback", "")
	this.renderLoginPage(appSig, callback)
}
<commit_msg>[OWL-186] add third party log out<commit_after>package uic

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"github.com/Cepave/fe/g"
	"github.com/Cepave/fe/http/base"
	. "github.com/Cepave/fe/model/uic"
	"github.com/Cepave/fe/utils"
	"github.com/toolkits/str"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type AuthController struct {
	base.BaseController
}

func (this *AuthController) Logout() {
	u := this.Ctx.Input.GetData("CurrentUser").(*User)
	token := this.Ctx.GetCookie("token")
	if len(token) > 0 {
		url := g.Config().Api.Logout + "/" + token
		log.Println("logout url =", url)
		result := getRequest(url)
		log.Println("logout result =", result)
		this.Ctx.SetCookie("token", "", 0, "/")
		this.Ctx.SetCookie("token", "", 0, "/", ".owlemon.com")
	}
	RemoveSessionByUid(u.Id)
	this.Ctx.SetCookie("sig", "", 0, "/")
	this.Ctx.SetCookie("sig", "", 0, "/", ".owlemon.com")
	this.Redirect("/auth/login", 302)
}

func (this *AuthController) LoginGet() {
	appSig := this.GetString("sig", "")
	callback := this.GetString("callback", "")

	cookieSig := this.Ctx.GetCookie("sig")
	if cookieSig == "" {
		this.renderLoginPage(appSig, callback)
		return
	}

	sessionObj := ReadSessionBySig(cookieSig)
	if sessionObj == nil {
		this.renderLoginPage(appSig, callback)
		return
	}

	if int64(sessionObj.Expired) < time.Now().Unix() {
		RemoveSessionByUid(sessionObj.Uid)
		this.renderLoginPage(appSig, callback)
		return
	}

	if appSig != "" && callback != "" {
		this.Redirect(callback, 302)
	} else {
		this.Redirect("/me/info", 302)
	}
}

func (this *AuthController) LoginPost() {
	name := this.GetString("name", "")
	password := this.GetString("password", "")

	if name == "" || password == "" {
		this.ServeErrJson("name or password is blank")
		return
	}

	var u *User

	ldapEnabled := this.MustGetBool("ldap", false)

	if ldapEnabled {
		sucess, err := utils.LdapBind(g.Config().Ldap.Addr, name, password)
		if err != nil {
			this.ServeErrJson(err.Error())
			return
		}

		if !sucess {
			this.ServeErrJson("name or password error")
			return
		}

		arr := strings.Split(name, "@")
		var userName, userEmail string
		if len(arr) == 2 {
			userName = arr[0]
			userEmail = name
		} else {
			userName = name
			userEmail = ""
		}

		u = ReadUserByName(userName)
		if u == nil {
			// 说明用户不存在
			u = &User{
				Name:   userName,
				Passwd: "",
				Email:  userEmail,
			}
			_, err = u.Save()
			if err != nil {
				this.ServeErrJson("insert user fail " + err.Error())
				return
			}
		}
	} else {
		u = ReadUserByName(name)
		if u == nil {
			this.ServeErrJson("no such user")
			return
		}

		if u.Passwd != str.Md5Encode(g.Config().Salt+password) {
			this.ServeErrJson("password error")
			return
		}
	}

	appSig := this.GetString("sig", "")
	callback := this.GetString("callback", "")
	if appSig != "" && callback != "" {
		SaveSessionAttrs(u.Id, appSig, int(time.Now().Unix())+3600*24*30)
	} else {
		this.CreateSession(u.Id, 3600*24*30)
	}

	this.ServeDataJson(callback)
}

func (this *AuthController) renderLoginPage(sig, callback string) {
	this.Data["CanRegister"] = g.Config().CanRegister
	this.Data["LdapEnabled"] = g.Config().Ldap.Enabled
	this.Data["Sig"] = sig
	this.Data["Callback"] = callback
	this.Data["Shortcut"] = g.Config().Shortcut
	this.TplNames = "auth/login.html"
}

func (this *AuthController) RegisterGet() {
	this.Data["CanRegister"] = g.Config().CanRegister
	this.Data["Shortcut"] = g.Config().Shortcut
	this.TplNames = "auth/register.html"
}

func (this *AuthController) RegisterPost() {
	if !g.Config().CanRegister {
		this.ServeErrJson("registration system is not open")
		return
	}

	name := strings.TrimSpace(this.GetString("name", ""))
	password := strings.TrimSpace(this.GetString("password", ""))
	repeatPassword := strings.TrimSpace(this.GetString("repeat_password", ""))

	if password != repeatPassword {
		this.ServeErrJson("password not equal the repeart one")
		return
	}

	if !utils.IsUsernameValid(name) {
		this.ServeErrJson("name pattern is invalid")
		return
	}

	if ReadUserIdByName(name) > 0 {
		this.ServeErrJson("name is already existent")
		return
	}

	lastId, err := InsertRegisterUser(name, str.Md5Encode(g.Config().Salt+password))
	if err != nil {
		this.ServeErrJson("insert user fail " + err.Error())
		return
	}

	this.CreateSession(lastId, 3600*24*30)

	this.ServeOKJson()
}

func (this *AuthController) CreateSession(uid int64, maxAge int) int {
	sig := utils.GenerateUUID()
	expired := int(time.Now().Unix()) + maxAge
	SaveSessionAttrs(uid, sig, expired)
	this.Ctx.SetCookie("sig", sig, maxAge, "/")
	this.Ctx.SetCookie("sig", sig, maxAge, "/", ".owlemon.com")
	return expired
}

/**
 * @function name:   func (this *AuthController) LoginThirdParty()
 * @description:     This function returns third party login URL.
 * @related issues:  OWL-206
 * @param:           void
 * @return:          void
 * @author:          Don Hsieh
 * @since:           12/17/2015
 * @last modified:   12/17/2015
 * @called by:       beego.Router("/auth/third-party", &AuthController{}, "post:LoginThirdParty")
 *                    in fe/http/uic/uic_routes.go
 */
func (this *AuthController) LoginThirdParty() {
	s := g.Config().Api.Redirect
	s = base64.StdEncoding.EncodeToString([]byte(s))
	strEncoded := url.QueryEscape(s)
	loginUrl := g.Config().Api.Login + "/" + strEncoded
	this.ServeDataJson(loginUrl)
}

/**
 * @function name:   func getRequest(url string) map[string]interface{}
 * @description:     This function sends GET request to given URL.
 * @related issues:  OWL-206, OWL-159
 * @param:           url string
 * @return:          map[string]interface{}
 * @author:          Don Hsieh
 * @since:           12/17/2015
 * @last modified:   12/17/2015
 * @called by:       func (this *AuthController) LoginWithToken()
 *                    in fe/http/uic/auth_controller.go
 */
func getRequest(url string) map[string]interface{} {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println("Error =", err.Error())
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println("Error =", err.Error())
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	var nodes = make(map[string]interface{})
	if err := json.Unmarshal(body, &nodes); err != nil {
		log.Println("Error =", err.Error())
	}
	return nodes
}

/**
 * @function name:   func (this *AuthController) LoginWithToken()
 * @description:     This function logins user with third party token.
 * @related issues:  OWL-206
 * @param:           void
 * @return:          void
 * @author:          Don Hsieh
 * @since:           12/16/2015
 * @last modified:   12/17/2015
 * @called by:       beego.Router("/auth/login/:token", &AuthController{}, "get:LoginWithToken")
 *                    in fe/http/uic/uic_routes.go
 */
func (this *AuthController) LoginWithToken() {
	token := this.Ctx.Input.Param(":token")
	key := g.Config().Api.Key
	authUrl := g.Config().Api.Access + "/" + token + "/" + key

	nodes := getRequest(authUrl)
	if status, ok := nodes["status"]; ok {
		if int(status.(float64)) == 1 {
			data := nodes["data"].(map[string]interface {})
			access_key := data["access_key"].(string)
			username := data["username"].(string)
			email := data["email"].(string)
			log.Println("access_key =", access_key)

			urlRole := g.Config().Api.Role + "/" + access_key
			nodes := getRequest(urlRole)
			role := 3
			if int(nodes["status"].(float64)) == 1 {
				permission := nodes["data"]
				log.Println("permission =", permission)
				if permission == "admin" {
					role = 0
				} else if permission == "operator" {
					role = 1
				} else if permission == "observer" {
					role = 2
				} else if permission == "deny" {
					role = 3
				}
			}
			user := ReadUserByName(username)
			if user == nil {		// create third party user
				InsertRegisterUser(username, "")
				user = ReadUserByName(username)
			}
			user.Passwd = ""
			user.Email = email
			user.Role = role
			user.Update()
			appSig := this.GetString("sig", "")
			callback := this.GetString("callback", "")
			if appSig != "" && callback != "" {
				SaveSessionAttrs(user.Id, appSig, int(time.Now().Unix())+3600*24*30)
			} else {
				this.CreateSession(user.Id, 3600*24*30)
			}
			maxAge := 3600*24*30
			this.Ctx.SetCookie("token", token, maxAge, "/")
			this.Ctx.SetCookie("token", token, maxAge, "/", ".owlemon.com")
			this.Redirect("/me/info", 302)
		}
	}
	// not logged in. redirect to login page.
	appSig := this.GetString("sig", "")
	callback := this.GetString("callback", "")
	this.renderLoginPage(appSig, callback)
}
<|endoftext|>


<commit_before><commit_msg>Revert "Updated Docker version in Travis"<commit_after><|endoftext|>


<commit_before>package sweetiebot

import (
	"math/rand"
	"strconv"
	"strings"

	"github.com/bwmarrin/discordgo"
)

type QuoteCommand struct {
}

func (c *QuoteCommand) Name() string {
	return "Quote"
}
func (c *QuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		l := 0
		for _, v := range info.config.Quotes {
			l += len(v)
		}
		if l <= 0 {
			return "```There are no quotes.```", false
		}
		i := rand.Intn(l)

		for k, v := range info.config.Quotes {
			if i < len(v) {
				return "**" + getUserName(k, info) + "**: " + v[i], false
			}
			i -= len(v)
		}
		return "```Error: invalid random quote chosen???```", false
	}

	arg := strings.ToLower(args[0])
	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}

	q, ok := info.config.Quotes[IDs[0]]
	l := len(q)
	if !ok || l <= 0 {
		return "```That user has no quotes.```", false
	}
	i := rand.Intn(l)
	if len(args) >= 2 {
		var err error
		i, err = strconv.Atoi(args[1])
		if err != nil {
			return "```Could not parse quote index. Make sure your username is in quotes.```", false
		}
		i--
		if i >= l || i < 0 {
			return "```Invalid quote index. Use !searchquote [user] to list a user's quotes and their indexes.```", false
		}
	}
	return "**" + IDsToUsernames(IDs, info)[0] + "**: " + q[i], false
}
func (c *QuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user] [quote index]", "If no arguments are specified, returns a random quote. If a user is specified, returns a random quote from that user. If a quote index is specified, returns that specific quote.")
}
func (c *QuoteCommand) UsageShort() string { return "Quotes a user." }

type AddQuoteCommand struct {
}

func (c *AddQuoteCommand) Name() string {
	return "AddQuote"
}
func (c *AddQuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		return "```Must specify username.```", false
	}
	if len(args) < 2 {
		return "```Can't add a blank quote!```", false
	}

	arg := strings.ToLower(args[0])
	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}

	if len(info.config.Quotes) == 0 {
		info.config.Quotes = make(map[uint64][]string)
	}
	info.config.Quotes[IDs[0]] = append(info.config.Quotes[IDs[0]], strings.Join(args[1:], " "))
	info.SaveConfig()
	return "```Quote added to " + IDsToUsernames(IDs, info)[0] + ".```", false
}
func (c *AddQuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user] [quote]", "Adds a quote to the quote database for the given user. If the username has spaces, it must be in quotes. If the user is ambiguous, sweetiebot will return all possible matches.")
}
func (c *AddQuoteCommand) UsageShort() string { return "Adds a quote." }

type RemoveQuoteCommand struct {
}

func (c *RemoveQuoteCommand) Name() string {
	return "RemoveQuote"
}
func (c *RemoveQuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		return "```Must specify username.```", false
	}
	if len(args) < 2 {
		return "```Must specify quote index. Use !searchquote to list them.```", false
	}

	arg := strings.ToLower(args[0])
	index, err := strconv.Atoi(args[1])
	if err != nil {
		return "```Error: could not parse quote index. Did you surround your username with quotes?```", false
	}

	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}

	index--
	if index >= len(info.config.Quotes[IDs[0]]) || index < 0 {
		return "```Invalid quote index. Use !searchquote [user] to list a user's quotes and their indexes.```", false
	}
	info.config.Quotes[IDs[0]] = append(info.config.Quotes[IDs[0]][:index], info.config.Quotes[IDs[0]][index+1:]...)
	info.SaveConfig()
	return "```Deleted quote #" + strconv.Itoa(index+1) + " from " + IDsToUsernames(IDs, info)[0] + ".```", false
}
func (c *RemoveQuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user] [quote index]", "Removes the quote with the given quote index from the user's set of quotes. If the username has spaces, it must be in quotes. If the user is ambiguous, sweetiebot will return all possible matches.")
}
func (c *RemoveQuoteCommand) UsageShort() string { return "Removes a quote." }

type SearchQuoteCommand struct {
}

func (c *SearchQuoteCommand) Name() string {
	return "SearchQuote"
}
func (c *SearchQuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		s := make([]uint64, 0, len(info.config.Quotes))
		for k, v := range info.config.Quotes {
			if len(v) > 0 { // Map entries can have 0 quotes associated with them
				s = append(s, k)
			}
		}
		return "```The following users have at least one quote:\n" + strings.Join(IDsToUsernames(s, info), "\n") + "```", len(s) > 6
	}

	arg := strings.ToLower(args[0])
	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}
	l := len(info.config.Quotes[IDs[0]])
	if l == 0 {
		return "```That user has no quotes.```", false
	}
	quotes := make([]string, l, l)
	for i := 0; i < l; i++ {
		quotes[i] = strconv.Itoa(i+1) + ". " + info.config.Quotes[IDs[0]][i]
	}
	return "All quotes for " + IDsToUsernames(IDs, info)[0] + ":\n" + strings.Join(quotes, "\n"), l > 6
}
func (c *SearchQuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user]", "Lists all quotes for the given user.")
}
func (c *SearchQuoteCommand) UsageShort() string { return "Finds a quote." }
<commit_msg>Add additional stupid-proofing.<commit_after>package sweetiebot

import (
	"math/rand"
	"strconv"
	"strings"

	"github.com/bwmarrin/discordgo"
)

type QuoteCommand struct {
}

func (c *QuoteCommand) Name() string {
	return "Quote"
}
func (c *QuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		l := 0
		for _, v := range info.config.Quotes {
			l += len(v)
		}
		if l <= 0 {
			return "```There are no quotes.```", false
		}
		i := rand.Intn(l)

		for k, v := range info.config.Quotes {
			if i < len(v) {
				return "**" + getUserName(k, info) + "**: " + v[i], false
			}
			i -= len(v)
		}
		return "```Error: invalid random quote chosen???```", false
	}

	arg := strings.ToLower(args[0])
	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}

	q, ok := info.config.Quotes[IDs[0]]
	l := len(q)
	if !ok || l <= 0 {
		return "```That user has no quotes.```", false
	}
	i := rand.Intn(l)
	if len(args) >= 2 {
		var err error
		i, err = strconv.Atoi(args[1])
		if err != nil {
			return "```Could not parse quote index. Make sure your username is in quotes. Use !searchquote [user] to list a user's quotes and their indexes.```", false
		}
		i--
		if i >= l || i < 0 {
			return "```Invalid quote index. Use !searchquote [user] to list a user's quotes and their indexes.```", false
		}
	}
	return "**" + IDsToUsernames(IDs, info)[0] + "**: " + q[i], false
}
func (c *QuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user] [quote index]", "If no arguments are specified, returns a random quote. If a user is specified, returns a random quote from that user. If a quote index is specified, returns that specific quote.")
}
func (c *QuoteCommand) UsageShort() string { return "Quotes a user." }

type AddQuoteCommand struct {
}

func (c *AddQuoteCommand) Name() string {
	return "AddQuote"
}
func (c *AddQuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		return "```Must specify username.```", false
	}
	if len(args) < 2 {
		return "```Can't add a blank quote!```", false
	}

	arg := strings.ToLower(args[0])
	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}

	if len(info.config.Quotes) == 0 {
		info.config.Quotes = make(map[uint64][]string)
	}
	info.config.Quotes[IDs[0]] = append(info.config.Quotes[IDs[0]], strings.Join(args[1:], " "))
	info.SaveConfig()
	return "```Quote added to " + IDsToUsernames(IDs, info)[0] + ".```", false
}
func (c *AddQuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user] [quote]", "Adds a quote to the quote database for the given user. If the username has spaces, it must be in quotes. If the user is ambiguous, sweetiebot will return all possible matches. Use !searchquote to find a quote index.")
}
func (c *AddQuoteCommand) UsageShort() string { return "Adds a quote." }

type RemoveQuoteCommand struct {
}

func (c *RemoveQuoteCommand) Name() string {
	return "RemoveQuote"
}
func (c *RemoveQuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		return "```Must specify username.```", false
	}
	if len(args) < 2 {
		return "```Must specify quote index. Use !searchquote to list them.```", false
	}

	arg := strings.ToLower(args[0])
	index, err := strconv.Atoi(args[1])
	if err != nil {
		return "```Error: could not parse quote index. Did you surround your username with quotes? Use !searchquote to find a quote index.```", false
	}

	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}

	index--
	if index >= len(info.config.Quotes[IDs[0]]) || index < 0 {
		return "```Invalid quote index. Use !searchquote [user] to list a user's quotes and their indexes.```", false
	}
	info.config.Quotes[IDs[0]] = append(info.config.Quotes[IDs[0]][:index], info.config.Quotes[IDs[0]][index+1:]...)
	info.SaveConfig()
	return "```Deleted quote #" + strconv.Itoa(index+1) + " from " + IDsToUsernames(IDs, info)[0] + ".```", false
}
func (c *RemoveQuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user] [quote index]", "Removes the quote with the given quote index from the user's set of quotes. If the username has spaces, it must be in quotes. If the user is ambiguous, sweetiebot will return all possible matches. Use !searchquote to find a quote index.")
}
func (c *RemoveQuoteCommand) UsageShort() string { return "Removes a quote." }

type SearchQuoteCommand struct {
}

func (c *SearchQuoteCommand) Name() string {
	return "SearchQuote"
}
func (c *SearchQuoteCommand) Process(args []string, msg *discordgo.Message, info *GuildInfo) (string, bool) {
	if len(args) < 1 {
		s := make([]uint64, 0, len(info.config.Quotes))
		for k, v := range info.config.Quotes {
			if len(v) > 0 { // Map entries can have 0 quotes associated with them
				s = append(s, k)
			}
		}
		return "```The following users have at least one quote:\n" + strings.Join(IDsToUsernames(s, info), "\n") + "```", len(s) > 6
	}

	arg := strings.ToLower(args[0])
	IDs := FindUsername(arg)
	if len(IDs) == 0 { // no matches!
		return "```Error: Could not find any usernames or aliases matching " + arg + "!```", false
	}
	if len(IDs) > 1 {
		return "```Could be any of the following users or their aliases:\n" + strings.Join(IDsToUsernames(IDs, info), "\n") + "```", len(IDs) > 5
	}
	l := len(info.config.Quotes[IDs[0]])
	if l == 0 {
		return "```That user has no quotes.```", false
	}
	quotes := make([]string, l, l)
	for i := 0; i < l; i++ {
		quotes[i] = strconv.Itoa(i+1) + ". " + info.config.Quotes[IDs[0]][i]
	}
	return "All quotes for " + IDsToUsernames(IDs, info)[0] + ":\n" + strings.Join(quotes, "\n"), l > 6
}
func (c *SearchQuoteCommand) Usage(info *GuildInfo) string {
	return info.FormatUsage(c, "[user]", "Lists all quotes for the given user.")
}
func (c *SearchQuoteCommand) UsageShort() string { return "Finds a quote." }
<|endoftext|>