<commit_before>package bild

import (
	"image"
	"image/color"
	"math"
)

// ConvolutionMatrix interface for use as an image Kernel
type ConvolutionMatrix interface {
	At(x, y int) float64
	Sum() float64
	Normalized() ConvolutionMatrix
	Size() int
}

// NewKernel returns a kernel of the provided size
func NewKernel(size int) *Kernel {
	matrix := make([][]float64, size)
	for i := 0; i < size; i++ {
		matrix[i] = make([]float64, size)
	}
	return &Kernel{matrix}
}

// Kernel is used as a convolution matrix
type Kernel struct {
	Matrix [][]float64
}

// Sum returns the cumulative value of the matrix
func (k *Kernel) Sum() float64 {
	var sum float64
	size := k.Size()
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			sum += k.Matrix[x][y]
		}
	}
	return sum
}

// Normalized returns a new Kernel with normalized values
func (k *Kernel) Normalized() ConvolutionMatrix {
	sum := k.Sum()
	size := k.Size()
	nk := NewKernel(size)

	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			nk.Matrix[x][y] = k.Matrix[x][y] / sum
		}
	}

	return nk
}

// Size returns the row/column length for the kernel
func (k *Kernel) Size() int {
	return len(k.Matrix)
}

// At returns the matrix value at position x, y
func (k *Kernel) At(x, y int) float64 {
	return k.Matrix[x][y]
}

// convolute applies a convolution matrix (kernel) to an image
func convolute(src *image.NRGBA, k ConvolutionMatrix) *image.NRGBA {
	bounds := src.Bounds()
	w, h := bounds.Max.X, bounds.Max.Y
	dst := image.NewNRGBA(bounds)

	nk := k.Normalized()
	nksize := nk.Size()

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {

				var r, g, b, a float64
				for kx := 0; kx < nksize; kx++ {
					for ky := 0; ky < nksize; ky++ {
						ix := x + kx - (nksize / 2)
						iy := y + ky - (nksize / 2)

						// Quality threshold
						if nk.At(kx, ky) < 0.00001 {
							continue
						}
						if ix < 0 || kx >= w || iy < 0 || ky >= h {
							continue
						}

						c := src.NRGBAAt(ix, iy)
						m := nk.At(kx, ky)
						r += float64(c.R) * m
						g += float64(c.G) * m
						b += float64(c.B) * m
						a += float64(c.A) * m
					}
				}

				c := color.NRGBA{
					uint8(math.Max(0, math.Min(r, 255))),
					uint8(math.Max(0, math.Min(g, 255))),
					uint8(math.Max(0, math.Min(b, 255))),
					uint8(math.Max(0, math.Min(a, 255))),
				}

				dst.Set(x, y, c)
			}
		}
	})

	return dst
}
<commit_msg>switched to accessing Pix array<commit_after>package bild

import (
	"image"
	"math"
)

// ConvolutionMatrix interface for use as an image Kernel
type ConvolutionMatrix interface {
	At(x, y int) float64
	Sum() float64
	Normalized() ConvolutionMatrix
	Diameter() int
}

// NewKernel returns a kernel of the provided size
func NewKernel(diameter int) *Kernel {
	matrix := make([][]float64, diameter)
	for i := 0; i < diameter; i++ {
		matrix[i] = make([]float64, diameter)
	}
	return &Kernel{matrix}
}

// Kernel is used as a convolution matrix
type Kernel struct {
	Matrix [][]float64
}

// Sum returns the cumulative value of the matrix
func (k *Kernel) Sum() float64 {
	var sum float64
	diameter := k.Diameter()
	for x := 0; x < diameter; x++ {
		for y := 0; y < diameter; y++ {
			sum += k.Matrix[x][y]
		}
	}
	return sum
}

// Normalized returns a new Kernel with normalized values
func (k *Kernel) Normalized() ConvolutionMatrix {
	sum := k.Sum()
	diameter := k.Diameter()
	nk := NewKernel(diameter)

	for x := 0; x < diameter; x++ {
		for y := 0; y < diameter; y++ {
			nk.Matrix[x][y] = k.Matrix[x][y] / sum
		}
	}

	return nk
}

// Diameter returns the row/column length for the kernel
func (k *Kernel) Diameter() int {
	return len(k.Matrix)
}

// At returns the matrix value at position x, y
func (k *Kernel) At(x, y int) float64 {
	return k.Matrix[x][y]
}

// convolute applies a convolution matrix (kernel) to an image
func convolute(img image.Image, k ConvolutionMatrix) *image.RGBA {
	bounds := img.Bounds()
	src := CloneAsRGBA(img)
	dst := image.NewRGBA(bounds)

	w, h := bounds.Max.X, bounds.Max.Y
	diameter := k.Diameter()
	normKernel := k.Normalized()

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {

				var r, g, b, a float64
				for kx := 0; kx < diameter; kx++ {
					for ky := 0; ky < diameter; ky++ {
						ix := x - diameter/2 + kx
						iy := y - diameter/2 + ky

						if ix < 0 || ix >= w || iy < 0 || iy >= h {
							continue
						}

						ipos := iy*dst.Stride + ix*4
						kvalue := normKernel.At(kx, ky)
						r += float64(src.Pix[ipos+0]) * kvalue
						g += float64(src.Pix[ipos+1]) * kvalue
						b += float64(src.Pix[ipos+2]) * kvalue
						a += float64(src.Pix[ipos+3]) * kvalue
					}
				}

				pos := y*dst.Stride + x*4

				dst.Pix[pos+0] = uint8(math.Max(math.Min(r, 255), 0))
				dst.Pix[pos+1] = uint8(math.Max(math.Min(g, 255), 0))
				dst.Pix[pos+2] = uint8(math.Max(math.Min(b, 255), 0))
				dst.Pix[pos+3] = uint8(math.Max(math.Min(a, 255), 0))
			}
		}
	})

	return dst
}
<|endoftext|>


<commit_before>package airstrike

import (
	"encoding/json"
	"runtime"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
)

type SimpleRaid struct {
	Planes []struct {
		Name    string `json:"name"`
		Weapons []struct {
			Name string `json:"name"`
		} `json:"weapons"`
	} `json:"planes"`
}

// A Raid is a collection of bombs capable of reporting summary statistics.
type Raid struct {
	Planes []Plane `json:"planes"`
}

func reportResults(ch chan logrus.Fields, urlInvariant string, warningThreshold time.Duration) {
	myPC, _, _, _ := runtime.Caller(0)
	desc := runtime.FuncForPC(myPC).Name()
	desc = strings.SplitAfter(desc, "github.com/dysolution/")[1]
	for {
		fields := <-ch
		responseTime, _ := fields["response_time"].(time.Duration)
		if responseTime > warningThreshold {
			log.WithFields(fields).Warn(desc)
		} else {
			log.WithFields(fields).Info(desc)
		}
	}
}

// Conduct tells the Squadron to launch all of its planes. Each Plane serially
// fires its weapons and sends the result of each weapon down a channel.
func (r *Raid) Conduct(logger *logrus.Logger, urlInvariant string, warningThreshold time.Duration) {
	ch := make(chan logrus.Fields)

	squadron := New(logger)

	go reportResults(ch, urlInvariant, warningThreshold)

	for _, plane := range r.Planes {

		go func(plane Plane) {
			myPC, _, _, _ := runtime.Caller(0)
			desc := runtime.FuncForPC(myPC).Name()
			desc = strings.SplitAfter(desc, "github.com/dysolution/")[1]

			results, err := plane.Launch()
			if err != nil {
				ch <- logrus.Fields{"error": err}
			}

			for weaponID, result := range results {
				stats := logrus.Fields{
					"plane":         plane.Name,
					"weapon_id":     weaponID,
					"squadron_id":   squadron.ID,
					"method":        result.Verb,
					"path":          strings.SplitAfter(result.Path, urlInvariant)[1],
					"response_time": result.Duration * time.Millisecond,
					"status_code":   result.StatusCode,
				}
				ch <- stats
			}
		}(plane)

	}
}

func (r *Raid) String() string {
	out, err := json.MarshalIndent(r, "", "  ")
	if err != nil {
		return "error marshaling Raid"
	}
	return string(out)
}

// NewRaid initializes and returns a Raid, . It should be used in lieu of Raid literals.
func NewRaid(planes ...Plane) Raid {
	var payload []Plane
	for _, plane := range planes {
		payload = append(payload, plane)
	}
	return Raid{Planes: payload}
}
<commit_msg>refactor: implement Clone and Chaos patterns<commit_after>package airstrike

import (
	"encoding/json"
	"runtime"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
)

type SimpleRaid struct {
	Planes []struct {
		Name    string `json:"name"`
		Weapons []struct {
			Name string `json:"name"`
		} `json:"weapons"`
	} `json:"planes"`
}

// A Raid is a collection of bombs capable of reporting summary statistics.
type Raid struct {
	Planes []Plane `json:"planes"`
}

func report(ch chan logrus.Fields, urlInvariant string, warningThreshold time.Duration) {
	myPC, _, _, _ := runtime.Caller(0)
	desc := runtime.FuncForPC(myPC).Name()
	desc = strings.SplitAfter(desc, "github.com/dysolution/")[1]
	for {
		fields := <-ch
		responseTime, _ := fields["response_time"].(time.Duration)
		if responseTime > warningThreshold {
			log.WithFields(fields).Warn(desc)
		} else {
			log.WithFields(fields).Info(desc)
		}
	}
}

// Conduct tells the Squadron to launch all of its planes. Each Plane serially
// fires its weapons and sends the result of each weapon down a channel.
func (r *Raid) Conduct(logger *logrus.Logger, urlInvariant string, warningThreshold time.Duration) {
	ch := make(chan logrus.Fields)

	squadron := New(logger)

	go report(ch, urlInvariant, warningThreshold)

	for _, plane := range r.Planes {

		go func(plane Plane) {
			myPC, _, _, _ := runtime.Caller(0)
			desc := runtime.FuncForPC(myPC).Name()
			desc = strings.SplitAfter(desc, "github.com/dysolution/")[1]

			results, err := plane.Launch()
			if err != nil {
				ch <- logrus.Fields{"error": err}
			}

			for weaponID, result := range results {
				stats := logrus.Fields{
					"plane":         plane.Name,
					"weapon_id":     weaponID,
					"squadron_id":   squadron.ID,
					"method":        result.Verb,
					"path":          strings.SplitAfter(result.Path, urlInvariant)[1],
					"response_time": result.Duration * time.Millisecond,
					"status_code":   result.StatusCode,
				}
				ch <- stats
			}
		}(plane)

	}
}

func (r *Raid) String() string {
	out, err := json.MarshalIndent(r, "", "  ")
	if err != nil {
		return "error marshaling Raid"
	}
	return string(out)
}

// NewRaid initializes and returns a Raid, . It should be used in lieu of Raid literals.
func NewRaid(planes ...Plane) Raid {
	var payload []Plane
	for _, plane := range planes {
		payload = append(payload, plane)
	}
	return Raid{Planes: payload}
}
<|endoftext|>


<commit_before>package search

import (
	"testing"
)

const (
	TEST_KEY             = "test"
	TEST_DATA            = "laksjdflaksjdflaskjdfl;aksjaksjdfl;akjsdlfkjkajiwuerinwenenwehuuxZJclnwehrherjnwfenaldjfajsdhfkjjjdafsdjfashdfahsdiufhaiwuenncaiwdsuhfiahweurinkjnwdfkjansdufhau9wenfuansdkjfnsdkbaksjdghkasjdfl;ads;flahsdifuhasdfnawdjnwkejnfiawdbgkjasdfkjahsdlfjasldkfjasldghauisdhfunfadsjfnakljbwerkjbakdjchajsdhfkjabdfkjlabdskhfhaksjdfhakjsdhfkjashdfjahcjdncandiufahsdkljf asdjfha dfa sdfasdf asdfhasdf ashdfjasdhfshdf asdfhajsdhijfhaklwjehkcn asd adfhajsdf adsg "
	SELECT_ALL_TEST_DATA = "jkjk kkk jkjk kkj"
)

var (
	s *SearchDb
)

// TestSplitData tests the TestSplitData function
func TestSplitData(t *testing.T) {
	d := "asdfgasdfasdkfjasldkjfal;sdjf;alsdkf"
	SplitData(d)
}

// TestInsert tests the Insert function
func TestInsert(t *testing.T) {
	s = NewSearchDb("testing")
	s.Insert("test", 1)
}

// TestSelect tests the Select function
func TestSelect(t *testing.T) {
	expected := 1
	if expected != s.Select("test") {
		t.Fail()

	}
}

// TestSelectAll tests selecting all of the KeyPointers of a given key
func TestSelectAll(t *testing.T) {
	one := NewSearchDb("one")
	err := one.CreateAndUpdateindicies(TEST_DATA)
	if err != nil {
		t.Error(err)
	}
	keys := one.SelectAll("j")
	// verify the correct number of search results
	if len(keys) != 45 {
		t.Error("Wront number of search elements")
	}
}

// TestMultipleManagers makes sure multiple managers don't have cross-talk
func TestMultipleManagers(t *testing.T) {
	one := NewSearchDb("one")
	two := NewSearchDb("two")
	one.Insert("test", "one")
	two.Insert("test", "two")
	if one.Select("test") != "one" {
		t.Fail()
	}
	if two.Select("test") != "two" {
		t.Fail()
	}
}

// TestUpdateChildren makes sure we can still update the children of an index
func TestUpdateChildren(t *testing.T) {
	// create the database for testing
	one := NewSearchDb("one")
	// insert a new index
	one.Insert(TEST_KEY, NewIndex())
	// if we can't update, error out
	err := one.UpdateChildren(TEST_KEY, &KeyPointer{Key: TEST_KEY, Pointer: 1})
	if err != nil {
		t.Log(err)
		t.Fail()
	}
	// make sure the data got added
	index := one.Select(TEST_KEY)
	// check that it is the correct type, and it has the correct value
	if len(index.(*Index).Children) != 1 {
		t.Log("Child not added. Wrong length: ", len(index.(*Index).Children))
		t.Fail()
	}
}

// TestCreateIndexIfNotExist tests that indicies are not overwritten by this method
func TestCreateIndexIfNotExist(t *testing.T) {
	one := NewSearchDb("one")
	one.CreateIndexIfNotExist("1")
	one.UpdateChildren("1", &KeyPointer{Key: TEST_KEY, Pointer: 1})
	one.CreateIndexIfNotExist("1")
	if len(one.Select("1").(*Index).Children) != 1 {
		t.Log("Overwrote Index.")
		t.Fail()
	}
}

// TestCreateAndUpdateindicies makes sure a dataset can be created in the db
func TestCreateAndUpdateindicies(t *testing.T) {
	one := NewSearchDb("one")
	one.CreateAndUpdateindicies("laksjdfklajsdflkajsd;lfjka")
}

// BenchmarkTestCreateAndUpdateindicies
func BenchmarkTestCreateAndUpdateindicies(b *testing.B) {
	one := NewSearchDb("one")
	for i := 0; i < b.N; i++ {

		one.CreateAndUpdateindicies(TEST_DATA)
	}

}

// BenchmarkSplitData tests performance of SplitData
func BenchmarkSplitData(b *testing.B) {
	for i := 0; i < b.N; i++ {
		SplitData(TEST_DATA)
	}
}

// BechmarkSelectAllShort test the perforamnce of the SelectAll function
func BenchmarkSelectAllShort(b *testing.B) {
	one := NewSearchDb("one")
	err := one.CreateAndUpdateindicies(SELECT_ALL_TEST_DATA)
	if err != nil {
		b.Error(err)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		one.SelectAll("j")
	}

}
<commit_msg>added value tags to KeyPointers<commit_after>package search

import (
	"testing"
)

const (
	TEST_KEY             = "test"
	TEST_DATA            = "laksjdflaksjdflaskjdfl;aksjaksjdfl;akjsdlfkjkajiwuerinwenenwehuuxZJclnwehrherjnwfenaldjfajsdhfkjjjdafsdjfashdfahsdiufhaiwuenncaiwdsuhfiahweurinkjnwdfkjansdufhau9wenfuansdkjfnsdkbaksjdghkasjdfl;ads;flahsdifuhasdfnawdjnwkejnfiawdbgkjasdfkjahsdlfjasldkfjasldghauisdhfunfadsjfnakljbwerkjbakdjchajsdhfkjabdfkjlabdskhfhaksjdfhakjsdhfkjashdfjahcjdncandiufahsdkljf asdjfha dfa sdfasdf asdfhasdf ashdfjasdhfshdf asdfhajsdhijfhaklwjehkcn asd adfhajsdf adsg "
	SELECT_ALL_TEST_DATA = "jkjk kkk jkjk kkj"
)

var (
	s *SearchDb
)

// TestSplitData tests the TestSplitData function
func TestSplitData(t *testing.T) {
	d := "asdfgasdfasdkfjasldkjfal;sdjf;alsdkf"
	SplitData(d, "tag")
}

// TestInsert tests the Insert function
func TestInsert(t *testing.T) {
	s = NewSearchDb("testing")
	s.Insert("test", 1)
}

// TestSelect tests the Select function
func TestSelect(t *testing.T) {
	expected := 1
	if expected != s.Select("test") {
		t.Fail()

	}
}

// TestSelectAll tests selecting all of the KeyPointers of a given key
func TestSelectAll(t *testing.T) {
	one := NewSearchDb("one")
	err := one.CreateAndUpdateindicies(TEST_DATA, "Test")
	if err != nil {
		t.Error(err)
	}
	keys := one.SelectAll("j")
	// verify the correct number of search results
	if len(keys) != 45 {
		t.Error("Wront number of search elements", keys)
	}
}

// TestMultipleManagers makes sure multiple managers don't have cross-talk
func TestMultipleManagers(t *testing.T) {
	one := NewSearchDb("one")
	two := NewSearchDb("two")
	one.Insert("test", "one")
	two.Insert("test", "two")
	if one.Select("test") != "one" {
		t.Fail()
	}
	if two.Select("test") != "two" {
		t.Fail()
	}
}

// TestUpdateChildren makes sure we can still update the children of an index
func TestUpdateChildren(t *testing.T) {
	// create the database for testing
	one := NewSearchDb("one")
	// insert a new index
	one.Insert(TEST_KEY, NewIndex())
	// if we can't update, error out
	err := one.UpdateChildren(TEST_KEY, &KeyPointer{Key: TEST_KEY, Pointer: 1})
	if err != nil {
		t.Log(err)
		t.Fail()
	}
	// make sure the data got added
	index := one.Select(TEST_KEY)
	// check that it is the correct type, and it has the correct value
	if len(index.(*Index).Children) != 1 {
		t.Log("Child not added. Wrong length: ", len(index.(*Index).Children))
		t.Fail()
	}
}

// TestCreateIndexIfNotExist tests that indicies are not overwritten by this method
func TestCreateIndexIfNotExist(t *testing.T) {
	one := NewSearchDb("one")
	one.CreateIndexIfNotExist("1")
	one.UpdateChildren("1", &KeyPointer{Key: TEST_KEY, Pointer: 1})
	one.CreateIndexIfNotExist("1")
	if len(one.Select("1").(*Index).Children) != 1 {
		t.Log("Overwrote Index.")
		t.Fail()
	}
}

// TestCreateAndUpdateindicies makes sure a dataset can be created in the db
func TestCreateAndUpdateindicies(t *testing.T) {
	one := NewSearchDb("one")
	one.CreateAndUpdateindicies("laksjdfklajsdflkajsd;lfjka", "Test")
}

// BenchmarkTestCreateAndUpdateindicies
func BenchmarkTestCreateAndUpdateindicies(b *testing.B) {
	one := NewSearchDb("one")
	for i := 0; i < b.N; i++ {

		one.CreateAndUpdateindicies(TEST_DATA, "Benchmark")
	}

}

// BenchmarkSplitData tests performance of SplitData
func BenchmarkSplitData(b *testing.B) {
	for i := 0; i < b.N; i++ {
		SplitData(TEST_DATA, "tag")
	}
}

// BechmarkSelectAllShort test the perforamnce of the SelectAll function
func BenchmarkSelectAllShort(b *testing.B) {
	one := NewSearchDb("one")
	err := one.CreateAndUpdateindicies(SELECT_ALL_TEST_DATA, "Benchmark")
	if err != nil {
		b.Error(err)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		one.SelectAll("j")
	}

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 search

import (
	"testing"
	"github.com/ctdk/goiardi/node"
	"github.com/ctdk/goiardi/role"
	"github.com/ctdk/goiardi/environment"
	"github.com/ctdk/goiardi/actor"
	"github.com/ctdk/goiardi/data_bag"
	"fmt"
)

// Most search testing can be handled fine with chef-pedant, but that's no
// reason to not have some go tests for it too.

var node1 *node.Node
var node2 *node.Node
var node3 *node.Node
var node4 *node.Node
var role1 *role.Role
var role2 *role.Role
var role3 *role.Role
var role4 *role.Role
var env1 *environment.ChefEnvironment
var env2 *environment.ChefEnvironment
var env3 *environment.ChefEnvironment
var env4 *environment.ChefEnvironment
var client1 *actor.Actor
var client2 *actor.Actor
var client3 *actor.Actor
var client4 *actor.Actor
var dbag1 *data_bag.DataBag
var dbag2 *data_bag.DataBag
var dbag3 *data_bag.DataBag
var dbag4 *data_bag.DataBag

func makeSearchItems() int{
	/* Gotta populate the search index */
	nodes := make([]*node.Node, 4)
	roles := make([]*role.Role, 4)
	envs := make([]*environment.ChefEnvironment, 4)
	clients := make([]*actor.Actor, 4)
	dbags := make([]*data_bag.DataBag, 4)

	for i := 0; i < 4; i++ {
		nodes[i], _ = node.New(fmt.Sprintf("node%d",i))
		nodes[i].Save()
		roles[i], _ = role.New(fmt.Sprintf("role%d",i))
		roles[i].Save()
		envs[i], _ = environment.New(fmt.Sprintf("env%d",i))
		envs[i].Save()
		clients[i], _ = actor.New(fmt.Sprintf("client%d",i), "client")
		clients[i].Save()
		dbags[i], _ = data_bag.New(fmt.Sprintf("data_bag%d",i))
		dbags[i].Save()
		dbi := make(map[string]interface{})
		dbi["id"] = fmt.Sprintf("dbi%d", i)
		dbi["foo"] = fmt.Sprintf("dbag_item_%d", i)
		dbags[i].NewDBItem(dbi)
	}
	node1 = nodes[0]
	node2 = nodes[1]
	node3 = nodes[2]
	node4 = nodes[3]
	role1 = roles[0]
	role2 = roles[1]
	role3 = roles[2]
	role4 = roles[3]
	env1 = envs[0]
	env2 = envs[1]
	env3 = envs[2]
	env4 = envs[3]
	client1 = clients[0]
	client2 = clients[1]
	client3 = clients[2]
	client4 = clients[3]
	dbag1 = dbags[0]
	dbag2 = dbags[1]
	dbag3 = dbags[2]
	dbag4 = dbags[3]

	/* Make this function return something so the compiler's happy building
	 * the tests. */
	return 1
}

var v = makeSearchItems()

func TestFoo(t *testing.T){
	return
}

/* Only basic search tests are here. The stronger tests are handled in
 * chef-pedant, but these tests are meant to check basic search functionality.
 */

func TestSearchNode(t *testing.T){
	n, _ := Search("node", "name:node1")
	if n[0].(*node.Node).Name != "node1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchNodeAll(t *testing.T){
	n, _ := Search("node", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchRole(t *testing.T){
	r, _ := Search("role", "name:role1")
	if r[0].(*role.Role).Name != "role1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchRoleAll(t *testing.T){
	n, _ := Search("role", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchEnv(t *testing.T){
	e, _ := Search("environment", "name:env1")
	if e[0].(*environment.ChefEnvironment).Name != "env1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchEnvAll(t *testing.T){
	n, _ := Search("environment", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchClient(t *testing.T){
	c, _ := Search("client", "name:client1")
	if c[0].(*actor.Actor).Name != "client1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchClientAll(t *testing.T){
	n, _ := Search("client", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchDbag(t *testing.T){
	d, _ := Search("data_bag1", "foo:dbag_item_1")
	if len(d) == 0 {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchDbagAll(t *testing.T){
	d, _ := Search("data_bag1", "*:*")
	if len(d) != 1 {
		t.Errorf("Incorrect number of items returned, expected 1, got %d", len(d))
	}
}
<commit_msg>Fix search test<commit_after>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 search

import (
	"testing"
	"github.com/ctdk/goiardi/node"
	"github.com/ctdk/goiardi/role"
	"github.com/ctdk/goiardi/environment"
	"github.com/ctdk/goiardi/client"
	"github.com/ctdk/goiardi/data_bag"
	"fmt"
)

// Most search testing can be handled fine with chef-pedant, but that's no
// reason to not have some go tests for it too.

var node1 *node.Node
var node2 *node.Node
var node3 *node.Node
var node4 *node.Node
var role1 *role.Role
var role2 *role.Role
var role3 *role.Role
var role4 *role.Role
var env1 *environment.ChefEnvironment
var env2 *environment.ChefEnvironment
var env3 *environment.ChefEnvironment
var env4 *environment.ChefEnvironment
var client1 *client.Client
var client2 *client.Client
var client3 *client.Client
var client4 *client.Client
var dbag1 *data_bag.DataBag
var dbag2 *data_bag.DataBag
var dbag3 *data_bag.DataBag
var dbag4 *data_bag.DataBag

func makeSearchItems() int{
	/* Gotta populate the search index */
	nodes := make([]*node.Node, 4)
	roles := make([]*role.Role, 4)
	envs := make([]*environment.ChefEnvironment, 4)
	clients := make([]*client.Client, 4)
	dbags := make([]*data_bag.DataBag, 4)

	for i := 0; i < 4; i++ {
		nodes[i], _ = node.New(fmt.Sprintf("node%d",i))
		nodes[i].Save()
		roles[i], _ = role.New(fmt.Sprintf("role%d",i))
		roles[i].Save()
		envs[i], _ = environment.New(fmt.Sprintf("env%d",i))
		envs[i].Save()
		clients[i], _ = client.New(fmt.Sprintf("client%d",i))
		clients[i].Save()
		dbags[i], _ = data_bag.New(fmt.Sprintf("data_bag%d",i))
		dbags[i].Save()
		dbi := make(map[string]interface{})
		dbi["id"] = fmt.Sprintf("dbi%d", i)
		dbi["foo"] = fmt.Sprintf("dbag_item_%d", i)
		dbags[i].NewDBItem(dbi)
	}
	node1 = nodes[0]
	node2 = nodes[1]
	node3 = nodes[2]
	node4 = nodes[3]
	role1 = roles[0]
	role2 = roles[1]
	role3 = roles[2]
	role4 = roles[3]
	env1 = envs[0]
	env2 = envs[1]
	env3 = envs[2]
	env4 = envs[3]
	client1 = clients[0]
	client2 = clients[1]
	client3 = clients[2]
	client4 = clients[3]
	dbag1 = dbags[0]
	dbag2 = dbags[1]
	dbag3 = dbags[2]
	dbag4 = dbags[3]

	/* Make this function return something so the compiler's happy building
	 * the tests. */
	return 1
}

var v = makeSearchItems()

func TestFoo(t *testing.T){
	return
}

/* Only basic search tests are here. The stronger tests are handled in
 * chef-pedant, but these tests are meant to check basic search functionality.
 */

func TestSearchNode(t *testing.T){
	n, _ := Search("node", "name:node1")
	if n[0].(*node.Node).Name != "node1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchNodeAll(t *testing.T){
	n, _ := Search("node", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchRole(t *testing.T){
	r, _ := Search("role", "name:role1")
	if r[0].(*role.Role).Name != "role1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchRoleAll(t *testing.T){
	n, _ := Search("role", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchEnv(t *testing.T){
	e, _ := Search("environment", "name:env1")
	if e[0].(*environment.ChefEnvironment).Name != "env1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchEnvAll(t *testing.T){
	n, _ := Search("environment", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchClient(t *testing.T){
	c, _ := Search("client", "name:client1")
	if c[0].(*client.Client).Name != "client1" {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchClientAll(t *testing.T){
	n, _ := Search("client", "*:*")
	if len(n) != 4 {
		t.Errorf("Incorrect number of items returned, expected 4, got %d", len(n))
	}
}

func TestSearchDbag(t *testing.T){
	d, _ := Search("data_bag1", "foo:dbag_item_1")
	if len(d) == 0 {
		t.Errorf("nothing returned from search")
	}
}

func TestSearchDbagAll(t *testing.T){
	d, _ := Search("data_bag1", "*:*")
	if len(d) != 1 {
		t.Errorf("Incorrect number of items returned, expected 1, got %d", len(d))
	}
}
<|endoftext|>


<commit_before>package sharedmap

type KeyType int   // Change this for a different key type
type ValueType int // Change this for a different value type

// A concurrency-safe Map implementation based on
// synchronization through channel I/O instead of
// mutexes
type SharedMap struct {
	cGetItem chan itemYield
	cAddItem chan itemAdd
	cDelItem chan itemDel
	cMapSize chan chan int
}

type itemYield struct {
	key    KeyType
	cValue chan ValueType
}

type itemAdd struct {
	key       KeyType
	val       ValueType
	cModified chan bool
}

type itemDel struct {
	key      KeyType
	cSuccess chan bool
}

type MapInfo struct {
	Length, Capacity int
}

// Gets the value corresponding to the given key from the SharedMap
func (s *SharedMap) Get(key KeyType) ValueType {
	g := itemYield{key, make(chan ValueType, 1)}
	s.cGetItem <- g
	return <-g.cValue
}

// Adds/updates the given key-value pair to the SharedMap and returns
// true if the key already existed
func (s *SharedMap) Add(key KeyType, value ValueType) bool {
	a := itemAdd{key, value, make(chan bool, 1)}
	s.cAddItem <- a
	return <-a.cModified
}

// Deletes the given key from the SharedMap and returns true if the
// key was found and deleted
func (s *SharedMap) Delete(key KeyType) bool {
	d := itemDel{key, make(chan bool, 1)}
	s.cDelItem <- d
	return <-d.cSuccess
}

// Returns the number of elements stored in the map
func (s *SharedMap) Size() int {
	rchan := make(chan int)
	s.cMapSize <- rchan
	return <-rchan
}

// Goroutine to handle the internal state of SharedMap
func (s *SharedMap) MapManager() {
	m := map[KeyType]ValueType{}
	for {
		select {
		case info := <-s.cAddItem:
			_, ok := m[info.key]
			m[info.key] = info.val
			info.cModified <- ok
		case info := <-s.cGetItem:
			info.cValue <- m[info.key]
		case info := <-s.cDelItem:
			_, ok := m[info.key]
			delete(m, info.key)
			info.cSuccess <- ok
		case rchan := <-s.cMapSize:
			rchan <- len(m)
		}
	}
}

// Creates a new SharedMap
func NewSharedMap() *SharedMap {
	s := &SharedMap{
		make(chan itemYield),
		make(chan itemAdd),
		make(chan itemDel),
		make(chan chan int),
	}
	go s.MapManager()
	return s
}
<commit_msg>Removed unused MapInfo struct<commit_after>package sharedmap

type KeyType int   // Change this for a different key type
type ValueType int // Change this for a different value type

// A concurrency-safe Map implementation based on
// synchronization through channel I/O instead of
// mutexes
type SharedMap struct {
	cGetItem chan itemYield
	cAddItem chan itemAdd
	cDelItem chan itemDel
	cMapSize chan chan int
}

type itemYield struct {
	key    KeyType
	cValue chan ValueType
}

type itemAdd struct {
	key       KeyType
	val       ValueType
	cModified chan bool
}

type itemDel struct {
	key      KeyType
	cSuccess chan bool
}

// Gets the value corresponding to the given key from the SharedMap
func (s *SharedMap) Get(key KeyType) ValueType {
	g := itemYield{key, make(chan ValueType, 1)}
	s.cGetItem <- g
	return <-g.cValue
}

// Adds/updates the given key-value pair to the SharedMap and returns
// true if the key already existed
func (s *SharedMap) Add(key KeyType, value ValueType) bool {
	a := itemAdd{key, value, make(chan bool, 1)}
	s.cAddItem <- a
	return <-a.cModified
}

// Deletes the given key from the SharedMap and returns true if the
// key was found and deleted
func (s *SharedMap) Delete(key KeyType) bool {
	d := itemDel{key, make(chan bool, 1)}
	s.cDelItem <- d
	return <-d.cSuccess
}

// Returns the number of elements stored in the map
func (s *SharedMap) Size() int {
	rchan := make(chan int)
	s.cMapSize <- rchan
	return <-rchan
}

// Goroutine to handle the internal state of SharedMap
func (s *SharedMap) MapManager() {
	m := map[KeyType]ValueType{}
	for {
		select {
		case info := <-s.cAddItem:
			_, ok := m[info.key]
			m[info.key] = info.val
			info.cModified <- ok
		case info := <-s.cGetItem:
			info.cValue <- m[info.key]
		case info := <-s.cDelItem:
			_, ok := m[info.key]
			delete(m, info.key)
			info.cSuccess <- ok
		case rchan := <-s.cMapSize:
			rchan <- len(m)
		}
	}
}

// Creates a new SharedMap
func NewSharedMap() *SharedMap {
	s := &SharedMap{
		make(chan itemYield),
		make(chan itemAdd),
		make(chan itemDel),
		make(chan chan int),
	}
	go s.MapManager()
	return s
}
<|endoftext|>


<commit_before>package shell

var Zsh = Shell(`# Put the line below in ~/.zshrc:
#
#   eval "$(jump shell zsh)"
#
# The following lines are autogenerated:

__jump_chpwd() {
  jump chdir
}

typeset -gaU chpwd_functions
chpwd_functions+=__jump_chpwd

j() {
  local dir="$(jump cd $@)"
  test -d "$dir" && cd "$dir"
}

jump_completion() {
  reply=($(jump hint $1 | head -3))
}

compctl -U -K jump_completion j
`)
<commit_msg>No need to limit the results in zsh autocomplete anymore<commit_after>package shell

var Zsh = Shell(`# Put the line below in ~/.zshrc:
#
#   eval "$(jump shell zsh)"
#
# The following lines are autogenerated:

__jump_chpwd() {
  jump chdir
}

typeset -gaU chpwd_functions
chpwd_functions+=__jump_chpwd

j() {
  local dir="$(jump cd $@)"
  test -d "$dir" && cd "$dir"
}

jump_completion() {
  reply=($(jump hint $1))
}

compctl -U -K jump_completion j
`)
<|endoftext|>


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

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

const bufferSize int = 2048
const maxMessageSize int = 20480 // Ensure that inbound messages don't cause the signalbox to run out of memory.

type Peer struct {
	Id     string          // The unique identifier of the peer.
	socket *websocket.Conn // The socket for writing to the peer.
}

type Room struct {
	Room string // The unique name of the room (id).
}

type SignalBox struct {
	Peers        map[string]*Peer            // All the peers currently inside this signalbox.
	Rooms        map[string]*Room            // All the rooms currently inside this signalbox.
	RoomContains map[string]map[string]*Peer // All the peers currently inside a room.
	PeerIsIn     map[string]map[string]*Room // All the rooms a peer is currently inside.
}

type Message struct {
	msgSocket *websocket.Conn // The socket that the message was broadcast across.
	msgBody   string          // The body of the broadcasted message.
}

func messagePump(config Configuration, msg chan Message, ws *websocket.Conn) {
	ws.SetReadDeadline(time.Now().Add(config.SocketTimeout * time.Second))
	ws.SetWriteDeadline(time.Now().Add(config.SocketTimeout * time.Second))

	for {
		_, reader, err := ws.NextReader()

		if err != nil {
			// Unable to get reader from socket - probably closed, tell the signalbox.
			log.Printf("ERROR - messagePump: Can't read from %p, closing", ws)
			log.Print(err)
			msg <- Message{ws, "/close"}

			return
		}

		buffer := make([]byte, bufferSize)
		n, err := reader.Read(buffer)
		socketContents := string(buffer[0:n])
		log.Printf("socketContents: %s %d %d %t %t\n", socketContents, n, bufferSize, err == nil, n == bufferSize)

		for err != io.EOF && (len(socketContents)-bufferSize) < maxMessageSize {
			// filled the buffer - we might have more stuff in the message.
			n, err = reader.Read(buffer)
			socketContents = socketContents + string(buffer[0:n])
			log.Printf("socketContents: %s\n", socketContents)
		}

		// n, err = reader.Read(buffer)
		// log.Printf("aaa: %t\n", err == io.EOF)

		// if err != nil {
		// 	log.Printf("ERROR - messagePump: Unable to read from websocket.")
		// 	log.Print(err)
		// 	continue
		// }

		// Recieved content from socket - extend read deadline.
		ws.SetReadDeadline(time.Now().Add(config.SocketTimeout * time.Second))

		// Pump the new message into the signalbox.
		var message string
		json.Unmarshal([]byte(socketContents), &message)

		log.Printf("Recieved %s from %p", message, ws)

		msg <- Message{ws, message}
	}
}

func signalbox(config Configuration, msg chan Message) {
	s := SignalBox{make(map[string]*Peer),
		make(map[string]*Room),
		make(map[string]map[string]*Peer),
		make(map[string]map[string]*Room)}

	for {
		m := <-msg

		// Message matches a primus heartbeat message. Lightly massage the connection
		// with pong brand baby oil to keep everything running smoothly.
		if strings.HasPrefix(m.msgBody, "primus::ping::") {
			pong := fmt.Sprintf("primus::pong::%s", strings.Split(m.msgBody, "primus::ping::")[1])
			b, _ := json.Marshal(pong)

			m.msgSocket.WriteMessage(websocket.TextMessage, b)
			m.msgSocket.SetWriteDeadline(time.Now().Add(config.SocketTimeout * time.Second))
			continue
		}

		action, messageBody, err := ParseMessage(m.msgBody)
		if err != nil {
			log.Printf("ERROR - signalbox: Unable to parse message.")
			log.Print(err)
			continue
		}

		s, err = action(messageBody, m.msgSocket, s)
		if err != nil {
			log.Printf("ERROR - signalbox: Unable to update state.")
			log.Print(err)
		}
	}
}

func main() {
	log.Printf("INFO - Started SignalBox\n")

	configFile := "signalbox.json"
	if len(os.Args) > 1 {
		configFile = os.Args[1]
	}

	config, err := parseConfiguration(configFile)
	if err != nil {
		log.Printf("ERROR - main: Unable to parse config %s - using defaults.", err)
	}

	msg := make(chan Message)
	go signalbox(config, msg)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.Error(w, "Method not allowed", 405)
			return
		}

		// Upgrade the HTTP server connection to the WebSocket protocol.
		ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		if err != nil {
			log.Printf("ERROR - http.HandleFunc: %s", err)
			return
		}

		// Start pumping messages from this websocket into the signal box.
		go messagePump(config, msg, ws)
	})

	err = http.ListenAndServe(config.ListenAddress, nil)
	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}
}
<commit_msg>Tidied up deborking messages in signalbox.go.<commit_after>/*
 * Copyright (c) Clinton Freeman 2014
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

const bufferSize int = 2048
const maxMessageSize int = 20480 // Ensure that inbound messages don't cause the signalbox to run out of memory.

type Peer struct {
	Id     string          // The unique identifier of the peer.
	socket *websocket.Conn // The socket for writing to the peer.
}

type Room struct {
	Room string // The unique name of the room (id).
}

type SignalBox struct {
	Peers        map[string]*Peer            // All the peers currently inside this signalbox.
	Rooms        map[string]*Room            // All the rooms currently inside this signalbox.
	RoomContains map[string]map[string]*Peer // All the peers currently inside a room.
	PeerIsIn     map[string]map[string]*Room // All the rooms a peer is currently inside.
}

type Message struct {
	msgSocket *websocket.Conn // The socket that the message was broadcast across.
	msgBody   string          // The body of the broadcasted message.
}

func messagePump(config Configuration, msg chan Message, ws *websocket.Conn) {
	ws.SetReadDeadline(time.Now().Add(config.SocketTimeout * time.Second))
	ws.SetWriteDeadline(time.Now().Add(config.SocketTimeout * time.Second))

	for {
		_, reader, err := ws.NextReader()

		if err != nil {
			// Unable to get reader from socket - probably closed, tell the signalbox.
			log.Printf("ERROR - messagePump: Can't read from %p, closing", ws)
			log.Print(err)
			msg <- Message{ws, "/close"}

			return
		}

		buffer := make([]byte, bufferSize)
		n, err := reader.Read(buffer)
		socketContents := string(buffer[0:n])

		for err != io.EOF && (len(socketContents)-bufferSize) < maxMessageSize {
			// filled the buffer - we might have more stuff in the message.
			n, err = reader.Read(buffer)
			socketContents = socketContents + string(buffer[0:n])
		}

		if err != io.EOF {
			log.Printf("ERROR - messagePump: Unable to read from websocket.")
			log.Print(err)
			continue
		}

		// Recieved content from socket - extend read deadline.
		ws.SetReadDeadline(time.Now().Add(config.SocketTimeout * time.Second))

		// Pump the new message into the signalbox.
		var message string
		json.Unmarshal([]byte(socketContents), &message)

		log.Printf("Recieved %s from %p", message, ws)

		msg <- Message{ws, message}
	}
}

func signalbox(config Configuration, msg chan Message) {
	s := SignalBox{make(map[string]*Peer),
		make(map[string]*Room),
		make(map[string]map[string]*Peer),
		make(map[string]map[string]*Room)}

	for {
		m := <-msg

		// Message matches a primus heartbeat message. Lightly massage the connection
		// with pong brand baby oil to keep everything running smoothly.
		if strings.HasPrefix(m.msgBody, "primus::ping::") {
			pong := fmt.Sprintf("primus::pong::%s", strings.Split(m.msgBody, "primus::ping::")[1])
			b, _ := json.Marshal(pong)

			m.msgSocket.WriteMessage(websocket.TextMessage, b)
			m.msgSocket.SetWriteDeadline(time.Now().Add(config.SocketTimeout * time.Second))
			continue
		}

		action, messageBody, err := ParseMessage(m.msgBody)
		if err != nil {
			log.Printf("ERROR - signalbox: Unable to parse message.")
			log.Print(err)
			continue
		}

		s, err = action(messageBody, m.msgSocket, s)
		if err != nil {
			log.Printf("ERROR - signalbox: Unable to update state.")
			log.Print(err)
		}
	}
}

func main() {
	log.Printf("INFO - Started SignalBox\n")

	configFile := "signalbox.json"
	if len(os.Args) > 1 {
		configFile = os.Args[1]
	}

	config, err := parseConfiguration(configFile)
	if err != nil {
		log.Printf("ERROR - main: Unable to parse config %s - using defaults.", err)
	}

	msg := make(chan Message)
	go signalbox(config, msg)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.Error(w, "Method not allowed", 405)
			return
		}

		// Upgrade the HTTP server connection to the WebSocket protocol.
		ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		if err != nil {
			log.Printf("ERROR - http.HandleFunc: %s", err)
			return
		}

		// Start pumping messages from this websocket into the signal box.
		go messagePump(config, msg, ws)
	})

	err = http.ListenAndServe(config.ListenAddress, nil)
	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"github.com/APTrust/bagit/util/fileutil"
	"os"
)

type Bagger struct {
	Bag            *Bag
	Profile        *BagItProfile
	PayloadDir     string
	TagValues      map[string]string
	ForceOverwrite bool
	errors         []string
}

// NewBagger creates a new Bagger.
//
// Param bagPath is the path to the location where the bag should
// be built. This should be a directory name. The bagger will
// create the directory, if it does not already exist. If the directory
// exists, the bagger will not overwrite it, unless the forceOverwrite
// (see below) is true.
//
// Param payloadDir is the name of the directory containing the files to be
// bagged.
//
// Param profile is the BagIt profile that describes the requirements
// for the bag.
//
// Param tagValues is a map of tag values that will be used
// to create tag files.
//
// If param forceOverwrite is true, the bagger will delete and then
// overwrite the directory at bagPath.
func NewBagger(bagPath, payloadDir string, profile *BagItProfile, tagValues map[string]string, forceOverwrite bool) *Bagger {
	return &Bagger{
		Bag:            NewBag(bagPath),
		Profile:        profile,
		PayloadDir:     payloadDir,
		TagValues:      tagValues,
		ForceOverwrite: forceOverwrite,
		errors:         make([]string, 0),
	}
}

/*

- Validate Profile
- Validate that all defaults are present
- Include progress callback or io.Writer for writing progress messages
- Add all payload files to bag in working dir
- Build tag files
- Create manifests

- Tar bag if serialization is required and serlialization format is tar?
- Provide a way of specifying tag files and where to put them?
- Parse existing tag files, and skip defaults if they're already defined in existing tag files?

*/

func (bagger *Bagger) BuildBag() bool {
	ok := bagger.makeDir()
	if !ok {
		return false
	}
	return true
}

// makeDir creates the directory in which we'll assemble the bag,
// performing some safety checks along the way. Returns true on
// success, false otherwise.
func (bagger *Bagger) makeDir() bool {
	if fileutil.FileExists(bagger.Bag.Path) {
		if bagger.ForceOverwrite {
			if fileutil.LooksSafeToDelete(bagger.Bag.Path, 12, 3) {
				err := os.RemoveAll(bagger.Bag.Path)
				if err != nil {
					bagger.addError("Error removing existing %s", bagger.Bag.Path, err.Error())
					return false
				}
			} else {
				bagger.addError("%s already exists and does not look safe to delete. "+
					"Choose a different path for this bag.",
					bagger.Bag.Path)
				return false
			}
		} else {
			bagger.addError("%s already exists. Use forceOverwrite=true to replace it.",
				bagger.Bag.Path)
			return false
		}
	}
	err := os.MkdirAll(bagger.Bag.Path, 0755)
	if err != nil {
		bagger.addError(err.Error())
		return false
	}
	return true
}

// addError adds a message to the list of bagging errors.
func (bagger *Bagger) addError(format string, a ...interface{}) {
	bagger.errors = append(bagger.errors, fmt.Sprintf(format, a...))
}
<commit_msg>Sketching out bagger work<commit_after>package core

import (
	"fmt"
	"github.com/APTrust/bagit/util/fileutil"
	"os"
)

type Bagger struct {
	Bag            *Bag
	Profile        *BagItProfile
	PayloadDir     string
	TagValues      map[string]string
	ForceOverwrite bool
	errors         []string
}

// NewBagger creates a new Bagger.
//
// Param bagPath is the path to the location where the bag should
// be built. This should be a directory name. The bagger will
// create the directory, if it does not already exist. If the directory
// exists, the bagger will not overwrite it, unless the forceOverwrite
// (see below) is true. If bagPath ends with ".tar", the bag will be
// created as a tar file instead of as a directory.
//
// Param payloadDir is the name of the directory containing the files to be
// bagged.
//
// Param profile is the BagIt profile that describes the requirements
// for the bag.
//
// Param tagValues is a map of tag values that will be used
// to create tag files.
//
// If param forceOverwrite is true, the bagger will delete and then
// overwrite the directory at bagPath.
func NewBagger(bagPath, payloadDir string, profile *BagItProfile, tagValues map[string]string, forceOverwrite bool) *Bagger {
	return &Bagger{
		Bag:            NewBag(bagPath),
		Profile:        profile,
		PayloadDir:     payloadDir,
		TagValues:      tagValues,
		ForceOverwrite: forceOverwrite,
		errors:         make([]string, 0),
	}
}

/*

- Validate Profile
- Validate that all defaults are present
- Include progress callback or io.Writer for writing progress messages
- Add all payload files to bag in working dir
- Build tag files
- Create manifests

- Tar bag if serialization is required and serlialization format is tar?
- Provide a way of specifying tag files and where to put them?
- Parse existing tag files, and skip defaults if they're already defined in existing tag files?

*/

func (bagger *Bagger) BuildBag() bool {
	ok := bagger.initFileOrDir()
	if !ok {
		return false
	}

	// copy payload files
	// write tag files
	// create manifests

	return true
}

// initFileOrDir creates the directory in which we'll assemble the bag,
// performing some safety checks along the way. Returns true on
// success, false otherwise.
func (bagger *Bagger) initFileOrDir() bool {
	if fileutil.FileExists(bagger.Bag.Path) {
		if bagger.ForceOverwrite {
			if fileutil.LooksSafeToDelete(bagger.Bag.Path, 12, 3) {
				err := os.RemoveAll(bagger.Bag.Path)
				if err != nil {
					bagger.addError("Error removing existing %s", bagger.Bag.Path, err.Error())
					return false
				}
			} else {
				bagger.addError("%s already exists and does not look safe to delete. "+
					"Choose a different path for this bag.",
					bagger.Bag.Path)
				return false
			}
		} else {
			bagger.addError("%s already exists. Use forceOverwrite=true to replace it.",
				bagger.Bag.Path)
			return false
		}
	}
	err := os.MkdirAll(bagger.Bag.Path, 0755)
	if err != nil {
		bagger.addError(err.Error())
		return false
	}
	return true
}

// copyPayload copies files from PayloadDir into the bag's data
// directory.
func (bagger *Bagger) copyPayload() bool {
	// -> os.Copy() files into dir/data for bag as directory, or
	// -> tarWriter.AddToArchive() if writing straight to a tar file
	//
	// calculate checksums along the way.
	//
	// Adapt fileutil.CalculateChecksums, to include a writer,
	// which can be a file writer or a tar file writer.
	return true
}

// addError adds a message to the list of bagging errors.
func (bagger *Bagger) addError(format string, a ...interface{}) {
	bagger.errors = append(bagger.errors, fmt.Sprintf(format, a...))
}
<|endoftext|>


<commit_before>package datatablessrv

import (
	"fmt"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

// Copyright (c) 2017 Escape Velocity, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// This package handles the server side processing of an AJAX request for DataTables
// For details on the parameters and the results, read the datatables documentation at
// https://datatables.net/manual/server-side

// SortDir is the direction of the sort (ascending/descending)
type SortDir int

const (
	// Asc for ascending sorting
	Asc SortDir = iota
	// Desc for descending sorting
	Desc
)

// OrderInfo is
type OrderInfo struct {
	ColNum    int // Which column to apply sorting to
	Direction SortDir
}

// ColData is
type ColData struct {
	Name string // columns[i][name] Column's name, as defined by columns.name.
	Data string // columns[i][data] Column's data source, as defined by columns.data.
	// It is poss
	Searchable bool // columns[i][searchable]	boolean	Flag to indicate if this column is searchable (true) or not (false).
	// This is controlled by columns.searchable.
	Orderable bool // columns[i][orderable] Flag to indicate if this column is orderable (true) or not (false).
	// This is controlled by columns.orderable.
	Searchval string // columns[i][search][value] Search value to apply to this specific column.
	UseRegex  bool   // columns[i][search][regex]
	// Flag to indicate if the search term for this column should be treated as regular expression (true) or not (false).
	// As with global search, normally server-side processing scripts will not perform regular expression searching
	// for performance reasons on large data sets, but it is technically possible and at the discretion of your script.
}

// DataTablesInfo is
type DataTablesInfo struct {
	hasFilter bool // There is a filter on the data to apply
	Draw      int  // Draw counter. This is used by DataTables to ensure that the Ajax returns
	// from server-side processing requests are drawn in sequence by DataTables
	// (Ajax requests are asynchronous and thus can return out of sequence).
	// This is used as part of the draw return parameter (see below).
	Start int // Paging first record indicator.
	// This is the start point in the current data set (0 index based - i.e. 0 is the first record).
	Length int // Number of records that the table can display in the current draw.
	// It is expected that the number of records returned will be equal to this number, unless the server has fewer records to return.
	//  Note that this can be -1 to indicate that all records should be returned (although that negates any benefits of server-side processing!)
	Searchval string // Global search value. To be applied to all columns which have searchable as true.
	UseRegex  bool   //	true if the global filter should be treated as a regular expression for advanced searching.
	//  Note that normally server-side processing scripts will not perform regular expression
	//  searching for performance reasons on large data sets, but it is technically possible and at the discretion of your script.
	Order   []OrderInfo
	Columns []ColData
}

// MySQLFilter generates the filter for a mySQL query based on the request and a map of the strings
// to the database entries.  Note if the field is searchable and we don't have a map, this generates
// an error,
// It is assumed that there is a fulltext index on fields in very large tables in order to optimize
// performance of this generated query
// This code will generate a string of the form
//        MATCH(field1,field2,field3) AGAINST('searchval')
//     OR LOCATE(field1,'field1searchval')!=0
//     OR LOCATE(field3,'field3searchval')!=0
// If regular expressions are specified (useRegex) then it will generate a match of
//         field1 REGEXP 'searchval'
//      OR field1 REGEXP 'field1searchval'
//      OR field2 REGEXP 'serchval'
//      OR field3 REGEXP 'searchval'
//      OR field3 REGEXP 'field3searchval'
// Note: If the searchval doesn't actually contain wildcard values (^$.*+|(){}[]?) then the search value bit is actually
// cleared by ParseDatatablesRequest so that we never actually see it
func (di *DataTablesInfo) MySQLFilter(SQLFieldMap map[string]string) (res string, err error) {
	// If there is no filter to be applied, we are done and can skip out
	if !di.hasFilter {
		return
	}
	matchfields := ""
	extramatch := ""
	extra := ""
	query := ""
	for _, coldata := range di.Columns {
		// If this column isn't searchable, then we can just skip to the next field
		if !coldata.Searchable {
			continue
		}
		sqlName, isFound := SQLFieldMap[coldata.Data]
		if !isFound {
			err = fmt.Errorf("Column Data Name %v not found in SQL FieldMap", coldata.Data)
			return
		}
		// Do we have a global search value to match against
		if di.Searchval != "" {
			// For wildcards we have to generate a REGEXP request
			if di.UseRegex {
				query += extra + sqlName + " REGEXP '" + di.Searchval + "'"
				extra = " OR "
			} else {
				// In the non wildcard case, we gather up all the fields
				matchfields += extramatch + sqlName
				extramatch = ","
			}
		}
		// Do we have a search value for just this field
		if coldata.Searchval != "" {
			// If it is a wildcard, we generate a REGEXP request
			if coldata.UseRegex {
				query += extra + sqlName + " REGEXP '" + coldata.Searchval + "'"
			} else {
				// Simple match can use the LOCATE function
				query += extra + "LOCATE(" + sqlName + ", '" + coldata.Searchval + "') > 0"
			}
			extra = " OR "
		}
	}
	if di.Searchval != "" && !di.UseRegex {
		res = " MATCH(" + matchfields + ") AGAINST '" + di.Searchval + "')"
	}
	res += query
	return
}

// MySQLOrderby generates the order by clause for a mySQL query based on the request and a map of the strings
// to the database entries.  Note if the field is orderable and we don't have a map, this generates
// an error
func (di *DataTablesInfo) MySQLOrderby(SQLFieldMap map[string]string) (res string, err error) {
	extra := " ORDER BY "
	// Go through the list of requested items to order
	for _, orderItem := range di.Order {
		// Make sure it refers to one of the columns that was passed in
		if orderItem.ColNum > len(di.Columns) {
			err = fmt.Errorf("Order Column %v out of range %v", orderItem.ColNum, len(di.Columns))
			return
		}
		// Get the data for that column and figure out if the name is one of the fields that we
		// allow in the table
		coldata := di.Columns[orderItem.ColNum]
		sqlName, isFound := SQLFieldMap[coldata.Data]
		if !isFound {
			err = fmt.Errorf("Column Data Name %v not found in SQL FieldMap", coldata.Data)
			return
		}
		res += extra + sqlName
		if orderItem.Direction == Desc {
			res += " DESC"
		}
		extra = ","
	}
	// If we didn't get anything to sort on then we return a simple "ORDER BY 1"
	if res == "" {
		res = " ORDER BY 1"
	}
	return
}

// parseParts takes the split out parts of the field string, verifies that they are
// syntactically valid and then parses them out
//  for example columns[i][search][regex] would come in as
//       field:  'columns[2][search][regex]'
//       nameparts[0]  'columns'
//       nameparts[1]  '2]'
//       nameparts[2]  'search]'
//       nameparts[3]  'regex]'
func parseParts(field string, nameparts []string) (index int, elem1 string, elem2 string, err error) {
	defaultErr := fmt.Errorf("Invalid order[] element %v", field)
	numRegex, err := regexp.Compile("^[0-9]+]$")
	if err != nil {
		return
	}
	elemRegex, err := regexp.Compile("^[a-z]+]$")
	if err == nil {
		return
	}
	if len(nameparts) != 3 && len(nameparts) != 4 {
		err = defaultErr
		return
	}
	// Make sure it is a number followed by the closing ]
	if !numRegex.MatchString(nameparts[1]) {
		err = defaultErr
		return
	}
	// And parse it as a number to make sure
	index, err = strconv.Atoi(nameparts[1])
	if err != nil {
		return
	}
	// Check that the next index is a name token followed by a ]
	if !elemRegex.MatchString(nameparts[2]) {
		err = defaultErr
		return
	}
	// Strip off the trailing ]
	elem1 = strings.TrimSuffix(nameparts[2], "]")
	// If we had a third element, check to make sure it is also close by a ]
	if len(nameparts) == 4 {
		if !elemRegex.MatchString(nameparts[3]) {
			err = defaultErr
			return
		}
		// And trim off the ]
		elem2 = strings.TrimSuffix(nameparts[3], "]")
	}
	// Let's sanity check and make sure they aren't returing an index that is way out of range.
	// We shall assume that no more than 200 columns are being returned
	if index > 200 || index < 0 {
		err = defaultErr
	}
	return
}

// ParseDatatablesRequest checks the HTTP request to see if it corresponds
// to a datatables AJAX data request and parses the request data into
// the DataTablesInfo structure.
func ParseDatatablesRequest(r *http.Request) (res *DataTablesInfo, err error) {
	foundDraw := false
	res = &DataTablesInfo{}
	// Let the request parst the post values into the r.Form structure
	err = r.ParseForm()
	if err != nil {
		return
	}
	for field, value := range r.Form {
		val0 := value[0]
		nameparts := strings.Split(field, "[")
		switch nameparts[0] {
		case "draw":
			foundDraw = true
			res.Draw, err = strconv.Atoi(val0)
		case "start":
			res.Start, err = strconv.Atoi(val0)
		case "length":
			res.Length, err = strconv.Atoi(val0)
		case "search":
			if len(nameparts) != 2 {
				err = fmt.Errorf("Invalid search[] element %v", field)
			} else if nameparts[1] == "value]" {
				res.Searchval = val0
			} else if nameparts[1] == "regex]" {
				res.UseRegex = (val0 == "true")
			} else {
				err = fmt.Errorf("Invalid search[] element %v", field)
			}
		case "order":
			index, elem, _, err := parseParts(field, nameparts)
			if err == nil {
				for len(res.Order) < index {
					res.Order = append(res.Order, OrderInfo{})
				}
				switch elem {
				case "column":
					res.Order[index-1].ColNum, err = strconv.Atoi(val0)
				case "dir":
					if val0 == "desc" {
						res.Order[index-1].Direction = Desc
					} else {
						res.Order[index-1].Direction = Asc
					}
				}
			}
		case "columns":
			index, elem, elem2, err := parseParts(field, nameparts)
			// First make sure we have a
			if err == nil {
				for len(res.Columns) < index {
					res.Columns = append(res.Columns, ColData{})
				}

			}
			switch elem {
			case "data":
				res.Columns[index-1].Data = val0
			case "name":
				res.Columns[index-1].Name = val0
			case "searchable":
				res.Columns[index-1].Searchable = (val0 != "false")
			case "orderable":
				res.Columns[index-1].Orderable = (val0 != "false")
			case "search":
				switch elem2 {
				case "value":
					res.Columns[index-1].Searchval = val0
				case "regex":
					res.Columns[index-1].UseRegex = (val0 != "false")
				}
			}
		}
	}
	// If no Draw was specified in the request, then this isn't a datatables request and we can safely ignore it
	if !foundDraw {
		res = nil
		err = fmt.Errorf("Not a DataTables request")
	}
	res.hasFilter = false
	// We have a valid datatables request, do a tiny bit of optimization. If they requested a wild card
	// but the searchval doesn't have a wild card in it, then we can optimize it to just use regular
	// searching (which will be significantly faster)
	// These are the possible wildcard values (^$.*+|(){}[]?) then the search value bit is actually
	if res.Searchval != "" {
		res.hasFilter = true
		if res.UseRegex && !strings.ContainsAny(res.Searchval, "^$.*+|(){}[]?") {
			res.UseRegex = false
		}
	}
	// Check whether any of the columns have searchable data
	for _, coldata := range res.Columns {
		// If this column is searchable check to make sure it contains something
		if coldata.Searchable && coldata.Searchval != "" {
			// There is something, so we do have a filter
			res.hasFilter = true
			// See if this filter is worthy of a regular expression
			if coldata.UseRegex && !strings.ContainsAny(coldata.Searchval, "^$.*+|(){}[]?") {
				coldata.UseRegex = false
			}
		}
	}
	return
}
<commit_msg>Rewrite to recover what I thought was lost.<commit_after>package datatablessrv

import (
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

// Copyright (c) 2017 Escape Velocity, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// This package handles the server side processing of an AJAX request for DataTables
// For details on the parameters and the results, read the datatables documentation at
// https://datatables.net/manual/server-side

// ErrNotDataTablesReq indicates that this is not being requested by Datatables
var ErrNotDataTablesReq = errors.New("Not a DataTables request")

// SortDir is the direction of the sort (ascending/descending)
type SortDir int

const (
	// Asc for ascending sorting
	Asc SortDir = iota
	// Desc for descending sorting
	Desc
)

// OrderInfo is
type OrderInfo struct {
	ColNum    int // Which column to apply sorting to
	Direction SortDir
}

// ColData is
type ColData struct {
	Name string // columns[i][name] Column's name, as defined by columns.name.
	Data string // columns[i][data] Column's data source, as defined by columns.data.
	// It is poss
	Searchable bool // columns[i][searchable]	boolean	Flag to indicate if this column is searchable (true) or not (false).
	// This is controlled by columns.searchable.
	Orderable bool // columns[i][orderable] Flag to indicate if this column is orderable (true) or not (false).
	// This is controlled by columns.orderable.
	Searchval string // columns[i][search][value] Search value to apply to this specific column.
	UseRegex  bool   // columns[i][search][regex]
	// Flag to indicate if the search term for this column should be treated as regular expression (true) or not (false).
	// As with global search, normally server-side processing scripts will not perform regular expression searching
	// for performance reasons on large data sets, but it is technically possible and at the discretion of your script.
}

// DataTablesInfo is
type DataTablesInfo struct {
	hasFilter bool // Indicates there is a filter on the data to apply
	Draw      int  // Draw counter. This is used by DataTables to ensure that the Ajax returns
	// from server-side processing requests are drawn in sequence by DataTables
	// (Ajax requests are asynchronous and thus can return out of sequence).
	// This is used as part of the draw return parameter (see below).
	Start int // Paging first record indicator.
	// This is the start point in the current data set (0 index based - i.e. 0 is the first record).
	Length int // Number of records that the table can display in the current draw.
	// It is expected that the number of records returned will be equal to this number, unless the server has fewer records to return.
	//  Note that this can be -1 to indicate that all records should be returned (although that negates any benefits of server-side processing!)
	Searchval string // Global search value. To be applied to all columns which have searchable as true.
	UseRegex  bool   //	true if the global filter should be treated as a regular expression for advanced searching.
	//  Note that normally server-side processing scripts will not perform regular expression
	//  searching for performance reasons on large data sets, but it is technically possible and at the discretion of your script.
	Order   []OrderInfo
	Columns []ColData
}

// MySQLFilter generates the filter for a mySQL query based on the request and a map of the strings
// to the database entries.  Note if the field is searchable and we don't have a map, this generates
// an error
// It is assumed that there is a fulltext index on fields in very large tables in order to optimize
// performance of this generated query
// We have several things that we can generate here.
// For the global string, (in di.Searchval) with no Regex we would generate something like
//  MATCH (field1,field2,field3) AGAINST('searchval')
// If an individual entry has a searchval with no Regex we generate
//  MATCH (field1) AGAINST('searchval')
// If we have a global string with a regex, we generate
//   field1 REGEX 'searchval' OR field2 REGEX 'searchval' OR field3 REGEX 'searchval'
// Likewise for individual entries with a searchval we generate
//   field1 REGEX 'searchval'
// Note: If the searchval doesn't actually contain wildcard values (^$.*+|(){}[]?) then the search value bit is actually
// cleared by ParseDatatablesRequest so that we never actually see it
// NOTE: It is the responsibility of the caller to put the " WHERE " in front of the string when it
// is non-null.  This allows the filter to be used in other situations or where it may need to be part
// of a more complex logical operation
// NOTE: We assume that the Searchval strings have all been escaped and quoted so that we can put in the string
// with no potential SQL injection
func (di *DataTablesInfo) MySQLFilter(SQLFieldMap map[string]string) (res string, err error) {
	// In the case where there is no filter at all, we can just return quickly
	if !di.hasFilter {
		return
	}
	matchfields := ""
	extramatch := ""
	extra := ""
	for _, colData := range di.Columns {
		if colData.Searchable {
			// Map the external name to the actual field in the database
			sqlName, isFound := SQLFieldMap[colData.Data]
			if !isFound {
				err = fmt.Errorf("Column Data Name %v not found in SQL FieldMap", colData.Data)
				return
			}
			// If we have a global search val, generate a match against the global value for this field
			if di.Searchval != "" {
				// For wildcards we have to generate a REGEXP request
				if di.UseRegex {
					res += extra + sqlName + " REGEX " + di.Searchval
					extra = " OR "
				} else {
					// In the special case where we have a top level non wild card search value we want
					// to gang all the fields together into a single match string
					matchfields += extramatch + sqlName
					extramatch = ","
				}
			}
			// See if we have a search value specific for this individual element
			if colData.Searchval != "" {
				if colData.UseRegex {
					res += extra + sqlName + " REGEX " + colData.Searchval
				} else {
					res += extra + "MATCH(" + sqlName + ") AGAINST(" + colData.Searchval + ")"
				}
				extra = " OR "
			}
		}
	}
	// See if we had any fields to put in to the main match query
	if matchfields != "" {
		res += extra + "MATCH(" + matchfields + ") AGAINST(" + di.Searchval + ")"
	}
	return
}

// MySQLOrderby generates the order by clause for a mySQL query based on the request and a map of the strings
// to the database entries.  Note if the field is orderable and we don't have a map, this generates
// an error
func (di *DataTablesInfo) MySQLOrderby(SQLFieldMap map[string]string) (res string, err error) {
	extra := " ORDER BY "
	// Go through the list of requested items to order
	for _, orderItem := range di.Order {
		// Make sure that the column is in range
		if orderItem.ColNum >= len(di.Columns) {
			err = fmt.Errorf("Datatables Request order column %v out of range %v of columns", orderItem.ColNum, len(di.Columns))
			return
		}
		// Get the data for that column and figure out if the name is one of the fields that we
		// allow in the table
		colData := di.Columns[orderItem.ColNum]
		// Map the external name to the actual field in the database
		sqlName, isFound := SQLFieldMap[colData.Data]
		if !isFound {
			err = fmt.Errorf("Invalid datatables reuest column name %v", colData.Data)
			return
		}
		// Make sure we can actually order on the column (in theory this will never happen)
		if !colData.Orderable {
			err = fmt.Errorf("Datatables requested ordering on non-orderable column %v", colData.Data)
			return
		}
		// We have the column in the database, add it to the order by query that we are generating
		// The first time we have " ORDER BY " in the extra string, subsequent times we get a simple ","
		// which allows us to build up the string without backtracking to remove characters
		res += extra + sqlName
		if orderItem.Direction == Desc {
			res += " DESC"
		}
		extra = ","
	}
	// If for some reason we got to the end with no columns, then we give them the order by the first item
	if res == "" {
		res = extra + "1"
	}
	return
}

// parseParts takes the split out parts of the field string, verifies that they are
// syntactically valid and then parses them out
//  for example columns[i][search][regex] would come in as
//       field:  'columns[2][search][regex]'
//       nameparts[0]  'columns'
//       nameparts[1]  '2]'
//       nameparts[2]  'search]'
//       nameparts[3]  'regex]'
func parseParts(field string, nameparts []string) (index int, elem1 string, elem2 string, err error) {
	defaultErr := fmt.Errorf("Invalid order[] element %v", field)
	numRegex, err := regexp.Compile("^[0-9]+]$")
	if err != nil {
		return
	}
	elemRegex, err := regexp.Compile("^[a-z]+]$")
	if err == nil {
		return
	}
	if len(nameparts) != 3 && len(nameparts) != 4 {
		err = defaultErr
		return
	}
	// Make sure it is a number followed by the closing ]
	if !numRegex.MatchString(nameparts[1]) {
		err = defaultErr
		return
	}
	// And parse it as a number to make sure
	index, err = strconv.Atoi(nameparts[1])
	if err != nil {
		return
	}
	// Check that the next index is a name token followed by a ]
	if !elemRegex.MatchString(nameparts[2]) {
		err = defaultErr
		return
	}
	// Strip off the trailing ]
	elem1 = strings.TrimSuffix(nameparts[2], "]")
	// If we had a third element, check to make sure it is also close by a ]
	if len(nameparts) == 4 {
		if !elemRegex.MatchString(nameparts[3]) {
			err = defaultErr
			return
		}
		// And trim off the ]
		elem2 = strings.TrimSuffix(nameparts[3], "]")
	}
	// Let's sanity check and make sure they aren't returing an index that is way out of range.
	// We shall assume that no more than 200 columns are being returned
	if index > 200 || index < 0 {
		err = defaultErr
	}
	return
}

// ParseDatatablesRequest checks the HTTP request to see if it corresponds
// to a datatables AJAX data request and parses the request data into
// the DataTablesInfo structure.
func ParseDatatablesRequest(r *http.Request) (res *DataTablesInfo, err error) {
	foundDraw := false
	res = &DataTablesInfo{}
	// Let the request parst the post values into the r.Form structure
	err = r.ParseForm()
	if err != nil {
		return
	}
	for field, value := range r.Form {
		val0 := value[0]
		nameparts := strings.Split(field, "[")
		switch nameparts[0] {
		case "draw":
			foundDraw = true
			res.Draw, err = strconv.Atoi(val0)
		case "start":
			res.Start, err = strconv.Atoi(val0)
		case "length":
			res.Length, err = strconv.Atoi(val0)
		case "search":
			if len(nameparts) != 2 {
				err = fmt.Errorf("Invalid search[] element %v", field)
			} else if nameparts[1] == "value]" {
				res.Searchval = val0
			} else if nameparts[1] == "regex]" {
				res.UseRegex = (val0 == "true")
			} else {
				err = fmt.Errorf("Invalid search[] element %v", field)
			}
		case "order":
			index, elem, _, err := parseParts(field, nameparts)
			if err == nil {
				for len(res.Order) < index {
					res.Order = append(res.Order, OrderInfo{})
				}
				switch elem {
				case "column":
					res.Order[index-1].ColNum, err = strconv.Atoi(val0)
				case "dir":
					if val0 == "desc" {
						res.Order[index-1].Direction = Desc
					} else {
						res.Order[index-1].Direction = Asc
					}
				}
			}
		case "columns":
			index, elem, elem2, err := parseParts(field, nameparts)
			// First make sure we have a valid column number to work against
			if err == nil {
				// Fill up the slice to get to the spot where it is going
				for len(res.Columns) < index {
					res.Columns = append(res.Columns, ColData{})
				}
			}
			// Now fill in the field in the column slice
			switch elem {
			case "data":
				res.Columns[index-1].Data = val0
			case "name":
				res.Columns[index-1].Name = val0
			case "searchable":
				res.Columns[index-1].Searchable = (val0 != "false")
			case "orderable":
				res.Columns[index-1].Orderable = (val0 != "false")
			case "search":
				switch elem2 {
				case "value":
					res.Columns[index-1].Searchval = val0
				case "regex":
					res.Columns[index-1].UseRegex = (val0 != "false")
				}
			}
		}
	}
	// If no Draw was specified in the request, then this isn't a datatables request and we can safely ignore it
	if !foundDraw {
		res = nil
		err = errors.New("Not a DataTables request")
	} else {
		// We have a valid datatables request.  See if we actually have any filtering
		res.hasFilter = false
		// Check the global search value to see if it has anything on it
		if res.Searchval != "" {
			// We do have a filter so note that for later
			res.hasFilter = true
			// If they ask for a regex but don't use any regular expressions, then turn off regex for efficiency
			if res.UseRegex && !strings.ContainsAny(res.Searchval, "^$.*+|[]?") {
				res.UseRegex = false
			}
			// Escape the single quotes and any escape characters and then quote the string
			res.Searchval = strings.Replace(res.Searchval, "\\", "\\\\", -1)
			res.Searchval = "'" + strings.Replace(res.Searchval, "'", "\\'", -1) + "'"
		}
		// Now we check all of the columns to see if they have search expressions
		for _, colData := range res.Columns {
			if colData.Searchval != "" {
				// We have a search expression so we remember we have a filter
				res.hasFilter = true
				// CHeck for any regular expresion characters and turn off regex if not
				if colData.UseRegex && !strings.ContainsAny(colData.Searchval, "[]^$.*?+") {
					colData.UseRegex = false
				}
				// Escape the single quotes and any escape characters and then quote the string
				colData.Searchval = strings.Replace(colData.Searchval, "\\", "\\\\", -1)
				colData.Searchval = "'" + strings.Replace(colData.Searchval, "'", "\\'", -1) + "'"
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package facebook

import (
	"log"

	"golang.org/x/oauth2"
)

// Authenticator provides the authentication functionality for Facebook users
// using Facebook's OAuth
type Authenticator interface {
	// AuthURL returns a Facebook URL the user should be redirect to. The user
	// will then be asked to log in by Facebook at that URL and will be redirected
	// back to our API by Facebook.
	AuthURL(session string) string
}

func NewAuthenticator(conf Config, domain string) Authenticator {
	return authenticator{conf, domain}
}

type authenticator struct {
	conf   Config
	domain string
}

func (a authenticator) AuthURL(session string) string {
	opts, err := oauth2.New(
		oauth2.Client(a.conf.AppID, a.conf.AppSecret),
		oauth2.RedirectURL(a.domain+"api/v1/oauth/facebook/redirect"),
		oauth2.Scope("manage_pages", "publish_actions"),
		oauth2.Endpoint(
			"https://www.facebook.com/dialog/oauth",
			"https://graph.facebook.com/oauth/access_token",
		),
	)
	if err != nil {
		log.Fatal(err)
	}
	return opts.AuthCodeURL(session, "offline", "auto")
}
<commit_msg>update facebook authenticator to initialize the oauth2.Options on creation<commit_after>package facebook

import "golang.org/x/oauth2"

// Authenticator provides the authentication functionality for Facebook users
// using Facebook's OAuth
type Authenticator interface {
	// AuthURL returns a Facebook URL the user should be redirect to. The user
	// will then be asked to log in by Facebook at that URL and will be redirected
	// back to our API by Facebook.
	AuthURL(session string) string
}

// NewAuthenticator initializes and returns an Authenticator
func NewAuthenticator(conf Config, domain string) (a Authenticator, err error) {
	var opts *oauth2.Options
	opts, err = oauth2.New(
		oauth2.Client(conf.AppID, conf.AppSecret),
		oauth2.RedirectURL(domain+"api/v1/oauth/facebook/redirect"),
		oauth2.Scope("manage_pages", "publish_actions"),
		oauth2.Endpoint(
			"https://www.facebook.com/dialog/oauth",
			"https://graph.facebook.com/oauth/access_token",
		),
	)
	a = authenticator{opts}
	return
}

type authenticator struct {
	*oauth2.Options
}

func (a authenticator) AuthURL(session string) string {
	return a.AuthCodeURL(session, "offline", "auto")
}
<|endoftext|>


<commit_before><commit_msg>🤖 bleep bloop => auto updated Weaviate<commit_after><|endoftext|>


<commit_before>package app

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"golang.org/x/exp/io/i2c"
)

func I2C() {
	I2C_find()
	Print("Searching for I2C devices...\n")
	if len(Ref.IOs) < 1 {
		Print("... no I2C devices found :(\n")
		os.Exit(0)
	}
	Print("... found %d devices.\n", len(Ref.IOs))

	Print("Restoring I2C devices state...\n")
	restore_count := 0
	for _, IO := range Ref.IOs {
		if IO.RestoreState() {
			restore_count += 1
		}
	}
	Print("... restored %d devices.\n", restore_count)
}

type I2Cx struct {
	Bus         byte
	Addr        byte
	AddrHex     string
	Device      *i2c.Device
	InputState  [8]I2Cx_input_state
	OutputState byte
}
type I2Cx_input_state struct {
	InputTime time.Time
	Switched  bool
}
type I2Cx_bits []I2Cx_bit
type I2Cx_bit struct {
	IO    *I2Cx
	Bit   byte
	State bool
	Name  string
	Ord   int
}

func I2C_find() {
	for _, bus := range []byte{0, 1, 2} {
		addresses, err := I2C_scan(bus)
		if err != nil {
			continue
		}
		for _, addr := range addresses {
			IO := I2C_create(bus, addr)
			IO.SetupIC()
			Ref.IOs = append(Ref.IOs, IO)
			Print("Device #%d-0x%x found\n", bus, addr)
		}
	}
}
func I2C_scan(bus byte) ([]byte, error) {
	args := []string{
		"-y",
		fmt.Sprintf("%d", bus),
	}
	out, err := exec.Command("/usr/sbin/i2cdetect", args...).Output()
	out_string := string(out)
	if err != nil || out_string[5:9] != "0  1" {
		return nil, errors.New("Cannot retrive any info from bus #" + strconv.Itoa(int(bus)))
	}
	addresses := []byte{}
	for _, row := range strings.Split(out_string, "\n") {
		if len(row) < 3 {
			continue
		}
		if row[2:3] != ":" {
			continue
		}
		for _, v := range strings.Split(strings.Trim(row[3:], " "), " ") {
			if v == "--" {
				continue
			}
			hex := "0x" + v
			addr, err := strconv.ParseUint(hex, 0, 8)
			if err != nil {
				continue
			}
			addresses = append(addresses, byte(addr))
		}
	}
	return addresses, nil
}
func I2C_create(bus, addr byte) *I2Cx {
	dev, err := i2c.Open(&i2c.Devfs{Dev: "/dev/i2c-" + strconv.Itoa(int(bus))}, int(addr))
	if err != nil {
		Print("Device #%d-0x%x cannot be opened?\n", bus, addr)
	}
	return &I2Cx{
		Bus:     bus,
		Addr:    addr,
		AddrHex: fmt.Sprintf("0x%x", addr),
		Device:  dev,
	}
}
func I2C_explode_to_bits() I2Cx_bits {
	var IObits I2Cx_bits
	for _, IO := range Ref.IOs {
		var bit byte
		for bit = 0; bit < 8; bit++ {
			IObit := I2Cx_bit{
				IO:    IO,
				Bit:   bit,
				State: HasBit(IO.OutputState, bit),
			}
			k := Ref.DB.PrepareValue(IO.Bus, IO.Addr, bit)
			if name, ok := Ref.DB.Get("BIT-NAME", k); ok {
				IObit.Name = name
			}
			if ord, ok := Ref.DB.Get("BIT-ORD", k); ok {
				IObit.Ord, _ = strconv.Atoi(ord)
			}
			IObits = append(IObits, IObit)
		}
	}
	return IObits
}

func (t *I2Cx) Write(register, value byte) error {
	if err := t.Device.WriteReg(register, []byte{value}); err != nil {
		return err
	}
	return nil
}
func (t *I2Cx) Read(register byte) (byte, error) {
	b := []byte{0}
	if err := t.Device.ReadReg(register, b); err != nil {
		return 0, err
	}
	return b[0], nil
}

func (t *I2Cx) SetupIC() {
	t.Write(0x00, 0x00)          // IODIRA
	t.Write(0x01, 0xff)          // IODIRB
	t.Write(0x03, 0xff)          // IPOLB
	t.Write(0x0d, 0xff)          // GPPUB
	t.Write(0x14, t.OutputState) // OLATA
}
func (t *I2Cx) Check() {
	t.SetupIC()

	input, err := t.Read(0x13)
	if err != nil {
		return
	}

	var now = time.Now()
	var bit byte = 0
	for bit = 0; bit < 8; bit++ {
		if !HasBit(input, bit) {
			t.InputState[bit] = I2Cx_input_state{}
			continue
		}

		// if output was already switched, ignore
		if t.InputState[bit].Switched {
			continue
		}

		// set time when input button was pressed
		if t.InputState[bit].InputTime.IsZero() {
			t.InputState[bit].InputTime = time.Now()
		}

		diff := t.InputState[bit].InputTime.Sub(now)
		diff_ms := diff.Nanoseconds() / 1000000
		if diff_ms < 0 {
			diff_ms *= -1
		}

		// don't switch output if trigger is not pressed for at least Xms
		if diff_ms <= 310 {
			continue
		}
		t.InputState[bit].Switched = true

		status := "OFF"
		if HasBit(t.OutputState, bit) {
			t.OutputState = ClearBit(t.OutputState, bit)
		} else {
			t.OutputState = SetBit(t.OutputState, bit)
			status = "ON"
		}
		t.ChangeState()
		Print("Phisical | Device #%d-0x%x-%d = %s (%s)\n", t.Bus, t.Addr, bit, status, ConvertTo8BitBinaryString(t.OutputState))
	}
}
func (t *I2Cx) ChangeState() {
	bucket := "DEVICE-OUTPUT"
	k := Ref.DB.PrepareValue(t.Bus, t.Addr)
	Ref.DB.Set(bucket, k, Ref.DB.PrepareValue(t.OutputState))
}
func (t *I2Cx) RestoreState() bool {
	bucket := "DEVICE-OUTPUT"
	k := Ref.DB.PrepareValue(t.Bus, t.Addr)
	if output_string, ok := Ref.DB.Get(bucket, k); ok {
		i, err := strconv.Atoi(output_string)
		if err == nil {
			t.OutputState = byte(i)
			t.ChangeState()
			return true
		}
	}
	return false
}

func (t I2Cx_bits) Len() int {
	return len(t)
}
func (t I2Cx_bits) Less(i, j int) bool {
	return t[i].Ord < t[j].Ord
}
func (t I2Cx_bits) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
<commit_msg>Lowered the delay needed for holding input button.<commit_after>package app

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"golang.org/x/exp/io/i2c"
)

func I2C() {
	I2C_find()
	Print("Searching for I2C devices...\n")
	if len(Ref.IOs) < 1 {
		Print("... no I2C devices found :(\n")
		os.Exit(0)
	}
	Print("... found %d devices.\n", len(Ref.IOs))

	Print("Restoring I2C devices state...\n")
	restore_count := 0
	for _, IO := range Ref.IOs {
		if IO.RestoreState() {
			restore_count += 1
		}
	}
	Print("... restored %d devices.\n", restore_count)
}

type I2Cx struct {
	Bus         byte
	Addr        byte
	AddrHex     string
	Device      *i2c.Device
	InputState  [8]I2Cx_input_state
	OutputState byte
}
type I2Cx_input_state struct {
	InputTime time.Time
	Switched  bool
}
type I2Cx_bits []I2Cx_bit
type I2Cx_bit struct {
	IO    *I2Cx
	Bit   byte
	State bool
	Name  string
	Ord   int
}

func I2C_find() {
	for _, bus := range []byte{0, 1, 2} {
		addresses, err := I2C_scan(bus)
		if err != nil {
			continue
		}
		for _, addr := range addresses {
			IO := I2C_create(bus, addr)
			IO.SetupIC()
			Ref.IOs = append(Ref.IOs, IO)
			Print("Device #%d-0x%x found\n", bus, addr)
		}
	}
}
func I2C_scan(bus byte) ([]byte, error) {
	args := []string{
		"-y",
		fmt.Sprintf("%d", bus),
	}
	out, err := exec.Command("/usr/sbin/i2cdetect", args...).Output()
	out_string := string(out)
	if err != nil || out_string[5:9] != "0  1" {
		return nil, errors.New("Cannot retrive any info from bus #" + strconv.Itoa(int(bus)))
	}
	addresses := []byte{}
	for _, row := range strings.Split(out_string, "\n") {
		if len(row) < 3 {
			continue
		}
		if row[2:3] != ":" {
			continue
		}
		for _, v := range strings.Split(strings.Trim(row[3:], " "), " ") {
			if v == "--" {
				continue
			}
			hex := "0x" + v
			addr, err := strconv.ParseUint(hex, 0, 8)
			if err != nil {
				continue
			}
			addresses = append(addresses, byte(addr))
		}
	}
	return addresses, nil
}
func I2C_create(bus, addr byte) *I2Cx {
	dev, err := i2c.Open(&i2c.Devfs{Dev: "/dev/i2c-" + strconv.Itoa(int(bus))}, int(addr))
	if err != nil {
		Print("Device #%d-0x%x cannot be opened?\n", bus, addr)
	}
	return &I2Cx{
		Bus:     bus,
		Addr:    addr,
		AddrHex: fmt.Sprintf("0x%x", addr),
		Device:  dev,
	}
}
func I2C_explode_to_bits() I2Cx_bits {
	var IObits I2Cx_bits
	for _, IO := range Ref.IOs {
		var bit byte
		for bit = 0; bit < 8; bit++ {
			IObit := I2Cx_bit{
				IO:    IO,
				Bit:   bit,
				State: HasBit(IO.OutputState, bit),
			}
			k := Ref.DB.PrepareValue(IO.Bus, IO.Addr, bit)
			if name, ok := Ref.DB.Get("BIT-NAME", k); ok {
				IObit.Name = name
			}
			if ord, ok := Ref.DB.Get("BIT-ORD", k); ok {
				IObit.Ord, _ = strconv.Atoi(ord)
			}
			IObits = append(IObits, IObit)
		}
	}
	return IObits
}

func (t *I2Cx) Write(register, value byte) error {
	if err := t.Device.WriteReg(register, []byte{value}); err != nil {
		return err
	}
	return nil
}
func (t *I2Cx) Read(register byte) (byte, error) {
	b := []byte{0}
	if err := t.Device.ReadReg(register, b); err != nil {
		return 0, err
	}
	return b[0], nil
}

func (t *I2Cx) SetupIC() {
	t.Write(0x00, 0x00)          // IODIRA
	t.Write(0x01, 0xff)          // IODIRB
	t.Write(0x03, 0xff)          // IPOLB
	t.Write(0x0d, 0xff)          // GPPUB
	t.Write(0x14, t.OutputState) // OLATA
}
func (t *I2Cx) Check() {
	t.SetupIC()

	input, err := t.Read(0x13)
	if err != nil {
		return
	}

	var now = time.Now()
	var bit byte = 0
	for bit = 0; bit < 8; bit++ {
		if !HasBit(input, bit) {
			t.InputState[bit] = I2Cx_input_state{}
			continue
		}

		// if output was already switched, ignore
		if t.InputState[bit].Switched {
			continue
		}

		// set time when input button was pressed
		if t.InputState[bit].InputTime.IsZero() {
			t.InputState[bit].InputTime = time.Now()
		}

		diff := t.InputState[bit].InputTime.Sub(now)
		diff_ms := diff.Nanoseconds() / 1000000
		if diff_ms < 0 {
			diff_ms *= -1
		}

		// don't switch output if trigger is not pressed for at least 60ms
		if diff_ms <= 60 {
			continue
		}
		t.InputState[bit].Switched = true

		status := "OFF"
		if HasBit(t.OutputState, bit) {
			t.OutputState = ClearBit(t.OutputState, bit)
		} else {
			t.OutputState = SetBit(t.OutputState, bit)
			status = "ON"
		}
		t.ChangeState()
		Print("Phisical | Device #%d-0x%x-%d = %s (%s)\n", t.Bus, t.Addr, bit, status, ConvertTo8BitBinaryString(t.OutputState))
	}
}
func (t *I2Cx) ChangeState() {
	bucket := "DEVICE-OUTPUT"
	k := Ref.DB.PrepareValue(t.Bus, t.Addr)
	Ref.DB.Set(bucket, k, Ref.DB.PrepareValue(t.OutputState))
}
func (t *I2Cx) RestoreState() bool {
	bucket := "DEVICE-OUTPUT"
	k := Ref.DB.PrepareValue(t.Bus, t.Addr)
	if output_string, ok := Ref.DB.Get(bucket, k); ok {
		i, err := strconv.Atoi(output_string)
		if err == nil {
			t.OutputState = byte(i)
			t.ChangeState()
			return true
		}
	}
	return false
}

func (t I2Cx_bits) Len() int {
	return len(t)
}
func (t I2Cx_bits) Less(i, j int) bool {
	return t[i].Ord < t[j].Ord
}
func (t I2Cx_bits) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
<|endoftext|>


<commit_before>package webapp

import (
	"net/http"

	"github.com/gorilla/csrf"
)

type CSRFMiddleware struct {
	Key               string
	UseInsecureCookie bool
}

func (m *CSRFMiddleware) Handle(next http.Handler) http.Handler {
	gorillaCSRF := csrf.Protect(
		[]byte(m.Key),
		csrf.Path("/"),
		csrf.Secure(!m.UseInsecureCookie),
		csrf.SameSite(csrf.SameSiteNoneMode),
		csrf.CookieName(csrfCookieName),
	)
	return gorillaCSRF(next)
}
<commit_msg>Update csrf middleware to detect should send samesite none by checking useragent<commit_after>package webapp

import (
	"net/http"

	"github.com/gorilla/csrf"
	"github.com/skygeario/skygear-server/pkg/core/samesite"
)

type CSRFMiddleware struct {
	Key               string
	UseInsecureCookie bool
}

func (m *CSRFMiddleware) Handle(next http.Handler) http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		useragent := r.UserAgent()
		options := []csrf.Option{
			csrf.Path("/"),
			csrf.Secure(!m.UseInsecureCookie),
			csrf.CookieName(csrfCookieName),
		}
		if samesite.ShouldSendSameSiteNone(useragent) {
			options = append(options, csrf.SameSite(csrf.SameSiteNoneMode))
		} else {
			// http.Cookie SameSiteDefaultMode option will write SameSite
			// with empty value to the cookie header which doesn't work for
			// some old browsers
			// ref: https://github.com/golang/go/issues/36990
			// To avoid writing samesite to the header
			// set empty value to Cookie SameSite
			// https://golang.org/src/net/http/cookie.go#L220
			options = append(options, csrf.SameSite(0))
		}

		gorillaCSRF := csrf.Protect(
			[]byte(m.Key), options...,
		)
		h := gorillaCSRF(next)
		h.ServeHTTP(w, r)
	})
}
<|endoftext|>