<commit_before>// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pkcs11

// These tests depend on SoftHSM and the library being in
// in /usr/lib/softhsm/libsofthsm.so

import (
	"fmt"
	"log"
	"math/big"
	"os"
	"testing"
)

/*
This test supports the following environment variables:

* SOFTHSM_LIB: complete path to libsofthsm.so
* SOFTHSM_TOKENLABEL
* SOFTHSM_PRIVKEYLABEL
* SOFTHSM_PIN
*/

func setenv(t *testing.T) *Ctx {
	lib := "/usr/lib/softhsm/libsofthsm.so"
	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
		lib = x
	}
	t.Logf("loading %s", lib)
	p := New(lib)
	if p == nil {
		t.Fatal("Failed to init lib")
	}
	return p
}

func TestSetenv(t *testing.T) {
	wd, _ := os.Getwd()
	os.Setenv("SOFTHSM_CONF", wd+"/softhsm.conf")

	lib := "/usr/lib/softhsm/libsofthsm.so"
	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
		lib = x
	}
	p := New(lib)
	if p == nil {
		t.Fatal("Failed to init pkcs11")
	}
	p.Destroy()
	return
}

func getSession(p *Ctx, t *testing.T) SessionHandle {
	if e := p.Initialize(); e != nil {
		t.Fatalf("init error %s\n", e)
	}
	slots, e := p.GetSlotList(true)
	if e != nil {
		t.Fatalf("slots %s\n", e)
	}
	session, e := p.OpenSession(slots[0], CKF_SERIAL_SESSION|CKF_RW_SESSION)
	if e != nil {
		t.Fatalf("session %s\n", e)
	}
	if e := p.Login(session, CKU_USER, pin); e != nil {
		t.Fatalf("user pin %s\n", e)
	}
	return session
}

func TestInitialize(t *testing.T) {
	p := setenv(t)
	if e := p.Initialize(); e != nil {
		t.Fatalf("init error %s\n", e)
	}
	p.Finalize()
	p.Destroy()
}

func TestNew(t *testing.T) {
	if p := New(""); p != nil {
		t.Fatalf("init should have failed, got %s\n", p)
	}
	if p := New("/does/not/exist"); p != nil {
		t.Fatalf("init should have failed, got %s\n", p)
	}
}

func finishSession(p *Ctx, session SessionHandle) {
	p.Logout(session)
	p.CloseSession(session)
	p.Finalize()
	p.Destroy()
}

func TestGetInfo(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	info, err := p.GetInfo()
	if err != nil {
		t.Fatalf("non zero error %s\n", err)
	}
	if info.ManufacturerID != "SoftHSM" {
		t.Fatal("ID should be SoftHSM")
	}
	t.Logf("%+v\n", info)
}

func TestFindObject(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	tokenLabel:= "TestFindObject"

	// There are 2 keys in the db with this tag
	generateRSAKeyPair(t, p, session, tokenLabel, false)

	template := []*Attribute{NewAttribute(CKA_LABEL, tokenLabel)}
	if e := p.FindObjectsInit(session, template); e != nil {
		t.Fatalf("failed to init: %s\n", e)
	}
	obj, b, e := p.FindObjects(session, 2)
	if e != nil {
		t.Fatalf("failed to find: %s %v\n", e, b)
	}
	if e := p.FindObjectsFinal(session); e != nil {
		t.Fatalf("failed to finalize: %s\n", e)
	}
	if len(obj) != 2 {
		t.Fatal("should have found two objects")
	}
}

func TestGetAttributeValue(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	pbk, _ := generateRSAKeyPair(t, p, session, "GetAttributeValue", false)

	template := []*Attribute{
		NewAttribute(CKA_PUBLIC_EXPONENT, nil),
		NewAttribute(CKA_MODULUS_BITS, nil),
		NewAttribute(CKA_MODULUS, nil),
		NewAttribute(CKA_LABEL, nil),
	}
	attr, err := p.GetAttributeValue(session, ObjectHandle(pbk), template)
	if err != nil {
		t.Fatalf("err %s\n", err)
	}
	for i, a := range attr {
		t.Logf("attr %d, type %d, valuelen %d", i, a.Type, len(a.Value))
		if a.Type == CKA_MODULUS {
			mod := big.NewInt(0)
			mod.SetBytes(a.Value)
			t.Logf("modulus %s\n", mod.String())
		}
	}
}

func TestDigest(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)})
	if e != nil {
		t.Fatalf("DigestInit: %s\n", e)
	}

	hash, e := p.Digest(session, []byte("this is a string"))
	if e != nil {
		t.Fatalf("digest: %s\n", e)
	}
	hex := ""
	for _, d := range hash {
		hex += fmt.Sprintf("%x", d)
	}
	// Teststring create with: echo -n "this is a string" | sha1sum
	if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
		t.Fatalf("wrong digest: %s", hex)
	}
}

func TestDigestUpdate(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	if e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)}); e != nil {
		t.Fatalf("DigestInit: %s\n", e)
	}
	if e := p.DigestUpdate(session, []byte("this is ")); e != nil {
		t.Fatalf("DigestUpdate: %s\n", e)
	}
	if e := p.DigestUpdate(session, []byte("a string")); e != nil {
		t.Fatalf("DigestUpdate: %s\n", e)
	}
	hash, e := p.DigestFinal(session)
	if e != nil {
		t.Fatalf("DigestFinal: %s\n", e)
	}
	hex := ""
	for _, d := range hash {
		hex += fmt.Sprintf("%x", d)
	}
	// Teststring create with: echo -n "this is a string" | sha1sum
	if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
		t.Fatalf("wrong digest: %s", hex)
	}
}

/*
Purpose: Generate RSA keypair with a given name and persistence.
Inputs: test object
	context
	session handle
	tokenLabel: string to set as the token labels
	tokenPersistent: boolean. Whether or not the token should be
			session based or persistent. If false, the
			token will not be saved in the HSM and is
			destroyed upon termination of the session.
Outputs: creates persistent or ephemeral tokens within the HSM.
Returns: object handles for public and private keys. Fatal on error.
*/
func generateRSAKeyPair(t *testing.T, p *Ctx, session SessionHandle, tokenLabel string, tokenPersistent bool) (ObjectHandle, ObjectHandle) {
	/*
		inputs: test object, context, session handle
			tokenLabel: string to set as the token labels
			tokenPersistent: boolean. Whether or not the token should be
					session based or persistent. If false, the
					token will not be saved in the HSM and is
					destroyed upon termination of the session.
		outputs: creates persistent or ephemeral tokens within the HSM.
		returns: object handles for public and private keys.
	*/

	publicKeyTemplate := []*Attribute{
		NewAttribute(CKA_CLASS, CKO_PUBLIC_KEY),
		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
		NewAttribute(CKA_TOKEN, tokenPersistent),
		NewAttribute(CKA_VERIFY, true),
		NewAttribute(CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
		NewAttribute(CKA_MODULUS_BITS, 2048),
		NewAttribute(CKA_LABEL, tokenLabel),
	}
	privateKeyTemplate := []*Attribute{
		NewAttribute(CKA_TOKEN, tokenPersistent),
		NewAttribute(CKA_SIGN, true),
		NewAttribute(CKA_LABEL, tokenLabel),
		NewAttribute(CKA_SENSITIVE, true),
		NewAttribute(CKA_EXTRACTABLE, true),
	}
	pbk, pvk, e := p.GenerateKeyPair(session,
		[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
		publicKeyTemplate, privateKeyTemplate)
	if e != nil {
		t.Fatalf("failed to generate keypair: %s\n", e)
	}

	return pbk, pvk
}

func TestGenerateKeyPair(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	tokenLabel := "TestGenerateKeyPair"
	generateRSAKeyPair(t, p, session, tokenLabel, false)
}

func TestSign(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	tokenLabel := "TestSign"
	_, pvk := generateRSAKeyPair(t, p, session, tokenLabel, false)

	p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, pvk)
	_, e := p.Sign(session, []byte("Sign me!"))
	if e != nil {
		t.Fatalf("failed to sign: %s\n", e)
	}
}

/* destroyObject
	Purpose: destroy and object from the HSM
	Inputs: test handle
		session handle
		searchToken: String containing the token label to search for.
		class: Key type (CKO_PRIVATE_KEY or CKO_PUBLIC_KEY) to remove.
	Outputs: removes object from HSM
	Returns: Fatal error on failure.
*/
func destroyObject(t *testing.T, p *Ctx, session SessionHandle, searchToken string, class uint) (err error){
	template := []*Attribute{
		NewAttribute(CKA_LABEL, searchToken),
		NewAttribute(CKA_CLASS, class)}

	if e := p.FindObjectsInit(session, template); e != nil {
		t.Fatalf("failed to init: %s\n", e)
	}
	obj, _, e := p.FindObjects(session, 1)
	if e != nil || len(obj) == 0 {
		t.Fatalf("failed to find objects")
	}
	if e := p.FindObjectsFinal(session); e != nil {
		t.Fatalf("failed to finalize: %s\n", e)
	}

	if e := p.DestroyObject(session, obj[0]); e != nil {
		t.Fatalf("DestroyObject failed: %s\n", e)
	}
	return
}

// Create and destroy persistent keys
func TestDestroyObject(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	generateRSAKeyPair(t, p, session, "TestDestroyKey", true)
	if e := destroyObject(t, p, session, "TestDestroyKey", CKO_PUBLIC_KEY); e != nil {
		t.Fatalf("Failed to destroy object: %s\n", e)
	}
	if e := destroyObject(t, p, session, "TestDestroyKey", CKO_PRIVATE_KEY); e != nil {
		t.Fatalf("Failed to destroy object: %s\n", e)
	}

}

// ExampleSign shows how to sign some data with a private key.
// Note: error correction is not implemented in this example.
func ExampleSign() {
	lib := "/usr/lib/softhsm/libsofthsm.so"
	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
		lib = x
	}
	p := New(lib)
	if p == nil {
		log.Fatal("Failed to init lib")
	}

	p.Initialize()
	defer p.Destroy()
	defer p.Finalize()
	slots, _ := p.GetSlotList(true)
	session, _ := p.OpenSession(slots[0], CKF_SERIAL_SESSION|CKF_RW_SESSION)
	defer p.CloseSession(session)
	p.Login(session, CKU_USER, "1234")
	defer p.Logout(session)
	publicKeyTemplate := []*Attribute{
		NewAttribute(CKA_CLASS, CKO_PUBLIC_KEY),
		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
		NewAttribute(CKA_TOKEN, false),
		NewAttribute(CKA_ENCRYPT, true),
		NewAttribute(CKA_PUBLIC_EXPONENT, []byte{3}),
		NewAttribute(CKA_MODULUS_BITS, 1024),
		NewAttribute(CKA_LABEL, "ExampleSign"),
	}
	privateKeyTemplate := []*Attribute{
		NewAttribute(CKA_CLASS, CKO_PRIVATE_KEY),
		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
		NewAttribute(CKA_TOKEN, false),
		NewAttribute(CKA_PRIVATE, true),
		NewAttribute(CKA_SIGN, true),
		NewAttribute(CKA_LABEL, "ExampleSign"),
	}
	_, priv, err := p.GenerateKeyPair(session,
		[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
		publicKeyTemplate, privateKeyTemplate)
	if err != nil {
		log.Fatal(err)
	}
	p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, priv)
	// Sign something with the private key.
	data := []byte("Lets sign this data")

	_, err = p.Sign(session, data)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("It works!")
	// Output: It works!
}
// Copyright 2013 Miek Gieben. All rights reserved.
<commit_msg>fix 'go test' error with go1.10 RC, gofmt the code (#62)<commit_after>// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pkcs11

// These tests depend on SoftHSM and the library being in
// in /usr/lib/softhsm/libsofthsm.so

import (
	"fmt"
	"log"
	"math/big"
	"os"
	"testing"
)

/*
This test supports the following environment variables:

* SOFTHSM_LIB: complete path to libsofthsm.so
* SOFTHSM_TOKENLABEL
* SOFTHSM_PRIVKEYLABEL
* SOFTHSM_PIN
*/

func setenv(t *testing.T) *Ctx {
	lib := "/usr/lib/softhsm/libsofthsm.so"
	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
		lib = x
	}
	t.Logf("loading %s", lib)
	p := New(lib)
	if p == nil {
		t.Fatal("Failed to init lib")
	}
	return p
}

func TestSetenv(t *testing.T) {
	wd, _ := os.Getwd()
	os.Setenv("SOFTHSM_CONF", wd+"/softhsm.conf")

	lib := "/usr/lib/softhsm/libsofthsm.so"
	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
		lib = x
	}
	p := New(lib)
	if p == nil {
		t.Fatal("Failed to init pkcs11")
	}
	p.Destroy()
	return
}

func getSession(p *Ctx, t *testing.T) SessionHandle {
	if e := p.Initialize(); e != nil {
		t.Fatalf("init error %s\n", e)
	}
	slots, e := p.GetSlotList(true)
	if e != nil {
		t.Fatalf("slots %s\n", e)
	}
	session, e := p.OpenSession(slots[0], CKF_SERIAL_SESSION|CKF_RW_SESSION)
	if e != nil {
		t.Fatalf("session %s\n", e)
	}
	if e := p.Login(session, CKU_USER, pin); e != nil {
		t.Fatalf("user pin %s\n", e)
	}
	return session
}

func TestInitialize(t *testing.T) {
	p := setenv(t)
	if e := p.Initialize(); e != nil {
		t.Fatalf("init error %s\n", e)
	}
	p.Finalize()
	p.Destroy()
}

func TestNew(t *testing.T) {
	if p := New(""); p != nil {
		t.Fatalf("init should have failed, got %v\n", p)
	}
	if p := New("/does/not/exist"); p != nil {
		t.Fatalf("init should have failed, got %v\n", p)
	}
}

func finishSession(p *Ctx, session SessionHandle) {
	p.Logout(session)
	p.CloseSession(session)
	p.Finalize()
	p.Destroy()
}

func TestGetInfo(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	info, err := p.GetInfo()
	if err != nil {
		t.Fatalf("non zero error %s\n", err)
	}
	if info.ManufacturerID != "SoftHSM" {
		t.Fatal("ID should be SoftHSM")
	}
	t.Logf("%+v\n", info)
}

func TestFindObject(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	tokenLabel := "TestFindObject"

	// There are 2 keys in the db with this tag
	generateRSAKeyPair(t, p, session, tokenLabel, false)

	template := []*Attribute{NewAttribute(CKA_LABEL, tokenLabel)}
	if e := p.FindObjectsInit(session, template); e != nil {
		t.Fatalf("failed to init: %s\n", e)
	}
	obj, b, e := p.FindObjects(session, 2)
	if e != nil {
		t.Fatalf("failed to find: %s %v\n", e, b)
	}
	if e := p.FindObjectsFinal(session); e != nil {
		t.Fatalf("failed to finalize: %s\n", e)
	}
	if len(obj) != 2 {
		t.Fatal("should have found two objects")
	}
}

func TestGetAttributeValue(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	pbk, _ := generateRSAKeyPair(t, p, session, "GetAttributeValue", false)

	template := []*Attribute{
		NewAttribute(CKA_PUBLIC_EXPONENT, nil),
		NewAttribute(CKA_MODULUS_BITS, nil),
		NewAttribute(CKA_MODULUS, nil),
		NewAttribute(CKA_LABEL, nil),
	}
	attr, err := p.GetAttributeValue(session, ObjectHandle(pbk), template)
	if err != nil {
		t.Fatalf("err %s\n", err)
	}
	for i, a := range attr {
		t.Logf("attr %d, type %d, valuelen %d", i, a.Type, len(a.Value))
		if a.Type == CKA_MODULUS {
			mod := big.NewInt(0)
			mod.SetBytes(a.Value)
			t.Logf("modulus %s\n", mod.String())
		}
	}
}

func TestDigest(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)})
	if e != nil {
		t.Fatalf("DigestInit: %s\n", e)
	}

	hash, e := p.Digest(session, []byte("this is a string"))
	if e != nil {
		t.Fatalf("digest: %s\n", e)
	}
	hex := ""
	for _, d := range hash {
		hex += fmt.Sprintf("%x", d)
	}
	// Teststring create with: echo -n "this is a string" | sha1sum
	if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
		t.Fatalf("wrong digest: %s", hex)
	}
}

func TestDigestUpdate(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	if e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)}); e != nil {
		t.Fatalf("DigestInit: %s\n", e)
	}
	if e := p.DigestUpdate(session, []byte("this is ")); e != nil {
		t.Fatalf("DigestUpdate: %s\n", e)
	}
	if e := p.DigestUpdate(session, []byte("a string")); e != nil {
		t.Fatalf("DigestUpdate: %s\n", e)
	}
	hash, e := p.DigestFinal(session)
	if e != nil {
		t.Fatalf("DigestFinal: %s\n", e)
	}
	hex := ""
	for _, d := range hash {
		hex += fmt.Sprintf("%x", d)
	}
	// Teststring create with: echo -n "this is a string" | sha1sum
	if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
		t.Fatalf("wrong digest: %s", hex)
	}
}

/*
Purpose: Generate RSA keypair with a given name and persistence.
Inputs: test object
	context
	session handle
	tokenLabel: string to set as the token labels
	tokenPersistent: boolean. Whether or not the token should be
			session based or persistent. If false, the
			token will not be saved in the HSM and is
			destroyed upon termination of the session.
Outputs: creates persistent or ephemeral tokens within the HSM.
Returns: object handles for public and private keys. Fatal on error.
*/
func generateRSAKeyPair(t *testing.T, p *Ctx, session SessionHandle, tokenLabel string, tokenPersistent bool) (ObjectHandle, ObjectHandle) {
	/*
		inputs: test object, context, session handle
			tokenLabel: string to set as the token labels
			tokenPersistent: boolean. Whether or not the token should be
					session based or persistent. If false, the
					token will not be saved in the HSM and is
					destroyed upon termination of the session.
		outputs: creates persistent or ephemeral tokens within the HSM.
		returns: object handles for public and private keys.
	*/

	publicKeyTemplate := []*Attribute{
		NewAttribute(CKA_CLASS, CKO_PUBLIC_KEY),
		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
		NewAttribute(CKA_TOKEN, tokenPersistent),
		NewAttribute(CKA_VERIFY, true),
		NewAttribute(CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
		NewAttribute(CKA_MODULUS_BITS, 2048),
		NewAttribute(CKA_LABEL, tokenLabel),
	}
	privateKeyTemplate := []*Attribute{
		NewAttribute(CKA_TOKEN, tokenPersistent),
		NewAttribute(CKA_SIGN, true),
		NewAttribute(CKA_LABEL, tokenLabel),
		NewAttribute(CKA_SENSITIVE, true),
		NewAttribute(CKA_EXTRACTABLE, true),
	}
	pbk, pvk, e := p.GenerateKeyPair(session,
		[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
		publicKeyTemplate, privateKeyTemplate)
	if e != nil {
		t.Fatalf("failed to generate keypair: %s\n", e)
	}

	return pbk, pvk
}

func TestGenerateKeyPair(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)
	tokenLabel := "TestGenerateKeyPair"
	generateRSAKeyPair(t, p, session, tokenLabel, false)
}

func TestSign(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	tokenLabel := "TestSign"
	_, pvk := generateRSAKeyPair(t, p, session, tokenLabel, false)

	p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, pvk)
	_, e := p.Sign(session, []byte("Sign me!"))
	if e != nil {
		t.Fatalf("failed to sign: %s\n", e)
	}
}

/* destroyObject
Purpose: destroy and object from the HSM
Inputs: test handle
	session handle
	searchToken: String containing the token label to search for.
	class: Key type (CKO_PRIVATE_KEY or CKO_PUBLIC_KEY) to remove.
Outputs: removes object from HSM
Returns: Fatal error on failure.
*/
func destroyObject(t *testing.T, p *Ctx, session SessionHandle, searchToken string, class uint) (err error) {
	template := []*Attribute{
		NewAttribute(CKA_LABEL, searchToken),
		NewAttribute(CKA_CLASS, class)}

	if e := p.FindObjectsInit(session, template); e != nil {
		t.Fatalf("failed to init: %s\n", e)
	}
	obj, _, e := p.FindObjects(session, 1)
	if e != nil || len(obj) == 0 {
		t.Fatalf("failed to find objects")
	}
	if e := p.FindObjectsFinal(session); e != nil {
		t.Fatalf("failed to finalize: %s\n", e)
	}

	if e := p.DestroyObject(session, obj[0]); e != nil {
		t.Fatalf("DestroyObject failed: %s\n", e)
	}
	return
}

// Create and destroy persistent keys
func TestDestroyObject(t *testing.T) {
	p := setenv(t)
	session := getSession(p, t)
	defer finishSession(p, session)

	generateRSAKeyPair(t, p, session, "TestDestroyKey", true)
	if e := destroyObject(t, p, session, "TestDestroyKey", CKO_PUBLIC_KEY); e != nil {
		t.Fatalf("Failed to destroy object: %s\n", e)
	}
	if e := destroyObject(t, p, session, "TestDestroyKey", CKO_PRIVATE_KEY); e != nil {
		t.Fatalf("Failed to destroy object: %s\n", e)
	}

}

// ExampleSign shows how to sign some data with a private key.
// Note: error correction is not implemented in this example.
func ExampleSign() {
	lib := "/usr/lib/softhsm/libsofthsm.so"
	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
		lib = x
	}
	p := New(lib)
	if p == nil {
		log.Fatal("Failed to init lib")
	}

	p.Initialize()
	defer p.Destroy()
	defer p.Finalize()
	slots, _ := p.GetSlotList(true)
	session, _ := p.OpenSession(slots[0], CKF_SERIAL_SESSION|CKF_RW_SESSION)
	defer p.CloseSession(session)
	p.Login(session, CKU_USER, "1234")
	defer p.Logout(session)
	publicKeyTemplate := []*Attribute{
		NewAttribute(CKA_CLASS, CKO_PUBLIC_KEY),
		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
		NewAttribute(CKA_TOKEN, false),
		NewAttribute(CKA_ENCRYPT, true),
		NewAttribute(CKA_PUBLIC_EXPONENT, []byte{3}),
		NewAttribute(CKA_MODULUS_BITS, 1024),
		NewAttribute(CKA_LABEL, "ExampleSign"),
	}
	privateKeyTemplate := []*Attribute{
		NewAttribute(CKA_CLASS, CKO_PRIVATE_KEY),
		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
		NewAttribute(CKA_TOKEN, false),
		NewAttribute(CKA_PRIVATE, true),
		NewAttribute(CKA_SIGN, true),
		NewAttribute(CKA_LABEL, "ExampleSign"),
	}
	_, priv, err := p.GenerateKeyPair(session,
		[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
		publicKeyTemplate, privateKeyTemplate)
	if err != nil {
		log.Fatal(err)
	}
	p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, priv)
	// Sign something with the private key.
	data := []byte("Lets sign this data")

	_, err = p.Sign(session, data)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("It works!")
	// Output: It works!
}

// Copyright 2013 Miek Gieben. All rights reserved.
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"os"

	"github.com/inconshreveable/log15"
)

type log struct {
	log15.Logger
}

var (
	// Log Is the global log variable.
	//Log = log15.New()
	Log = newLog()

	// stdHandler is the log handler with level applied
	stdHandler = log15.StreamHandler(os.Stdout, log15.LogfmtFormat())

	// Default handler used in the package.
	defaultHandler log15.Handler
)

func newLog() *log {
	return &log{
		Logger: log15.New(),
	}
}

func init() {
	SetDefaultLogHandler(log15.LvlFilterHandler(log15.LvlInfo, stdHandler))
	Log.SetHandler(defaultHandler)
}

// NewLog creates a new instance of the logger using the current default handler
// for its output.
func NewLog(ctx ...interface{}) log15.Logger {
	l := log15.New(ctx...)
	l.SetHandler(defaultHandler)
	return l
}

// Errorf will write a formatted Error to the default log.
func (l *log) Errorf(format string, args ...interface{}) {
	l.Error(fmt.Sprintf(format, args...))
}

// Debugf will write a formatted Debug to the default log.
func (l *log) Debugf(format string, args ...interface{}) {
	l.Debug(fmt.Sprintf(format, args...))
}

// Critf will write a formatted Crit to the default log.
func (l *log) Critf(format string, args ...interface{}) {
	l.Crit(fmt.Sprintf(format, args...))
}

// Infof will write a formatted Info to the default log.
func (l *log) Infof(format string, args ...interface{}) {
	l.Info(fmt.Sprintf(format, args...))
}

// Warnf will write a formatted Warn to the default log.
func (l *log) Warnf(format string, args ...interface{}) {
	l.Warn(fmt.Sprintf(format, args...))
}

// Logf is short hand to create a message string using fmt.Sprintf.
func Logf(format string, args ...interface{}) string {
	return fmt.Sprintf(format, args...)
}

// SetDefaultLogHandler sets the handler for the logger. It wraps handlers in a SyncHandler. You
// should not pass in handlers that are already wrapped in a SyncHandler.
func SetDefaultLogHandler(handler log15.Handler) {
	defaultHandler = log15.SyncHandler(handler)
	Log.SetHandler(defaultHandler)
}

// Sets a new log level for the global logging and the default handler.
func SetLogLvl(lvl log15.Lvl) {
	SetDefaultLogHandler(log15.LvlFilterHandler(lvl, stdHandler))
	Log.SetHandler(defaultHandler)
}

// DefaultLogHandler returns the current handler. It can be used to create additional
// logger instances that all use the same handler for output.
func DefaultLogHandler() log15.Handler {
	return defaultHandler
}
<commit_msg>Remove commented out code.<commit_after>package app

import (
	"fmt"
	"os"

	"github.com/inconshreveable/log15"
)

type log struct {
	log15.Logger
}

var (
	// Log Is the global log variable.
	Log = newLog()

	// stdHandler is the log handler with level applied
	stdHandler = log15.StreamHandler(os.Stdout, log15.LogfmtFormat())

	// Default handler used in the package.
	defaultHandler log15.Handler
)

func newLog() *log {
	return &log{
		Logger: log15.New(),
	}
}

func init() {
	SetDefaultLogHandler(log15.LvlFilterHandler(log15.LvlInfo, stdHandler))
	Log.SetHandler(defaultHandler)
}

// NewLog creates a new instance of the logger using the current default handler
// for its output.
func NewLog(ctx ...interface{}) log15.Logger {
	l := log15.New(ctx...)
	l.SetHandler(defaultHandler)
	return l
}

// Errorf will write a formatted Error to the default log.
func (l *log) Errorf(format string, args ...interface{}) {
	l.Error(fmt.Sprintf(format, args...))
}

// Debugf will write a formatted Debug to the default log.
func (l *log) Debugf(format string, args ...interface{}) {
	l.Debug(fmt.Sprintf(format, args...))
}

// Critf will write a formatted Crit to the default log.
func (l *log) Critf(format string, args ...interface{}) {
	l.Crit(fmt.Sprintf(format, args...))
}

// Infof will write a formatted Info to the default log.
func (l *log) Infof(format string, args ...interface{}) {
	l.Info(fmt.Sprintf(format, args...))
}

// Warnf will write a formatted Warn to the default log.
func (l *log) Warnf(format string, args ...interface{}) {
	l.Warn(fmt.Sprintf(format, args...))
}

// Logf is short hand to create a message string using fmt.Sprintf.
func Logf(format string, args ...interface{}) string {
	return fmt.Sprintf(format, args...)
}

// SetDefaultLogHandler sets the handler for the logger. It wraps handlers in a SyncHandler. You
// should not pass in handlers that are already wrapped in a SyncHandler.
func SetDefaultLogHandler(handler log15.Handler) {
	defaultHandler = log15.SyncHandler(handler)
	Log.SetHandler(defaultHandler)
}

// Sets a new log level for the global logging and the default handler.
func SetLogLvl(lvl log15.Lvl) {
	SetDefaultLogHandler(log15.LvlFilterHandler(lvl, stdHandler))
	Log.SetHandler(defaultHandler)
}

// DefaultLogHandler returns the current handler. It can be used to create additional
// logger instances that all use the same handler for output.
func DefaultLogHandler() log15.Handler {
	return defaultHandler
}
<|endoftext|>


<commit_before>package tls

import (
	"context"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/rancher/dynamiclistener"
	"github.com/rancher/dynamiclistener/cert"
	"github.com/rancher/dynamiclistener/server"
	"github.com/rancher/dynamiclistener/storage/kubernetes"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/wrangler-api/pkg/generated/controllers/core"
	corev1controllers "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1"
	"github.com/rancher/wrangler/pkg/data"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
)

const (
	rancherCertFile    = "/etc/rancher/ssl/cert.pem"
	rancherKeyFile     = "/etc/rancher/ssl/key.pem"
	rancherCACertsFile = "/etc/rancher/ssl/cacerts.pem"
)

func migrateCA(restConfig *rest.Config) (*core.Factory, error) {
	core, err := core.NewFactoryFromConfig(restConfig)
	if err != nil {
		return nil, err
	}

	if _, err := core.Core().V1().Secret().Get("cattle-system", "serving-ca", metav1.GetOptions{}); err == nil {
		return core, nil
	}

	dc, err := dynamic.NewForConfig(restConfig)
	if err != nil {
		return nil, err
	}

	listenClient := dc.Resource(schema.GroupVersionResource{
		Group:    "management.cattle.io",
		Version:  "v3",
		Resource: "listenconfigs",
	})
	obj, err := listenClient.Get("cli-config", metav1.GetOptions{})
	if err != nil {
		return core, nil
	}

	caCert := data.Object(obj.Object).String("caCert")
	caKey := data.Object(obj.Object).String("caKey")

	if len(caCert) == 0 || len(caKey) == 0 {
		return core, nil
	}

	_, err = core.Core().V1().Secret().Create(&v1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "serving-ca",
			Namespace: "cattle-system",
		},
		Data: map[string][]byte{
			v1.TLSCertKey:       []byte(caCert),
			v1.TLSPrivateKeyKey: []byte(caKey),
		},
		StringData: nil,
		Type:       v1.SecretTypeTLS,
	})
	return core, err
}

func ListenAndServe(ctx context.Context, restConfig *rest.Config, handler http.Handler, httpsPort, httpPort int, acmeDomains []string, noCACerts bool) error {
	restConfig = rest.CopyConfig(restConfig)
	restConfig.Timeout = 10 * time.Minute

	core, err := migrateCA(restConfig)
	if err != nil {
		return err
	}

	opts, err := SetupListener(core.Core().V1().Secret(), acmeDomains, noCACerts)
	if err != nil {
		return err
	}

	if err := server.ListenAndServe(ctx, httpsPort, httpPort, handler, opts); err != nil {
		return err
	}

	if err := core.Start(ctx, 5); err != nil {
		return err
	}

	<-ctx.Done()
	return ctx.Err()

}

func SetupListener(secrets corev1controllers.SecretController, acmeDomains []string, noCACerts bool) (*server.ListenOpts, error) {
	caForAgent, opts, err := readConfig(secrets, acmeDomains, noCACerts)
	if err != nil {
		return nil, err
	}

	if noCACerts {
		caForAgent = ""
	} else if caForAgent == "" {
		caCert, caKey, err := kubernetes.LoadOrGenCA(secrets, opts.CANamespace, opts.CAName)
		if err != nil {
			return nil, err
		}
		caForAgent = string(cert.EncodeCertPEM(caCert))
		opts.CA = caCert
		opts.CAKey = caKey
	}

	caForAgent = strings.TrimSpace(caForAgent)
	if settings.CACerts.Get() != caForAgent {
		if err := settings.CACerts.Set(caForAgent); err != nil {
			return nil, err
		}
	}

	return opts, nil
}

func readConfig(secrets corev1controllers.SecretController, acmeDomains []string, noCACerts bool) (string, *server.ListenOpts, error) {
	var (
		ca  string
		err error
	)

	tlsConfig, err := BaseTLSConfig()
	if err != nil {
		return "", nil, err
	}

	expiration, err := strconv.Atoi(settings.RotateCertsIfExpiringInDays.Get())
	if err != nil {
		return "", nil, errors.Wrapf(err, "parsing %s", settings.RotateCertsIfExpiringInDays.Get())
	}

	sans := []string{"localhost", "127.0.0.1"}
	ip, err := net.ChooseHostInterface()
	if err == nil {
		sans = append(sans, ip.String())
	}

	opts := &server.ListenOpts{
		Secrets:       secrets,
		CAName:        "serving-ca",
		CANamespace:   "cattle-system",
		CertNamespace: "cattle-system",
		AcmeDomains:   acmeDomains,
		TLSListenerConfig: dynamiclistener.Config{
			TLSConfig:           tlsConfig,
			ExpirationDaysCheck: expiration,
			SANs:                sans,
		},
	}

	// ACME / Let's Encrypt
	// If --acme-domain is set, configure and return
	if len(acmeDomains) > 0 {
		return "", opts, nil
	}

	// Mounted certificates
	// If certificate file/key are set
	certFileExists := fileExists(rancherCertFile)
	keyFileExists := fileExists(rancherKeyFile)

	// If certificate file exists but not certificate key, or other way around, error out
	if (certFileExists && !keyFileExists) || (!certFileExists && keyFileExists) {
		return "", nil, fmt.Errorf("invalid SSL configuration found, please set both certificate file and certificate key file (one is missing)")
	}

	caFileExists := fileExists(rancherCACertsFile)

	// If certificate file and certificate key file exists, load files into listenConfig
	if certFileExists && keyFileExists {
		cert, err := tls.LoadX509KeyPair(rancherCertFile, rancherKeyFile)
		if err != nil {
			return "", nil, err
		}
		opts.TLSListenerConfig.TLSConfig.Certificates = []tls.Certificate{cert}

		// Selfsigned needs cacerts, recognized CA needs --no-cacerts but can't be used together
		if (caFileExists && noCACerts) || (!caFileExists && !noCACerts) {
			return "", nil, fmt.Errorf("invalid SSL configuration found, please set cacerts when using self signed certificates or use --no-cacerts when using certificates from a recognized Certificate Authority, do not use both at the same time")
		}
		// Load cacerts if exists
		if caFileExists {
			ca, err = readPEM(rancherCACertsFile)
			if err != nil {
				return "", nil, err
			}
		}
		return ca, opts, nil
	}

	// External termination
	// We need to check if cacerts is passed or if --no-cacerts is used (when not providing certificate file and key)
	// If cacerts is passed
	if caFileExists {
		// We can't have --no-cacerts
		if noCACerts {
			return "", nil, fmt.Errorf("invalid SSL configuration found, please set cacerts when using self signed certificates or use --no-cacerts when using certificates from a recognized Certificate Authority, do not use both at the same time")
		}
		ca, err = readPEM(rancherCACertsFile)
		if err != nil {
			return "", nil, err
		}
	}

	// No certificates mounted or only --no-cacerts used
	return ca, opts, nil
}

func fileExists(path string) bool {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func readPEM(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}

	return string(content), nil
}
<commit_msg>Fix HA TLS<commit_after>package tls

import (
	"context"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/rancher/dynamiclistener"
	"github.com/rancher/dynamiclistener/cert"
	"github.com/rancher/dynamiclistener/server"
	"github.com/rancher/dynamiclistener/storage/kubernetes"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/wrangler-api/pkg/generated/controllers/core"
	corev1controllers "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/client-go/rest"
)

const (
	rancherCertFile    = "/etc/rancher/ssl/cert.pem"
	rancherKeyFile     = "/etc/rancher/ssl/key.pem"
	rancherCACertsFile = "/etc/rancher/ssl/cacerts.pem"
)

func ListenAndServe(ctx context.Context, restConfig *rest.Config, handler http.Handler, httpsPort, httpPort int, acmeDomains []string, noCACerts bool) error {
	restConfig = rest.CopyConfig(restConfig)
	restConfig.Timeout = 10 * time.Minute

	core, err := core.NewFactoryFromConfig(restConfig)
	if err != nil {
		return err
	}

	opts, err := SetupListener(core.Core().V1().Secret(), acmeDomains, noCACerts)
	if err != nil {
		return err
	}

	if err := server.ListenAndServe(ctx, httpsPort, httpPort, handler, opts); err != nil {
		return err
	}

	if err := core.Start(ctx, 5); err != nil {
		return err
	}

	<-ctx.Done()
	return ctx.Err()

}

func SetupListener(secrets corev1controllers.SecretController, acmeDomains []string, noCACerts bool) (*server.ListenOpts, error) {
	caForAgent, opts, err := readConfig(secrets, acmeDomains, noCACerts)
	if err != nil {
		return nil, err
	}

	if noCACerts {
		caForAgent = ""
	} else if caForAgent == "" {
		caCert, caKey, err := kubernetes.LoadOrGenCA(secrets, opts.CANamespace, opts.CAName)
		if err != nil {
			return nil, err
		}
		caForAgent = string(cert.EncodeCertPEM(caCert))
		opts.CA = caCert
		opts.CAKey = caKey
	}

	caForAgent = strings.TrimSpace(caForAgent)
	if settings.CACerts.Get() != caForAgent {
		if err := settings.CACerts.Set(caForAgent); err != nil {
			return nil, err
		}
	}

	return opts, nil
}

func readConfig(secrets corev1controllers.SecretController, acmeDomains []string, noCACerts bool) (string, *server.ListenOpts, error) {
	var (
		ca  string
		err error
	)

	tlsConfig, err := BaseTLSConfig()
	if err != nil {
		return "", nil, err
	}

	expiration, err := strconv.Atoi(settings.RotateCertsIfExpiringInDays.Get())
	if err != nil {
		return "", nil, errors.Wrapf(err, "parsing %s", settings.RotateCertsIfExpiringInDays.Get())
	}

	sans := []string{"localhost", "127.0.0.1"}
	ip, err := net.ChooseHostInterface()
	if err == nil {
		sans = append(sans, ip.String())
	}

	opts := &server.ListenOpts{
		Secrets:       secrets,
		CAName:        "tls-rancher",
		CANamespace:   "cattle-system",
		CertNamespace: "cattle-system",
		AcmeDomains:   acmeDomains,
		TLSListenerConfig: dynamiclistener.Config{
			TLSConfig:           tlsConfig,
			ExpirationDaysCheck: expiration,
			SANs:                sans,
		},
	}

	// ACME / Let's Encrypt
	// If --acme-domain is set, configure and return
	if len(acmeDomains) > 0 {
		return "", opts, nil
	}

	// Mounted certificates
	// If certificate file/key are set
	certFileExists := fileExists(rancherCertFile)
	keyFileExists := fileExists(rancherKeyFile)

	// If certificate file exists but not certificate key, or other way around, error out
	if (certFileExists && !keyFileExists) || (!certFileExists && keyFileExists) {
		return "", nil, fmt.Errorf("invalid SSL configuration found, please set both certificate file and certificate key file (one is missing)")
	}

	caFileExists := fileExists(rancherCACertsFile)

	// If certificate file and certificate key file exists, load files into listenConfig
	if certFileExists && keyFileExists {
		cert, err := tls.LoadX509KeyPair(rancherCertFile, rancherKeyFile)
		if err != nil {
			return "", nil, err
		}
		opts.TLSListenerConfig.TLSConfig.Certificates = []tls.Certificate{cert}

		// Selfsigned needs cacerts, recognized CA needs --no-cacerts but can't be used together
		if (caFileExists && noCACerts) || (!caFileExists && !noCACerts) {
			return "", nil, fmt.Errorf("invalid SSL configuration found, please set cacerts when using self signed certificates or use --no-cacerts when using certificates from a recognized Certificate Authority, do not use both at the same time")
		}
		// Load cacerts if exists
		if caFileExists {
			ca, err = readPEM(rancherCACertsFile)
			if err != nil {
				return "", nil, err
			}
		}
		return ca, opts, nil
	}

	// External termination
	// We need to check if cacerts is passed or if --no-cacerts is used (when not providing certificate file and key)
	// If cacerts is passed
	if caFileExists {
		// We can't have --no-cacerts
		if noCACerts {
			return "", nil, fmt.Errorf("invalid SSL configuration found, please set cacerts when using self signed certificates or use --no-cacerts when using certificates from a recognized Certificate Authority, do not use both at the same time")
		}
		ca, err = readPEM(rancherCACertsFile)
		if err != nil {
			return "", nil, err
		}
	}

	// No certificates mounted or only --no-cacerts used
	return ca, opts, nil
}

func fileExists(path string) bool {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func readPEM(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}

	return string(content), nil
}
<|endoftext|>


<commit_before>package i18n

import (
	"fmt"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/admin"
	"github.com/qor/qor/utils"
	"github.com/theplant/cldr"
)

var Default = "en-US"

type I18n struct {
	scope        string
	Backends     []Backend
	Translations map[string]map[string]*Translation
}

type Backend interface {
	LoadTranslations() []*Translation
	SaveTranslation(*Translation)
	DeleteTranslation(*Translation)
}

type Translation struct {
	Key     string
	Locale  string
	Value   string
	Backend Backend
}

func New(backends ...Backend) *I18n {
	i18n := &I18n{Backends: backends, Translations: map[string]map[string]*Translation{}}
	for _, backend := range backends {
		for _, translation := range backend.LoadTranslations() {
			translation.Backend = backend
			i18n.AddTransaltion(translation)
		}
	}
	return i18n
}

func (i18n *I18n) AddTransaltion(translation *Translation) {
	if i18n.Translations[translation.Locale] == nil {
		i18n.Translations[translation.Locale] = map[string]*Translation{}
	}
	i18n.Translations[translation.Locale][translation.Key] = translation
}

func (i18n *I18n) SaveTransaltion(translation *Translation) {
	if i18n.Translations[translation.Locale] == nil {
		i18n.Translations[translation.Locale] = map[string]*Translation{}
	}
	i18n.Translations[translation.Locale][translation.Key] = translation
	if backend := translation.Backend; backend != nil {
		backend.SaveTranslation(translation)
	}
}

func (i18n *I18n) DeleteTransaltion(translation *Translation) {
	delete(i18n.Translations[translation.Locale], translation.Key)
	translation.Backend.DeleteTranslation(translation)
}

func (i18n *I18n) Scope(scope string) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: scope, Backends: i18n.Backends}
}

func (i18n *I18n) T(locale, key string, args ...interface{}) string {
	var value string
	var translationKey = key
	if i18n.scope != "" {
		translationKey = strings.Join([]string{i18n.scope, key}, ".")
	}

	if translations := i18n.Translations[locale]; translations != nil && translations[translationKey] != nil {
		value = translations[translationKey].Value
	} else {
		// Save translations
		i18n.SaveTransaltion(&Translation{Key: translationKey, Locale: locale, Backend: i18n.Backends[0]})
	}

	if value == "" {
		// Get default translation if not translated
		if translations := i18n.Translations[Default]; translations != nil && translations[translationKey] != nil {
			value = translations[translationKey].Value
		}
		if value == "" {
			value = key
		}
	}

	if str, err := cldr.Parse(locale, value, args...); err == nil {
		return str
	}
	return value
}

func getLocaleFromContext(context *qor.Context) string {
	if locale := utils.GetLocale(context); locale != "" {
		return locale
	}

	return Default
}

type availableLocalesInterface interface {
	AvailableLocales() []string
}

type viewableLocalesInterface interface {
	ViewableLocales() []string
}

type editableLocalesInterface interface {
	EditableLocales() []string
}

func getAvailableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(viewableLocalesInterface); ok {
		return user.ViewableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{}
}

func getEditableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(editableLocalesInterface); ok {
		return user.EditableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{}
}

func (i18n *I18n) InjectQorAdmin(res *admin.Resource) {
	res.UseTheme("i18n")
	res.GetAdmin().I18n = i18n
	res.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB { return context.GetDB() }

	res.GetAdmin().RegisterFuncMap("lt", func(locale, key string, withDefault bool) string {
		translations := i18n.Translations[locale]
		if (translations == nil) && withDefault {
			translations = i18n.Translations[Default]
		}

		if translation := translations[key]; translation != nil {
			return translation.Value
		}

		return ""
	})

	res.GetAdmin().RegisterFuncMap("i18n_available_keys", func(context *admin.Context) (keys []string) {
		translations := i18n.Translations[Default]
		if translations == nil {
			for _, values := range i18n.Translations {
				translations = values
				break
			}
		}

		keyword := context.Request.URL.Query().Get("keyword")

		for key, translation := range translations {
			if (keyword == "") || (strings.Index(strings.ToLower(translation.Key), strings.ToLower(keyword)) != -1 ||
				strings.Index(strings.ToLower(translation.Value), keyword) != -1) {
				keys = append(keys, key)
			}
		}

		sort.Strings(keys)

		pagination := context.Searcher.Pagination
		pagination.Total = len(keys)
		pagination.PrePage = 25
		pagination.CurrentPage, _ = strconv.Atoi(context.Request.URL.Query().Get("page"))
		if pagination.CurrentPage == 0 {
			pagination.CurrentPage = 1
		}
		if pagination.CurrentPage > 0 {
			pagination.Pages = pagination.Total / pagination.PrePage
		}
		context.Searcher.Pagination = pagination

		if pagination.CurrentPage == -1 {
			return keys
		}

		lastIndex := pagination.CurrentPage * pagination.PrePage
		if pagination.Total < lastIndex {
			lastIndex = pagination.Total
		}

		return keys[(pagination.CurrentPage-1)*pagination.PrePage : lastIndex]
	})

	res.GetAdmin().RegisterFuncMap("i18n_primary_locale", func(context admin.Context) string {
		if locale := context.Request.Form.Get("primary_locale"); locale != "" {
			return locale
		}
		return getAvailableLocales(context.Request, context.CurrentUser)[0]
	})

	res.GetAdmin().RegisterFuncMap("i18n_editing_locale", func(context admin.Context) string {
		if locale := context.Request.Form.Get("to_locale"); locale != "" {
			return locale
		}
		return getLocaleFromContext(context.Context)
	})

	res.GetAdmin().RegisterFuncMap("i18n_viewable_locales", func(context admin.Context) []string {
		return getAvailableLocales(context.Request, context.CurrentUser)
	})

	res.GetAdmin().RegisterFuncMap("i18n_editable_locales", func(context admin.Context) []string {
		return getEditableLocales(context.Request, context.CurrentUser)
	})

	controller := i18nController{i18n}
	router := res.GetAdmin().GetRouter()
	router.Get(fmt.Sprintf("^/%v", res.ToParam()), controller.Index)
	router.Post(fmt.Sprintf("^/%v", res.ToParam()), controller.Update)
	router.Put(fmt.Sprintf("^/%v", res.ToParam()), controller.Update)

	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/qor/i18n/views"))
	}
}
<commit_msg>Save translation key as value for default locale<commit_after>package i18n

import (
	"fmt"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/admin"
	"github.com/qor/qor/utils"
	"github.com/theplant/cldr"
)

var Default = "en-US"

type I18n struct {
	scope        string
	Backends     []Backend
	Translations map[string]map[string]*Translation
}

type Backend interface {
	LoadTranslations() []*Translation
	SaveTranslation(*Translation)
	DeleteTranslation(*Translation)
}

type Translation struct {
	Key     string
	Locale  string
	Value   string
	Backend Backend
}

func New(backends ...Backend) *I18n {
	i18n := &I18n{Backends: backends, Translations: map[string]map[string]*Translation{}}
	for _, backend := range backends {
		for _, translation := range backend.LoadTranslations() {
			translation.Backend = backend
			i18n.AddTransaltion(translation)
		}
	}
	return i18n
}

func (i18n *I18n) AddTransaltion(translation *Translation) {
	if i18n.Translations[translation.Locale] == nil {
		i18n.Translations[translation.Locale] = map[string]*Translation{}
	}
	i18n.Translations[translation.Locale][translation.Key] = translation
}

func (i18n *I18n) SaveTransaltion(translation *Translation) {
	if i18n.Translations[translation.Locale] == nil {
		i18n.Translations[translation.Locale] = map[string]*Translation{}
	}
	i18n.Translations[translation.Locale][translation.Key] = translation
	if backend := translation.Backend; backend != nil {
		backend.SaveTranslation(translation)
	}
}

func (i18n *I18n) DeleteTransaltion(translation *Translation) {
	delete(i18n.Translations[translation.Locale], translation.Key)
	translation.Backend.DeleteTranslation(translation)
}

func (i18n *I18n) Scope(scope string) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: scope, Backends: i18n.Backends}
}

func (i18n *I18n) T(locale, key string, args ...interface{}) string {
	var value string
	var translationKey = key
	if i18n.scope != "" {
		translationKey = strings.Join([]string{i18n.scope, key}, ".")
	}

	if translations := i18n.Translations[locale]; translations != nil && translations[translationKey] != nil {
		value = translations[translationKey].Value
	} else {
		var value string
		if Default == locale {
			value = key
		}
		// Save translations
		i18n.SaveTransaltion(&Translation{Key: translationKey, Value: value, Locale: locale, Backend: i18n.Backends[0]})
	}

	if value == "" {
		// Get default translation if not translated
		if translations := i18n.Translations[Default]; translations != nil && translations[translationKey] != nil {
			value = translations[translationKey].Value
		}
		if value == "" {
			value = key
		}
	}

	if str, err := cldr.Parse(locale, value, args...); err == nil {
		return str
	}
	return value
}

func getLocaleFromContext(context *qor.Context) string {
	if locale := utils.GetLocale(context); locale != "" {
		return locale
	}

	return Default
}

type availableLocalesInterface interface {
	AvailableLocales() []string
}

type viewableLocalesInterface interface {
	ViewableLocales() []string
}

type editableLocalesInterface interface {
	EditableLocales() []string
}

func getAvailableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(viewableLocalesInterface); ok {
		return user.ViewableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{}
}

func getEditableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(editableLocalesInterface); ok {
		return user.EditableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{}
}

func (i18n *I18n) InjectQorAdmin(res *admin.Resource) {
	res.UseTheme("i18n")
	res.GetAdmin().I18n = i18n
	res.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB { return context.GetDB() }

	res.GetAdmin().RegisterFuncMap("lt", func(locale, key string, withDefault bool) string {
		translations := i18n.Translations[locale]
		if (translations == nil) && withDefault {
			translations = i18n.Translations[Default]
		}

		if translation := translations[key]; translation != nil {
			return translation.Value
		}

		return ""
	})

	res.GetAdmin().RegisterFuncMap("i18n_available_keys", func(context *admin.Context) (keys []string) {
		translations := i18n.Translations[Default]
		if translations == nil {
			for _, values := range i18n.Translations {
				translations = values
				break
			}
		}

		keyword := context.Request.URL.Query().Get("keyword")

		for key, translation := range translations {
			if (keyword == "") || (strings.Index(strings.ToLower(translation.Key), strings.ToLower(keyword)) != -1 ||
				strings.Index(strings.ToLower(translation.Value), keyword) != -1) {
				keys = append(keys, key)
			}
		}

		sort.Strings(keys)

		pagination := context.Searcher.Pagination
		pagination.Total = len(keys)
		pagination.PrePage = 25
		pagination.CurrentPage, _ = strconv.Atoi(context.Request.URL.Query().Get("page"))
		if pagination.CurrentPage == 0 {
			pagination.CurrentPage = 1
		}
		if pagination.CurrentPage > 0 {
			pagination.Pages = pagination.Total / pagination.PrePage
		}
		context.Searcher.Pagination = pagination

		if pagination.CurrentPage == -1 {
			return keys
		}

		lastIndex := pagination.CurrentPage * pagination.PrePage
		if pagination.Total < lastIndex {
			lastIndex = pagination.Total
		}

		return keys[(pagination.CurrentPage-1)*pagination.PrePage : lastIndex]
	})

	res.GetAdmin().RegisterFuncMap("i18n_primary_locale", func(context admin.Context) string {
		if locale := context.Request.Form.Get("primary_locale"); locale != "" {
			return locale
		}
		return getAvailableLocales(context.Request, context.CurrentUser)[0]
	})

	res.GetAdmin().RegisterFuncMap("i18n_editing_locale", func(context admin.Context) string {
		if locale := context.Request.Form.Get("to_locale"); locale != "" {
			return locale
		}
		return getLocaleFromContext(context.Context)
	})

	res.GetAdmin().RegisterFuncMap("i18n_viewable_locales", func(context admin.Context) []string {
		return getAvailableLocales(context.Request, context.CurrentUser)
	})

	res.GetAdmin().RegisterFuncMap("i18n_editable_locales", func(context admin.Context) []string {
		return getEditableLocales(context.Request, context.CurrentUser)
	})

	controller := i18nController{i18n}
	router := res.GetAdmin().GetRouter()
	router.Get(fmt.Sprintf("^/%v", res.ToParam()), controller.Index)
	router.Post(fmt.Sprintf("^/%v", res.ToParam()), controller.Update)
	router.Put(fmt.Sprintf("^/%v", res.ToParam()), controller.Update)

	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/qor/i18n/views"))
	}
}
<|endoftext|>


<commit_before>package envstruct_test

import (
	"errors"
	"os"

	"github.com/bradylove/envstruct"

	"fmt"
	"time"

	. "github.com/apoydence/eachers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("envstruct", func() {
	Describe("Load()", func() {
		var (
			ts        LargeTestStruct
			loadError error
			envVars   map[string]string
		)

		BeforeEach(func() {
			ts = LargeTestStruct{}
			ts.UnmarshallerPointer = newMockUnmarshaller()
			ts.UnmarshallerPointer.UnmarshalEnvOutput.Ret0 <- nil
			um := newMockUnmarshaller()
			ts.UnmarshallerValue = *um
			ts.UnmarshallerValue.UnmarshalEnvOutput.Ret0 <- nil

			envVars = make(map[string]string)
			for k, v := range baseEnvVars {
				envVars[k] = v
			}
		})

		JustBeforeEach(func() {
			for k, v := range envVars {
				os.Setenv(k, v)
			}
		})

		Context("when load is successful", func() {
			JustBeforeEach(func() {
				loadError = envstruct.Load(&ts)
			})

			AfterEach(func() {
				for k := range envVars {
					os.Setenv(k, "")
				}
			})

			It("does not return an error", func() {
				Expect(loadError).ToNot(HaveOccurred())
			})

			Context("with unmarshallers", func() {
				It("passes the value to the pointer field", func() {
					Expect(ts.UnmarshallerPointer.UnmarshalEnvInput).To(BeCalled(
						With("pointer"),
					))
				})

				It("passes the value to the value field's address", func() {
					Expect(ts.UnmarshallerValue.UnmarshalEnvInput).To(BeCalled(
						With("value"),
					))
				})
			})

			Context("with strings", func() {
				It("populates the string thing", func() {
					Expect(ts.StringThing).To(Equal("stringy thingy"))
				})
			})

			Describe("case sensitiveity", func() {
				It("populates the case sensitive thing", func() {
					Expect(ts.CaseSensitiveThing).To(Equal("case sensitive"))
				})
			})

			Context("with bools", func() {
				Context("with 'true'", func() {
					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with 'false'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "false"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})

				Context("with '1'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "1"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with '0'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "0"
					})

					It("is false", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})
			})

			Context("with ints", func() {
				It("populates the int thing", func() {
					Expect(ts.IntThing).To(Equal(100))
				})

				It("populates the int 8 thing", func() {
					Expect(ts.Int8Thing).To(Equal(int8(20)))
				})

				It("populates the int 16 thing", func() {
					Expect(ts.Int16Thing).To(Equal(int16(2000)))
				})

				It("populates the int 32 thing", func() {
					Expect(ts.Int32Thing).To(Equal(int32(200000)))
				})

				It("populates the int 64 thing", func() {
					Expect(ts.Int64Thing).To(Equal(int64(200000000)))
				})
			})

			Context("with uints", func() {
				It("populates the uint thing", func() {
					Expect(ts.UintThing).To(Equal(uint(100)))
				})

				It("populates the uint 8 thing", func() {
					Expect(ts.Uint8Thing).To(Equal(uint8(20)))
				})

				It("populates the uint 16 thing", func() {
					Expect(ts.Uint16Thing).To(Equal(uint16(2000)))
				})

				It("populates the uint 32 thing", func() {
					Expect(ts.Uint32Thing).To(Equal(uint32(200000)))
				})

				It("populates the uint 64 thing", func() {
					Expect(ts.Uint64Thing).To(Equal(uint64(200000000)))
				})
			})

			Context("with comma separated strings", func() {
				Context("slice of strings", func() {
					It("populates a slice of strings", func() {
						Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
					})

					Context("with leading and trailing spaces", func() {
						BeforeEach(func() {
							envVars["STRING_SLICE_THING"] = "one , two , three"
						})

						It("populates a slice of strings", func() {
							Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
						})
					})
				})

				Context("slice of ints", func() {
					It("populates a slice of ints", func() {
						Expect(ts.IntSliceThing).To(Equal([]int{1, 2, 3}))
					})
				})
			})

			Context("with structs", func() {
				It("parses the duration string", func() {
					Expect(ts.DurationThing).To(Equal(2 * time.Second))
				})

				It("parses the url string", func() {
					Expect(ts.URLThing.Scheme).To(Equal("http"))
					Expect(ts.URLThing.Host).To(Equal("github.com"))
					Expect(ts.URLThing.Path).To(Equal("/some/path"))
				})
			})
		})

		Context("with defaults", func() {
			It("honors default values if env var is empty", func() {
				ts.DefaultThing = "Default Value"

				Expect(envstruct.Load(&ts)).To(Succeed())
				Expect(ts.DefaultThing).To(Equal("Default Value"))
			})
		})

		Context("when load is unsuccessfull", func() {
			Context("when a required environment variable is not given", func() {
				BeforeEach(func() {
					envVars["REQUIRED_THING"] = ""
				})

				It("returns a validation error", func() {
					loadError = envstruct.Load(&ts)

					Expect(loadError).To(MatchError(fmt.Errorf("REQUIRED_THING is required but was empty")))
				})
			})

			Context("with an invalid int", func() {
				BeforeEach(func() {
					envVars["INT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with an invalid uint", func() {
				BeforeEach(func() {
					envVars["UINT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with a failing unmarshaller pointer", func() {
				BeforeEach(func() {
					ts.UnmarshallerPointer.UnmarshalEnvOutput.Ret0 = make(chan error, 100)
					ts.UnmarshallerPointer.UnmarshalEnvOutput.Ret0 <- errors.New("failed to unmarshal")
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with a failing unmarshaller value", func() {
				BeforeEach(func() {
					ts.UnmarshallerValue.UnmarshalEnvOutput.Ret0 = make(chan error, 100)
					ts.UnmarshallerValue.UnmarshalEnvOutput.Ret0 <- errors.New("failed to unmarshal")
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})
		})
	})
})
<commit_msg>Fix typo<commit_after>package envstruct_test

import (
	"errors"
	"os"

	"github.com/bradylove/envstruct"

	"fmt"
	"time"

	. "github.com/apoydence/eachers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("envstruct", func() {
	Describe("Load()", func() {
		var (
			ts        LargeTestStruct
			loadError error
			envVars   map[string]string
		)

		BeforeEach(func() {
			ts = LargeTestStruct{}
			ts.UnmarshallerPointer = newMockUnmarshaller()
			ts.UnmarshallerPointer.UnmarshalEnvOutput.Ret0 <- nil
			um := newMockUnmarshaller()
			ts.UnmarshallerValue = *um
			ts.UnmarshallerValue.UnmarshalEnvOutput.Ret0 <- nil

			envVars = make(map[string]string)
			for k, v := range baseEnvVars {
				envVars[k] = v
			}
		})

		JustBeforeEach(func() {
			for k, v := range envVars {
				os.Setenv(k, v)
			}
		})

		Context("when load is successful", func() {
			JustBeforeEach(func() {
				loadError = envstruct.Load(&ts)
			})

			AfterEach(func() {
				for k := range envVars {
					os.Setenv(k, "")
				}
			})

			It("does not return an error", func() {
				Expect(loadError).ToNot(HaveOccurred())
			})

			Context("with unmarshallers", func() {
				It("passes the value to the pointer field", func() {
					Expect(ts.UnmarshallerPointer.UnmarshalEnvInput).To(BeCalled(
						With("pointer"),
					))
				})

				It("passes the value to the value field's address", func() {
					Expect(ts.UnmarshallerValue.UnmarshalEnvInput).To(BeCalled(
						With("value"),
					))
				})
			})

			Context("with strings", func() {
				It("populates the string thing", func() {
					Expect(ts.StringThing).To(Equal("stringy thingy"))
				})
			})

			Describe("case sensitiveity", func() {
				It("populates the case sensitive thing", func() {
					Expect(ts.CaseSensitiveThing).To(Equal("case sensitive"))
				})
			})

			Context("with bools", func() {
				Context("with 'true'", func() {
					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with 'false'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "false"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})

				Context("with '1'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "1"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with '0'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "0"
					})

					It("is false", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})
			})

			Context("with ints", func() {
				It("populates the int thing", func() {
					Expect(ts.IntThing).To(Equal(100))
				})

				It("populates the int 8 thing", func() {
					Expect(ts.Int8Thing).To(Equal(int8(20)))
				})

				It("populates the int 16 thing", func() {
					Expect(ts.Int16Thing).To(Equal(int16(2000)))
				})

				It("populates the int 32 thing", func() {
					Expect(ts.Int32Thing).To(Equal(int32(200000)))
				})

				It("populates the int 64 thing", func() {
					Expect(ts.Int64Thing).To(Equal(int64(200000000)))
				})
			})

			Context("with uints", func() {
				It("populates the uint thing", func() {
					Expect(ts.UintThing).To(Equal(uint(100)))
				})

				It("populates the uint 8 thing", func() {
					Expect(ts.Uint8Thing).To(Equal(uint8(20)))
				})

				It("populates the uint 16 thing", func() {
					Expect(ts.Uint16Thing).To(Equal(uint16(2000)))
				})

				It("populates the uint 32 thing", func() {
					Expect(ts.Uint32Thing).To(Equal(uint32(200000)))
				})

				It("populates the uint 64 thing", func() {
					Expect(ts.Uint64Thing).To(Equal(uint64(200000000)))
				})
			})

			Context("with comma separated strings", func() {
				Context("slice of strings", func() {
					It("populates a slice of strings", func() {
						Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
					})

					Context("with leading and trailing spaces", func() {
						BeforeEach(func() {
							envVars["STRING_SLICE_THING"] = "one , two , three"
						})

						It("populates a slice of strings", func() {
							Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
						})
					})
				})

				Context("slice of ints", func() {
					It("populates a slice of ints", func() {
						Expect(ts.IntSliceThing).To(Equal([]int{1, 2, 3}))
					})
				})
			})

			Context("with structs", func() {
				It("parses the duration string", func() {
					Expect(ts.DurationThing).To(Equal(2 * time.Second))
				})

				It("parses the url string", func() {
					Expect(ts.URLThing.Scheme).To(Equal("http"))
					Expect(ts.URLThing.Host).To(Equal("github.com"))
					Expect(ts.URLThing.Path).To(Equal("/some/path"))
				})
			})
		})

		Context("with defaults", func() {
			It("honors default values if env var is empty", func() {
				ts.DefaultThing = "Default Value"

				Expect(envstruct.Load(&ts)).To(Succeed())
				Expect(ts.DefaultThing).To(Equal("Default Value"))
			})
		})

		Context("when load is unsuccessful", func() {
			Context("when a required environment variable is not given", func() {
				BeforeEach(func() {
					envVars["REQUIRED_THING"] = ""
				})

				It("returns a validation error", func() {
					loadError = envstruct.Load(&ts)

					Expect(loadError).To(MatchError(fmt.Errorf("REQUIRED_THING is required but was empty")))
				})
			})

			Context("with an invalid int", func() {
				BeforeEach(func() {
					envVars["INT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with an invalid uint", func() {
				BeforeEach(func() {
					envVars["UINT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with a failing unmarshaller pointer", func() {
				BeforeEach(func() {
					ts.UnmarshallerPointer.UnmarshalEnvOutput.Ret0 = make(chan error, 100)
					ts.UnmarshallerPointer.UnmarshalEnvOutput.Ret0 <- errors.New("failed to unmarshal")
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with a failing unmarshaller value", func() {
				BeforeEach(func() {
					ts.UnmarshallerValue.UnmarshalEnvOutput.Ret0 = make(chan error, 100)
					ts.UnmarshallerValue.UnmarshalEnvOutput.Ret0 <- errors.New("failed to unmarshal")
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
    "os"
    "os/exec"
    "bufio"
    "fmt"
    "flag"
    "regexp"
    "encoding/json"
    "io/ioutil"
    "github.com/speedata/gogit"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3/s3manager"
    "github.com/deckarep/golang-set"
    "path/filepath"
)

const REF_S3_PUSH string = "refs/heads/s3-pushed"
const CONFIG_FILE_PATH string = ".git_s3_push"

type Repository struct {
    GitRepo         *gogit.Repository
    HeadCommit      *gogit.Commit
    LastPushCommit  *gogit.Commit
    UnpushedFiles   mapset.Set
    Config          RepoConfig
    IgnoreRegexes   []*regexp.Regexp
    S3Uploader      S3Uploader
}

type RepoConfig struct {
    S3Region        string
    S3Bucket        string
    Ignore          []string
    IncludeNonGit   []string
}

func OpenRepository() (*Repository, error) {
    repo := new(Repository)
    repo.UnpushedFiles = mapset.NewSet()

    wd, err := os.Getwd()
    if err != nil {
        return nil, err
    }

    path := filepath.Join(wd, ".git")
    if _, err := os.Stat(path); os.IsNotExist(err) {
        return nil, err
    }

    gitRepo, err := gogit.OpenRepository(path)
    if err != nil {
        return nil, err
    }
    repo.GitRepo = gitRepo

    return repo, nil
}

func (repo *Repository) ReadConfigFile() error {
    file, err := ioutil.ReadFile(CONFIG_FILE_PATH)
    if err != nil {
        return err
    }

    err = json.Unmarshal(file, &repo.Config)
    if err != nil {
        return err
    }

    err = repo.CompileIgnoreRegexes()
    if err != nil {
        return err
    }

    return nil
}

func (repo *Repository) CompileIgnoreRegexes() error {
    for _, regexStr := range repo.Config.Ignore {
        regex, err := regexp.Compile(regexStr)
        if err != nil {
            return err
        }

        repo.IgnoreRegexes = append(repo.IgnoreRegexes, regex)
    }

    return nil
}

func (repo Repository) SaveConfigToFile() error {
    jsonData, err := json.Marshal(repo.Config)
    if err != nil {
        return err
    }

    err = ioutil.WriteFile(CONFIG_FILE_PATH, jsonData, 0644)
    if err != nil {
        return err
    }

    return nil
}

func (repo *Repository) FindRelevantCommits() error {
    headRef, err := repo.GitRepo.LookupReference("HEAD")
    if err != nil {
        return err
    }

    headCommit, err := repo.GitRepo.LookupCommit(headRef.Target())
    if err != nil {
        return err
    }
    repo.HeadCommit = headCommit

    lastPushRef, err := repo.GitRepo.LookupReference(REF_S3_PUSH)
    if err != nil {
        return nil
    }

    lastPushCommit, err := repo.GitRepo.LookupCommit(lastPushRef.Target())
    if err != nil {
        return nil
    }
    repo.LastPushCommit = lastPushCommit

    return nil
}

func (repo *Repository) ReadGitModifiedFiles(scanner *bufio.Scanner)  {
    for scanner.Scan() {
        file := scanner.Text()

        if _, err := os.Stat(file); os.IsNotExist(err) {
            continue
        }

        matched := false
        for _, regex := range repo.IgnoreRegexes {
            if regex.Match([]byte(file)) {
                fmt.Println("Skipping file " + file + " matches ignore spec " + regex.String())
                matched = true
                break
            }
        }

        if !matched {
            repo.UnpushedFiles.Add(scanner.Text())
        }
    }
}

func (repo *Repository) FindCommitModifiedFiles(commit *gogit.Commit) error {
    cmd := exec.Command("git", "show", "--name-only", "--oneline", commit.Id().String())
    out, err := cmd.StdoutPipe()
    if err != nil {
        return err
    }

    err = cmd.Start()
    if err != nil {
        return err
    }

    scanner := bufio.NewScanner(out)

    repo.ReadGitModifiedFiles(scanner)
    cmd.Wait()

    return nil
}

func (repo *Repository) FindUnpushedModifiedFiles() error {
    queue := []*gogit.Commit{};
    visited := mapset.NewSet();

    currentCommit := repo.HeadCommit;
    for currentCommit != nil  {
        if repo.LastPushCommit != nil && repo.LastPushCommit.Id().Equal(currentCommit.Id()) {
            break;
        }

        err := repo.FindCommitModifiedFiles(currentCommit)
        if err != nil {
            return err
        }

        for i := 0; i < currentCommit.ParentCount(); i++ {
            parentCommit := currentCommit.Parent(i)
            if !visited.Contains(parentCommit) {
                queue = append(queue, parentCommit)
            }
        }

        if len(queue) < 1 {
            break;
        }

        currentCommit = queue[0]
        queue = queue[1:]
    }

    return nil
}

func (repo Repository) UpdateGitLastPushRef() error {
    newLastPushRef := repo.HeadCommit.Id().String()
    cmd := exec.Command("git", "update-ref", REF_S3_PUSH, newLastPushRef)

    err := cmd.Start()
    if err != nil {
        return err
    }

    cmd.Wait()
    return nil
}

type S3Uploader struct {
    BucketName      *string
    S3Uploader      *s3manager.Uploader
}

func InitS3Uploader(config RepoConfig) *S3Uploader {
    uploader := new(S3Uploader)
    uploader.BucketName = aws.String(config.S3Bucket)

    s3config := aws.Config{Region: aws.String(config.S3Region)}
    s3uploader := s3manager.NewUploader(session.New(&s3config))
    uploader.S3Uploader = s3uploader

    return uploader
}

func (uploader S3Uploader) UploadFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        return err
    }

    result, err := uploader.S3Uploader.Upload(&s3manager.UploadInput{
        Body: file,
        Bucket: uploader.BucketName,
        Key: aws.String(path),
    })

    if err != nil {
        return err
    }

    fmt.Println(result.Location)
    return nil
}

func main() {
    repo, err := OpenRepository()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    repo.ReadConfigFile()

    flag.StringVar(&repo.Config.S3Bucket, "b", repo.Config.S3Bucket, "Destination S3 bucket name")
    flag.StringVar(&repo.Config.S3Region, "r", repo.Config.S3Region, "AWS region of destination bucket")
    saveConfig := flag.Bool("save", false, "Save destination region/bucket to config file")
    forceNonTracked := flag.Bool("force-external", false, "Force the upload of files not tracked in git (IncludeNonGit files in config)")
    flag.Parse()

    if repo.Config.S3Bucket == "" {
        flag.Usage()
        os.Exit(1)
    } else if (repo.Config.S3Region == "") {
        flag.Usage()
        os.Exit(1)
    } else if (*saveConfig) {
        err := repo.SaveConfigToFile()
        if err != nil {
            fmt.Println("WARNING: Failed to save config to file: ", err)
        }
    }

    if err := repo.FindRelevantCommits(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    repo.FindUnpushedModifiedFiles();

    if repo.UnpushedFiles.Cardinality() == 0 && !*forceNonTracked {
        fmt.Println("No modified files to push")
        os.Exit(0)
    }

    for _, includedFile := range repo.Config.IncludeNonGit {
        if _, err := os.Stat(includedFile); os.IsNotExist(err) {
            continue
        }

        repo.UnpushedFiles.Add(includedFile)
    }

    if repo.UnpushedFiles.Cardinality() == 0 {
        fmt.Println("No files to push")
        os.Exit(0)
    }

    uploader := InitS3Uploader(repo.Config)

    for filePath := range repo.UnpushedFiles.Iter() {
        fmt.Println("Uploading: ", filePath.(string))
        err := uploader.UploadFile(filePath.(string))
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    }

    err = repo.UpdateGitLastPushRef()
    if err != nil {
        fmt.Println("Failed to update LAST_S3_PUSH ref with git: ", err)
    }
}<commit_msg>Rudimentary unix glob matching on ignore strings in regexes<commit_after>package main

import (
    "os"
    "os/exec"
    "bufio"
    "fmt"
    "flag"
    "regexp"
    "encoding/json"
    "io/ioutil"
    "github.com/speedata/gogit"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3/s3manager"
    "github.com/deckarep/golang-set"
    "path/filepath"
    "strings"
)

const REF_S3_PUSH string = "refs/heads/s3-pushed"
const CONFIG_FILE_PATH string = ".git_s3_push"

type Repository struct {
    GitRepo         *gogit.Repository
    HeadCommit      *gogit.Commit
    LastPushCommit  *gogit.Commit
    UnpushedFiles   mapset.Set
    Config          RepoConfig
    IgnoreRegexes   []*regexp.Regexp
    S3Uploader      S3Uploader
}

type RepoConfig struct {
    S3Region        string
    S3Bucket        string
    Ignore          []string
    IncludeNonGit   []string
}

func OpenRepository() (*Repository, error) {
    repo := new(Repository)
    repo.UnpushedFiles = mapset.NewSet()

    wd, err := os.Getwd()
    if err != nil {
        return nil, err
    }

    path := filepath.Join(wd, ".git")
    if _, err := os.Stat(path); os.IsNotExist(err) {
        return nil, err
    }

    gitRepo, err := gogit.OpenRepository(path)
    if err != nil {
        return nil, err
    }
    repo.GitRepo = gitRepo

    return repo, nil
}

func (repo *Repository) ReadConfigFile() error {
    file, err := ioutil.ReadFile(CONFIG_FILE_PATH)
    if err != nil {
        return err
    }

    err = json.Unmarshal(file, &repo.Config)
    if err != nil {
        return err
    }

    err = repo.CompileIgnoreRegexes()
    if err != nil {
        return err
    }

    return nil
}

func (repo *Repository) CompileIgnoreRegexes() error {
    for _, regexStr := range repo.Config.Ignore {
        regexStr = strings.Replace("*", "(.*)")
        regex, err := regexp.Compile(regexStr)
        if err != nil {
            return err
        }

        repo.IgnoreRegexes = append(repo.IgnoreRegexes, regex)
    }

    return nil
}

func (repo Repository) SaveConfigToFile() error {
    jsonData, err := json.Marshal(repo.Config)
    if err != nil {
        return err
    }

    err = ioutil.WriteFile(CONFIG_FILE_PATH, jsonData, 0644)
    if err != nil {
        return err
    }

    return nil
}

func (repo *Repository) FindRelevantCommits() error {
    headRef, err := repo.GitRepo.LookupReference("HEAD")
    if err != nil {
        return err
    }

    headCommit, err := repo.GitRepo.LookupCommit(headRef.Target())
    if err != nil {
        return err
    }
    repo.HeadCommit = headCommit

    lastPushRef, err := repo.GitRepo.LookupReference(REF_S3_PUSH)
    if err != nil {
        return nil
    }

    lastPushCommit, err := repo.GitRepo.LookupCommit(lastPushRef.Target())
    if err != nil {
        return nil
    }
    repo.LastPushCommit = lastPushCommit

    return nil
}

func (repo *Repository) ReadGitModifiedFiles(scanner *bufio.Scanner)  {
    for scanner.Scan() {
        file := scanner.Text()

        if _, err := os.Stat(file); os.IsNotExist(err) {
            continue
        }

        matched := false
        for _, regex := range repo.IgnoreRegexes {
            if regex.Match([]byte(file)) {
                fmt.Println("Skipping file " + file + " matches ignore spec " + regex.String())
                matched = true
                break
            }
        }

        if !matched {
            repo.UnpushedFiles.Add(scanner.Text())
        }
    }
}

func (repo *Repository) FindCommitModifiedFiles(commit *gogit.Commit) error {
    cmd := exec.Command("git", "show", "--name-only", "--oneline", commit.Id().String())
    out, err := cmd.StdoutPipe()
    if err != nil {
        return err
    }

    err = cmd.Start()
    if err != nil {
        return err
    }

    scanner := bufio.NewScanner(out)

    repo.ReadGitModifiedFiles(scanner)
    cmd.Wait()

    return nil
}

func (repo *Repository) FindUnpushedModifiedFiles() error {
    queue := []*gogit.Commit{};
    visited := mapset.NewSet();

    currentCommit := repo.HeadCommit;
    for currentCommit != nil  {
        if repo.LastPushCommit != nil && repo.LastPushCommit.Id().Equal(currentCommit.Id()) {
            break;
        }

        err := repo.FindCommitModifiedFiles(currentCommit)
        if err != nil {
            return err
        }

        for i := 0; i < currentCommit.ParentCount(); i++ {
            parentCommit := currentCommit.Parent(i)
            if !visited.Contains(parentCommit) {
                queue = append(queue, parentCommit)
            }
        }

        if len(queue) < 1 {
            break;
        }

        currentCommit = queue[0]
        queue = queue[1:]
    }

    return nil
}

func (repo Repository) UpdateGitLastPushRef() error {
    newLastPushRef := repo.HeadCommit.Id().String()
    cmd := exec.Command("git", "update-ref", REF_S3_PUSH, newLastPushRef)

    err := cmd.Start()
    if err != nil {
        return err
    }

    cmd.Wait()
    return nil
}

type S3Uploader struct {
    BucketName      *string
    S3Uploader      *s3manager.Uploader
}

func InitS3Uploader(config RepoConfig) *S3Uploader {
    uploader := new(S3Uploader)
    uploader.BucketName = aws.String(config.S3Bucket)

    s3config := aws.Config{Region: aws.String(config.S3Region)}
    s3uploader := s3manager.NewUploader(session.New(&s3config))
    uploader.S3Uploader = s3uploader

    return uploader
}

func (uploader S3Uploader) UploadFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        return err
    }

    result, err := uploader.S3Uploader.Upload(&s3manager.UploadInput{
        Body: file,
        Bucket: uploader.BucketName,
        Key: aws.String(path),
    })

    if err != nil {
        return err
    }

    fmt.Println(result.Location)
    return nil
}

func main() {
    repo, err := OpenRepository()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    repo.ReadConfigFile()

    flag.StringVar(&repo.Config.S3Bucket, "b", repo.Config.S3Bucket, "Destination S3 bucket name")
    flag.StringVar(&repo.Config.S3Region, "r", repo.Config.S3Region, "AWS region of destination bucket")
    saveConfig := flag.Bool("save", false, "Save destination region/bucket to config file")
    forceNonTracked := flag.Bool("force-external", false, "Force the upload of files not tracked in git (IncludeNonGit files in config)")
    flag.Parse()

    if repo.Config.S3Bucket == "" {
        flag.Usage()
        os.Exit(1)
    } else if (repo.Config.S3Region == "") {
        flag.Usage()
        os.Exit(1)
    } else if (*saveConfig) {
        err := repo.SaveConfigToFile()
        if err != nil {
            fmt.Println("WARNING: Failed to save config to file: ", err)
        }
    }

    if err := repo.FindRelevantCommits(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    repo.FindUnpushedModifiedFiles();

    if repo.UnpushedFiles.Cardinality() == 0 && !*forceNonTracked {
        fmt.Println("No modified files to push")
        os.Exit(0)
    }

    for _, includedFile := range repo.Config.IncludeNonGit {
        if _, err := os.Stat(includedFile); os.IsNotExist(err) {
            continue
        }

        repo.UnpushedFiles.Add(includedFile)
    }

    if repo.UnpushedFiles.Cardinality() == 0 {
        fmt.Println("No files to push")
        os.Exit(0)
    }

    uploader := InitS3Uploader(repo.Config)

    for filePath := range repo.UnpushedFiles.Iter() {
        fmt.Println("Uploading: ", filePath.(string))
        err := uploader.UploadFile(filePath.(string))
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    }

    err = repo.UpdateGitLastPushRef()
    if err != nil {
        fmt.Println("Failed to update LAST_S3_PUSH ref with git: ", err)
    }
}<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"
)

type List struct {
	sync.RWMutex
	items []Playable
	pos   map[string]int
}

type Playable struct {
	Urn          string    `json:"urn"`
	FinishedAt   time.Time `json:"finished_at"`
	LastPlayedAt time.Time `json:"last_played_at"`
	Progress     uint64    `json:"progress"`
}

func NewList() *List {
	return &List{
		items: make([]Playable, 0),
		pos:   make(map[string]int),
	}
}

func (l *List) Set(urn string, finished, last time.Time, progress uint64) (Playable, error) {
	l.Lock()
	defer l.Unlock()
	pos, ok := l.pos[urn]
	if !ok {
		l.items = append(l.items, Playable{Urn: urn})
		l.pos[urn] = len(l.items) - 1
	}
	if finished.Unix() != 0 {
		l.items[pos].FinishedAt = finished
	}
	if last.Unix() != 0 {
		l.items[pos].LastPlayedAt = last
	}
	if progress != 0 {
		l.items[pos].Progress = progress
	}
	return l.items[pos], nil
}

func (l *List) Delete(urn string) (Playable, error) {
	l.Lock()
	defer l.Unlock()

	pos, ok := l.pos[urn]
	if !ok {
		return Playable{}, fmt.Errorf("`%s` not found", urn)
	}

	playable := l.items[pos]
	updatedItems := make([]Playable, len(l.items)-1)
	copy(updatedItems, l.items[:pos-1])
	copy(updatedItems[pos:], l.items[pos+1:])
	delete(l.pos, urn)

	return playable, nil
}

func (l *List) Size() int {
	l.RLock()
	defer l.RUnlock()
	return len(l.items)
}

func (q *List) MarshalJSON() ([]byte, error) {
	q.RLock()
	defer q.RUnlock()
	return json.Marshal(q.items)
}
<commit_msg>Fix delete<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"
)

type List struct {
	sync.RWMutex
	items []Playable
	pos   map[string]int
}

type Playable struct {
	Urn          string    `json:"urn"`
	FinishedAt   time.Time `json:"finished_at"`
	LastPlayedAt time.Time `json:"last_played_at"`
	Progress     uint64    `json:"progress"`
}

func NewList() *List {
	return &List{
		items: make([]Playable, 0),
		pos:   make(map[string]int),
	}
}

func (l *List) Set(urn string, finished, last time.Time, progress uint64) (Playable, error) {
	l.Lock()
	defer l.Unlock()
	pos, ok := l.pos[urn]
	if !ok {
		l.items = append(l.items, Playable{Urn: urn})
		l.pos[urn] = len(l.items) - 1
	}
	if finished.Unix() != 0 {
		l.items[pos].FinishedAt = finished
	}
	if last.Unix() != 0 {
		l.items[pos].LastPlayedAt = last
	}
	if progress != 0 {
		l.items[pos].Progress = progress
	}
	return l.items[pos], nil
}

func (l *List) Delete(urn string) (Playable, error) {
	l.Lock()
	defer l.Unlock()

	pos, ok := l.pos[urn]
	if !ok {
		return Playable{}, fmt.Errorf("`%s` not found", urn)
	}

	playable := l.items[pos]
	if pos == 0 {
		l.items = l.items[1:]
	} else if pos == len(l.items)-1 {
		l.items = l.items[:len(l.items)-1]
	} else {
		updatedItems := make([]Playable, len(l.items)-1)
		copy(updatedItems, l.items[:pos])
		copy(updatedItems[pos:], l.items[pos+1:])
		l.items = updatedItems
	}
	delete(l.pos, urn)

	return playable, nil
}

func (l *List) Size() int {
	l.RLock()
	defer l.RUnlock()
	return len(l.items)
}

func (q *List) MarshalJSON() ([]byte, error) {
	q.RLock()
	defer q.RUnlock()
	return json.Marshal(q.items)
}
<|endoftext|>


<commit_before>// Package server maps a connection to each player(who is online) so we have a comunication chanel.
package server

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"path"
	"runtime"
	"runtime/debug"

	"github.com/fzzy/sockjs-go/sockjs"

	"warcluster/server/response"
)

var listener net.Listener

var sessions *sockjs.SessionPool = sockjs.NewSessionPool() //This is the SockJs sessions pull (a list of all the currently active client's sessions).

// This function goes trough all the procedurs needed for the werver to be initialized.
// Create an empty connections pool and start the listening foe messages loop.
func Start(host string, port uint16) error {
	log.Print(fmt.Sprintf("Server is running at http://%v:%v/", host, port))
	log.Print("Quit the server with Ctrl-C.")

	mux := sockjs.NewServeMux(http.DefaultServeMux)
	conf := sockjs.NewConfig()

	http.HandleFunc("/console", staticHandler)
	http.Handle("/static", http.FileServer(http.Dir(getStaticDir())))
	mux.Handle("/universe", handler, conf)

	if err := ListenAndServe(fmt.Sprintf("%v:%v", host, port), mux); err != nil {
		log.Println(err)
		return err
	}

	return Stop()
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.  If
// srv.Addr is blank, ":http" is used.
func ListenAndServe(address string, mux *sockjs.ServeMux) error {
	var err error

	server := &http.Server{Addr: address, Handler: mux}
	addr := server.Addr
	if addr == "" {
		addr = ":http"
	}
	listener, err = net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	return server.Serve(listener)
}

// Die biatch and get the fuck out.
func Stop() error {
	log.Println("Server is shutting down...")
	listener.Close()
	log.Println("Server has stopped.")
	return nil
}

// Returns the HTML page needed to display the debug page (server "chat" window).
func staticHandler(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, path.Join(getStaticDir(), "/index.html"))
}

// This function is called from the message handler to parse the first message for every new connection.
// It check for existing user in the DB and logs him if the password is correct.
// If the user is new he is initiated and a new home planet nad solar system are generated.
func login(session sockjs.Session) (*Client, error) {
	nickname, player, err := authenticate(session)
	if err != nil {
		response.Send(response.NewLoginFailed(), session.Send)
		return nil, errors.New("Login failed")
	}

	client := &Client{
		Session:  session,
		Nickname: nickname,
		Player:   player,
	}

	loginSuccess := response.NewLoginSuccess()
	loginSuccess.Username = client.Nickname
	loginSuccess.Position = player.ScreenPosition
	response.Send(loginSuccess, session.Send)
	return client, nil
}

// On the first received message from each connection the server will call the handler.
// Add new session to the session pool, call the login func to validate the connection and
// if the connection is valid enters "while true" state and uses ParseRequest to parse the requests.
//
// Shocking right?!?!
func handler(session sockjs.Session) {
	defer func() {
		if panicked := recover(); panicked != nil {
			log.Println(string(debug.Stack()))
			return
		}
	}()
	defer sessions.Remove(session)

	if client, err := login(session); err == nil {
		sessions.Add(session)
		for {
			message := session.Receive()
			if message == nil {
				break
			}

			if request, err := UnmarshalRequest(message, client); err == nil {
				if action, err := ParseRequest(request); err == nil {
					if err := action(request); err != nil {
						log.Println(err)
					}
				} else {
					log.Println("Error in server.main.handler.ParseRequest:", err.Error())
				}
			} else {
				log.Println("Error in server.main.handler.UnmarshalRequest:", err.Error())
			}
		}
	} else {
		session.End()
	}
}

// getStaticDir return an absolute path to the static files
func getStaticDir() string {
	_, filename, _, _ := runtime.Caller(1)
	return path.Join(path.Dir(filename), "../static")
}
<commit_msg>Make the panic reporting a bit more adequate<commit_after>// Package server maps a connection to each player(who is online) so we have a comunication chanel.
package server

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"path"
	"runtime"
	"runtime/debug"

	"github.com/fzzy/sockjs-go/sockjs"

	"warcluster/server/response"
)

var listener net.Listener

var sessions *sockjs.SessionPool = sockjs.NewSessionPool() //This is the SockJs sessions pull (a list of all the currently active client's sessions).

// This function goes trough all the procedurs needed for the werver to be initialized.
// Create an empty connections pool and start the listening foe messages loop.
func Start(host string, port uint16) error {
	log.Print(fmt.Sprintf("Server is running at http://%v:%v/", host, port))
	log.Print("Quit the server with Ctrl-C.")

	mux := sockjs.NewServeMux(http.DefaultServeMux)
	conf := sockjs.NewConfig()

	http.HandleFunc("/console", staticHandler)
	http.Handle("/static", http.FileServer(http.Dir(getStaticDir())))
	mux.Handle("/universe", handler, conf)

	if err := ListenAndServe(fmt.Sprintf("%v:%v", host, port), mux); err != nil {
		log.Println(err)
		return err
	}

	return Stop()
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.  If
// srv.Addr is blank, ":http" is used.
func ListenAndServe(address string, mux *sockjs.ServeMux) error {
	var err error

	server := &http.Server{Addr: address, Handler: mux}
	addr := server.Addr
	if addr == "" {
		addr = ":http"
	}
	listener, err = net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	return server.Serve(listener)
}

// Die biatch and get the fuck out.
func Stop() error {
	log.Println("Server is shutting down...")
	listener.Close()
	log.Println("Server has stopped.")
	return nil
}

// Returns the HTML page needed to display the debug page (server "chat" window).
func staticHandler(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, path.Join(getStaticDir(), "/index.html"))
}

// This function is called from the message handler to parse the first message for every new connection.
// It check for existing user in the DB and logs him if the password is correct.
// If the user is new he is initiated and a new home planet nad solar system are generated.
func login(session sockjs.Session) (*Client, error) {
	nickname, player, err := authenticate(session)
	if err != nil {
		response.Send(response.NewLoginFailed(), session.Send)
		return nil, errors.New("Login failed")
	}

	client := &Client{
		Session:  session,
		Nickname: nickname,
		Player:   player,
	}

	loginSuccess := response.NewLoginSuccess()
	loginSuccess.Username = client.Nickname
	loginSuccess.Position = player.ScreenPosition
	response.Send(loginSuccess, session.Send)
	return client, nil
}

// On the first received message from each connection the server will call the handler.
// Add new session to the session pool, call the login func to validate the connection and
// if the connection is valid enters "while true" state and uses ParseRequest to parse the requests.
//
// Shocking right?!?!
func handler(session sockjs.Session) {
	defer func() {
		if panicked := recover(); panicked != nil {
			log.Println(fmt.Sprintf("%s\n\nStacktrace:\n\n%s", panicked, debug.Stack()))
			return
		}
	}()
	defer sessions.Remove(session)

	if client, err := login(session); err == nil {
		sessions.Add(session)
		for {
			message := session.Receive()
			if message == nil {
				break
			}

			if request, err := UnmarshalRequest(message, client); err == nil {
				if action, err := ParseRequest(request); err == nil {
					if err := action(request); err != nil {
						log.Println(err)
					}
				} else {
					log.Println("Error in server.main.handler.ParseRequest:", err.Error())
				}
			} else {
				log.Println("Error in server.main.handler.UnmarshalRequest:", err.Error())
			}
		}
	} else {
		session.End()
	}
}

// getStaticDir return an absolute path to the static files
func getStaticDir() string {
	_, filename, _, _ := runtime.Caller(1)
	return path.Join(path.Dir(filename), "../static")
}
<|endoftext|>