<commit_before>package sudoku

import (
	"testing"
)

const POINTING_PAIR_ROW_GRID = `3|.|6|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
4|.|5|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|7|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.`
const POINTING_PAIR_COL_GRID = `3|.|6|.|.|.|.|.|.
.|.|.|.|7|.|.|.|.
4|.|5|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.`

//The following example comes from http://www.sadmansoftware.com/sudoku/nakedsubset.htm
const NAKED_PAIR_COL_GRID = `3|.|5|.|.|.|7|.|9
.|9|.|3|7|.|.|.|6
.|7|.|.|.|1|.|3|.
6|.|4|.|.|.|3|.|.
.|.|.|4|.|5|.|.|.
.|.|9|.|.|.|.|.|4
.|4|.|1|.|.|9|8|.
9|.|.|.|8|7|.|.|.
8|.|2|.|.|.|1|7|5`

func TestSolveOnlyLegalNumber(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)
	cell := grid.Cell(3, 3)
	num := cell.Number()
	cell.SetNumber(0)

	//Now that cell should be filled by this technique.

	solver := &nakedSingleTechnique{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The only legal number technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The only legal number technique identified the wrong cell.")
		t.Fail()
	}
	numFromStep := step.Nums[0]

	if numFromStep != num {
		t.Log("The only legal number technique identified the wrong number.")
		t.Fail()
	}
	if grid.Solved() {
		t.Log("The only legal number technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInRow(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Row(3) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &hiddenSingleInRow{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The necessary in row technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The necessary in row technique identified the wrong cell.")
		t.Fail()
	}

	numFromStep := step.Nums[0]

	if numFromStep != DIM {
		t.Log("The necessary in row technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() != 0 {
		t.Log("The necessary in row technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInCol(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Col(3) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &hiddenSingleInCol{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The necessary in col technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The necessary in col technique identified the wrong cell.")
		t.Fail()
	}

	numFromStep := step.Nums[0]

	if numFromStep != DIM {
		t.Log("The necessary in col technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() != 0 {
		t.Log("The necessary in col technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInBlock(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Block(4) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &hiddenSingleInBlock{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The necessary in block technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The necessary in block technique identified the wrong cell.")
		t.Fail()
	}

	numFromStep := step.Nums[0]

	if numFromStep != DIM {
		t.Log("The necessary in block technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() != 0 {
		t.Log("The necessary in block technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestPointingPairCol(t *testing.T) {
	grid := NewGrid()
	grid.Load(POINTING_PAIR_COL_GRID)
	solver := &pointingPairCol{}
	step := solver.Find(grid)
	if step == nil {
		t.Log("The pointing pair col didn't find a cell it should have")
		t.FailNow()
	}
	if len(step.TargetCells) != BLOCK_DIM*2 {
		t.Log("The pointing pair col gave back the wrong number of target cells")
		t.Fail()
	}
	if len(step.PointerCells) != BLOCK_DIM-1 {
		t.Log("The pointing pair col gave back the wrong number of pointer cells")
		t.Fail()
	}
	if !step.TargetCells.SameCol() || step.TargetCells.Col() != 1 {
		t.Log("The target cells in the pointing pair col technique were wrong col")
		t.Fail()
	}
	if len(step.Nums) != 1 || step.Nums[0] != 7 {
		t.Log("Pointing pair col technique gave the wrong number")
		t.Fail()
	}
	step.Apply(grid)
	num := step.Nums[0]
	for _, cell := range step.TargetCells {
		if cell.Possible(num) {
			t.Log("The pointing pairs col technique was not applied correclty")
			t.Fail()
		}
	}
}

func TestPointingPairRow(t *testing.T) {
	grid := NewGrid()
	grid.Load(POINTING_PAIR_ROW_GRID)
	solver := &pointingPairRow{}
	step := solver.Find(grid)
	if step == nil {
		t.Log("The pointing pair row didn't find a cell it should have")
		t.FailNow()
	}
	if len(step.TargetCells) != BLOCK_DIM*2 {
		t.Log("The pointing pair row gave back the wrong number of target cells")
		t.Fail()
	}
	if len(step.PointerCells) != BLOCK_DIM-1 {
		t.Log("The pointing pair row gave back the wrong number of pointer cells")
		t.Fail()
	}
	if !step.TargetCells.SameRow() || step.TargetCells.Row() != 1 {
		t.Log("The target cells in the pointing pair row technique were wrong row")
		t.Fail()
	}
	if len(step.Nums) != 1 || step.Nums[0] != 7 {
		t.Log("Pointing pair row technique gave the wrong number")
		t.Fail()
	}
	step.Apply(grid)
	num := step.Nums[0]
	for _, cell := range step.TargetCells {
		if cell.Possible(num) {
			t.Log("The pointing pairs row technique was not applied correclty")
			t.Fail()
		}
	}
}

func TestNakedPairCol(t *testing.T) {
	grid := NewGrid()
	grid.Load(NAKED_PAIR_COL_GRID)
	solver := &nakedPairCol{}
	step := solver.Find(grid)
	if step == nil {
		t.Log("The naked pair col didn't find a cell it should have.")
		t.Fail()
	}
	//TODO: verify it's the RIGHT solution.
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	grid.Load(TEST_GRID)
	steps := grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}
}
<commit_msg>More tests for NakedPairCol.<commit_after>package sudoku

import (
	"testing"
)

const POINTING_PAIR_ROW_GRID = `3|.|6|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
4|.|5|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|7|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.`
const POINTING_PAIR_COL_GRID = `3|.|6|.|.|.|.|.|.
.|.|.|.|7|.|.|.|.
4|.|5|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.
.|.|.|.|.|.|.|.|.`

//The following example comes from http://www.sadmansoftware.com/sudoku/nakedsubset.htm
const NAKED_PAIR_COL_GRID = `3|.|5|.|.|.|7|.|9
.|9|.|3|7|.|.|.|6
.|7|.|.|.|1|.|3|.
6|.|4|.|.|.|3|.|.
.|.|.|4|.|5|.|.|.
.|.|9|.|.|.|.|.|4
.|4|.|1|.|.|9|8|.
9|.|.|.|8|7|.|.|.
8|.|2|.|.|.|1|7|5`

func TestSolveOnlyLegalNumber(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)
	cell := grid.Cell(3, 3)
	num := cell.Number()
	cell.SetNumber(0)

	//Now that cell should be filled by this technique.

	solver := &nakedSingleTechnique{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The only legal number technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The only legal number technique identified the wrong cell.")
		t.Fail()
	}
	numFromStep := step.Nums[0]

	if numFromStep != num {
		t.Log("The only legal number technique identified the wrong number.")
		t.Fail()
	}
	if grid.Solved() {
		t.Log("The only legal number technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInRow(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Row(3) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &hiddenSingleInRow{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The necessary in row technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The necessary in row technique identified the wrong cell.")
		t.Fail()
	}

	numFromStep := step.Nums[0]

	if numFromStep != DIM {
		t.Log("The necessary in row technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() != 0 {
		t.Log("The necessary in row technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInCol(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Col(3) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &hiddenSingleInCol{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The necessary in col technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The necessary in col technique identified the wrong cell.")
		t.Fail()
	}

	numFromStep := step.Nums[0]

	if numFromStep != DIM {
		t.Log("The necessary in col technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() != 0 {
		t.Log("The necessary in col technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInBlock(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Block(4) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &hiddenSingleInBlock{}

	step := solver.Find(grid)

	if step == nil {
		t.Log("The necessary in block technique did not solve a puzzle it should have.")
		t.FailNow()
	}

	cellFromStep := step.TargetCells[0]

	if cellFromStep.Col != 3 || cellFromStep.Row != 3 {
		t.Log("The necessary in block technique identified the wrong cell.")
		t.Fail()
	}

	numFromStep := step.Nums[0]

	if numFromStep != DIM {
		t.Log("The necessary in block technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() != 0 {
		t.Log("The necessary in block technique did actually mutate the grid.")
		t.Fail()
	}
}

func TestPointingPairCol(t *testing.T) {
	grid := NewGrid()
	grid.Load(POINTING_PAIR_COL_GRID)
	solver := &pointingPairCol{}
	step := solver.Find(grid)
	if step == nil {
		t.Log("The pointing pair col didn't find a cell it should have")
		t.FailNow()
	}
	if len(step.TargetCells) != BLOCK_DIM*2 {
		t.Log("The pointing pair col gave back the wrong number of target cells")
		t.Fail()
	}
	if len(step.PointerCells) != BLOCK_DIM-1 {
		t.Log("The pointing pair col gave back the wrong number of pointer cells")
		t.Fail()
	}
	if !step.TargetCells.SameCol() || step.TargetCells.Col() != 1 {
		t.Log("The target cells in the pointing pair col technique were wrong col")
		t.Fail()
	}
	if len(step.Nums) != 1 || step.Nums[0] != 7 {
		t.Log("Pointing pair col technique gave the wrong number")
		t.Fail()
	}
	step.Apply(grid)
	num := step.Nums[0]
	for _, cell := range step.TargetCells {
		if cell.Possible(num) {
			t.Log("The pointing pairs col technique was not applied correclty")
			t.Fail()
		}
	}
}

func TestPointingPairRow(t *testing.T) {
	grid := NewGrid()
	grid.Load(POINTING_PAIR_ROW_GRID)
	solver := &pointingPairRow{}
	step := solver.Find(grid)
	if step == nil {
		t.Log("The pointing pair row didn't find a cell it should have")
		t.FailNow()
	}
	if len(step.TargetCells) != BLOCK_DIM*2 {
		t.Log("The pointing pair row gave back the wrong number of target cells")
		t.Fail()
	}
	if len(step.PointerCells) != BLOCK_DIM-1 {
		t.Log("The pointing pair row gave back the wrong number of pointer cells")
		t.Fail()
	}
	if !step.TargetCells.SameRow() || step.TargetCells.Row() != 1 {
		t.Log("The target cells in the pointing pair row technique were wrong row")
		t.Fail()
	}
	if len(step.Nums) != 1 || step.Nums[0] != 7 {
		t.Log("Pointing pair row technique gave the wrong number")
		t.Fail()
	}
	step.Apply(grid)
	num := step.Nums[0]
	for _, cell := range step.TargetCells {
		if cell.Possible(num) {
			t.Log("The pointing pairs row technique was not applied correclty")
			t.Fail()
		}
	}
}

func TestNakedPairCol(t *testing.T) {
	grid := NewGrid()
	grid.Load(NAKED_PAIR_COL_GRID)
	solver := &nakedPairCol{}
	step := solver.Find(grid)
	if step == nil {
		t.Log("The naked pair col didn't find a cell it should have.")
		t.FailNow()
	}
	if len(step.TargetCells) != DIM-2 {
		t.Log("The naked pair col had the wrong number of target cells")
		t.Fail()
	}
	if len(step.PointerCells) != 2 {
		t.Log("The naked pair col had the wrong number of pointer clles")
		t.Fail()
	}
	if !step.TargetCells.SameCol() || step.TargetCells.Col() != 8 {
		t.Log("The target cells in the naked pair col were wrong col")
		t.Fail()
	}
	if len(step.Nums) != 2 || step.Nums[0] != 2 || step.Nums[1] != 3 {
		t.Log("Naked pair col found the wrong numbers")
		t.Fail()
	}
	step.Apply(grid)
	firstNum := step.Nums[0]
	secondNum := step.Nums[1]
	for _, cell := range step.TargetCells {
		if cell.Possible(firstNum) || cell.Possible(secondNum) {
			t.Log("Naked Pair col found was not appleid correctly")
			t.Fail()
		}
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	grid.Load(TEST_GRID)
	steps := grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
)

// Constructor - holds information about original request (which is needed to create response
// and also holds payload
type Constructor struct {
	request *http.Request
	payload Payload
}

// NewConstructor - returns constructor instance
func NewConstructor(req *http.Request, payload Payload) Constructor {
	c := Constructor{request: req, payload: payload}
	return c
}

// ApplyMiddleware - activates given middleware, middleware should be passed as string to executable, can be
// full path.
func (c *Constructor) ApplyMiddleware(middleware string) error {

	newPayload, err := ExecuteMiddleware(middleware, c.payload)

	if err != nil {
		log.WithFields(log.Fields{
			"error":      err.Error(),
			"middleware": middleware,
		}).Error("Error during middleware transformation, not modifying payload!")

		return err
	}

	log.WithFields(log.Fields{
		"middleware": middleware,
	}).Info("Middleware transformation complete!")
	// override payload with transformed new payload
	c.payload = newPayload

	return nil

}

// reconstructResponse changes original response with details provided in Constructor Payload.Response
func (c *Constructor) reconstructResponse() *http.Response {
	response := &http.Response{}
	response.Request = c.request

	// adding headers
	response.Header = make(http.Header)

	// applying payload
	if len(c.payload.Response.Headers) > 0 {
		for k, values := range c.payload.Response.Headers {
			// headers is a map, appending each value
			for _, v := range values {
				response.Header.Add(k, v)
			}

		}
	}
	// adding body, length, status code
	buf := bytes.NewBufferString(c.payload.Response.Body)
	response.ContentLength = int64(buf.Len())
	response.Body = ioutil.NopCloser(buf)
	response.StatusCode = c.payload.Response.Status

	return response
}

// reconstructRequest replaces original request with details provided in Constructor Payload.Request
func (c *Constructor) reconstructRequest() (*http.Request, error) {
	// let's default to what was given
	if c.payload.Request.Scheme == "" {
		c.payload.Request.Scheme = c.request.URL.Scheme
	}

	if c.payload.Request.Destination == "" {
		return nil, fmt.Errorf("failed to reconstruct request, destination not specified")
	}

	request, err := http.NewRequest(
		c.payload.Request.Method,
		fmt.Sprintf("%s://%s", c.payload.Request.Scheme, c.payload.Request.Destination),
		ioutil.NopCloser(bytes.NewBuffer([]byte(c.payload.Request.Body))))

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Request reconstruction failed...")
		return nil, err
	}
	request.Method = c.payload.Request.Method
	request.URL.Path = c.payload.Request.Path
	request.URL.RawQuery = c.payload.Request.Query
	request.RemoteAddr = c.payload.Request.RemoteAddr
	request.Header = c.payload.Request.Headers

	return request, nil
}
<commit_msg>adding original request info<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
)

// Constructor - holds information about original request (which is needed to create response
// and also holds payload
type Constructor struct {
	request *http.Request
	payload Payload
}

// NewConstructor - returns constructor instance
func NewConstructor(req *http.Request, payload Payload) *Constructor {
	c := &Constructor{request: req, payload: payload}
	return c
}

// ApplyMiddleware - activates given middleware, middleware should be passed as string to executable, can be
// full path.
func (c *Constructor) ApplyMiddleware(middleware string) error {

	newPayload, err := ExecuteMiddleware(middleware, c.payload)

	if err != nil {
		log.WithFields(log.Fields{
			"error":      err.Error(),
			"middleware": middleware,
		}).Error("Error during middleware transformation, not modifying payload!")

		return err
	}

	log.WithFields(log.Fields{
		"middleware": middleware,
	}).Debug("Middleware transformation complete!")
	// override payload with transformed new payload
	c.payload = newPayload

	return nil

}

// reconstructResponse changes original response with details provided in Constructor Payload.Response
func (c *Constructor) reconstructResponse() *http.Response {
	response := &http.Response{}
	response.Request = c.request

	// adding headers
	response.Header = make(http.Header)

	// applying payload
	if len(c.payload.Response.Headers) > 0 {
		for k, values := range c.payload.Response.Headers {
			// headers is a map, appending each value
			for _, v := range values {
				response.Header.Add(k, v)
			}

		}
	}
	// adding body, length, status code
	buf := bytes.NewBufferString(c.payload.Response.Body)
	response.ContentLength = int64(buf.Len())
	response.Body = ioutil.NopCloser(buf)
	response.StatusCode = c.payload.Response.Status

	return response
}

// reconstructRequest replaces original request with details provided in Constructor Payload.Request
func (c *Constructor) reconstructRequest() (*http.Request, error) {
	// let's default to what was given
	if c.payload.Request.Scheme == "" {
		c.payload.Request.Scheme = c.request.URL.Scheme
	}

	if c.payload.Request.Destination == "" {
		return nil, fmt.Errorf("failed to reconstruct request, destination not specified")
	}

	newRequest, err := http.NewRequest(
		c.payload.Request.Method,
		fmt.Sprintf("%s://%s", c.payload.Request.Scheme, c.payload.Request.Destination),
		ioutil.NopCloser(bytes.NewBuffer([]byte(c.payload.Request.Body))))

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Request reconstruction failed...")
		return nil, err
	}

	newRequest.Method = c.payload.Request.Method
	newRequest.URL.Path = c.payload.Request.Path
	newRequest.URL.RawQuery = c.payload.Request.Query
	newRequest.RemoteAddr = c.payload.Request.RemoteAddr
	newRequest.Header = c.payload.Request.Headers

	// overriding original request
	c.request = newRequest

	return newRequest, nil
}
<|endoftext|>


<commit_before>package sse

import (
	"fmt"
	"testing"
)

// Test Datatype
type structPayload struct {
	Bar string
	Baz int
}

func TestMessageSerialization(t *testing.T) {

	payload := "foo bar"

	multilinePayload := "foo\nbar baz"

	// Should only contain data
	m := Message{
		Data: payload,
	}

	data, _ := m.MarshalText()
	fmt.Println("Serialized message (string):")
	fmt.Println(string(data))

	// Serialize raw
	m = Message{
		Event: "foo",
		Retry: 23,
		Data:  []byte("foo"),
	}

	data, _ = m.MarshalText()
	fmt.Println("Serialized message (raw):")
	fmt.Println(string(data))

	// Use multiline strings
	m = Message{
		Event: "foo",
		Data:  MultilineStringData(multilinePayload),
	}

	data, _ = m.MarshalText()
	fmt.Println("Serialized message (MultilineStringData):")
	fmt.Println(string(data))

	// Serialize complex datatype
	m = Message{
		Event: "foo",
		Data: structPayload{
			Bar: "baz",
			Baz: 23,
		},
	}

	data, _ = m.MarshalText()
	fmt.Println("Serialized message (struct):")
	fmt.Println(string(data))

}
<commit_msg>added test case for failing json encoding<commit_after>package sse

import (
	"errors"
	"testing"
)

// Json encoding error test
type faulty string

func (f faulty) MarshalJSON() ([]byte, error) {
	return nil, errors.New("This did not went well.")
}

// Test Datatype
type structPayload struct {
	Bar string
	Baz int
}

func TestMessageSerialization(t *testing.T) {

	payload := "foo bar"

	multilinePayload := "foo\nbar baz"

	// Should only contain data
	m := Message{
		Data: payload,
	}

	data, _ := m.MarshalText()
	t.Log("Serialized message (string):")
	t.Log(string(data))

	// Serialize raw
	m = Message{
		Id:    "foo42",
		Event: "foo",
		Retry: 23,
		Data:  []byte("foo"),
	}

	data, _ = m.MarshalText()
	t.Log("Serialized message (raw):")
	t.Log(string(data))

	// Use multiline strings
	m = Message{
		Event: "foo",
		Data:  MultilineStringData(multilinePayload),
	}

	data, _ = m.MarshalText()
	t.Log("Serialized message (MultilineStringData):")
	t.Log(string(data))

	// Serialize complex datatype
	m = Message{
		Event: "foo",
		Data: structPayload{
			Bar: "baz",
			Baz: 23,
		},
	}

	data, _ = m.MarshalText()
	t.Log("Serialized message (struct):")
	t.Log(string(data))

	// Marshaling error
	m = Message{
		Event: "foo",
		Data:  faulty("foo"),
	}

	data, err := m.MarshalText()
	t.Log("Serialized message (error):")
	if err == nil {
		t.Error("JSON marshalling errors should propagate")
	}
	t.Log(err)

}
<|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"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"reflect"
	"runtime"
	"testing"
	"time"
)

func Test(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Message Suite")
}

var _ = Describe("Message", func() {
	Context("Utf8 encoding", func() {
		It("should return an error for non-utf8 encoded messages", func() {
			_, _, err := ParseMessage(string([]byte{0xff, 0xfe, 0xfd}))
			Ω(err).ShouldNot(BeNil())
		})

		It("should should not return an error for utf8 encoded messages", func() {
			_, _, err := ParseMessage("/announce|{\"id\":\"dc6ac0ae-6e15-409b-b211-228a8f4a43b9\"}|{\"browser\":\"node\",\"browserVersion\":\"?\",\"id\":\"dc6ac0ae-6e15-409b-b211-228a8f4a43b9\",\"agent\":\"signaller@0.18.3\",\"room\":\"test-room\"}")
			Ω(err).Should(BeNil())
		})
	})

	Context("Action parsing", func() {
		// TODO test to make sure zero-length messages are handled OK.

		It("should be able to parse an announce message", func() {
			action, message, err := ParseMessage("/announce")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.announce"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse a leave message", func() {
			action, message, err := ParseMessage("/leave")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.leave"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse a to message", func() {
			action, message, err := ParseMessage("/to")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.to"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse a custom message", func() {
			action, message, err := ParseMessage("/custom|part1|part2")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.custom"))
			Ω(len(message)).Should(Equal(3))
			Ω(message[0]).Should(Equal("/custom"))
			Ω(message[1]).Should(Equal("part1"))
			Ω(message[2]).Should(Equal("part2"))
		})

		It("should ignore malformed messages", func() {
			action, message, err := ParseMessage(":lkajsd??asdj/foo")
			Ω(err).Should(BeNil())
			Ω(len(message)).Should(Equal(1))
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.ignore"))
		})
	})

	Context("ParsePeerAndRoom", func() {
		It("should return an error when their is not enough parts to a message", func() {
			_, message, _ := ParseMessage("/foo")
			_, _, err := ParsePeerAndRoom(message)
			Ω(err).ShouldNot(BeNil())
		})

		It("should parse source id and room", func() {
			_, message, _ := ParseMessage("/announce|{\"id\":\"abc\"}|{\"room\":\"test\"}")
			source, destination, err := ParsePeerAndRoom(message)
			Ω(err).Should(BeNil())
			Ω(source.Id).Should(Equal("abc"))
			Ω(destination.Room).Should(Equal("test"))
		})
	})

	Context("Test SignalBox State", func() {
		var state SignalBox
		var announceAAct messageFn
		var announceAMsg []string

		var announceA2Act messageFn
		var announceA2Msg []string

		var leaveAAct messageFn
		var leaveAMsg []string

		var leaveA2Act messageFn
		var leaveA2Msg []string

		var announceBAct messageFn
		var announceBMsg []string

		var leaveBAct messageFn
		var leaveBMsg []string

		BeforeEach(func() {
			var err error
			state = SignalBox{make(map[string]*Peer),
				make(map[string]*Room),
				make(map[string]map[string]*Peer),
				make(map[string]map[string]*Room)}

			announceAAct, announceAMsg, err = ParseMessage("/announce|{\"id\":\"a\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())

			announceA2Act, announceA2Msg, err = ParseMessage("/announce|{\"id\":\"a\"}|{\"room\":\"test2\"}")
			Ω(err).Should(BeNil())

			leaveAAct, leaveAMsg, err = ParseMessage("/leave|{\"id\":\"a\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())

			leaveA2Act, leaveA2Msg, err = ParseMessage("/leave|{\"id\":\"a\"}|{\"room\":\"test2\"}")
			Ω(err).Should(BeNil())

			announceBAct, announceBMsg, err = ParseMessage("/announce|{\"id\":\"b\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())

			leaveBAct, leaveBMsg, err = ParseMessage("/leave|{\"id\":\"b\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())
		})

		It("only add someone to the roome once, even if they announce more than once", func() {
			state, err := announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(1))
			Ω(len(state.Rooms)).Should(Equal(1))
			Ω(len(state.RoomContains)).Should(Equal(1))
			Ω(len(state.PeerIsIn)).Should(Equal(1))
			Ω(len(state.RoomContains["test"])).Should(Equal(1))
			Ω(len(state.PeerIsIn["a"])).Should(Equal(1))
			Ω(state.RoomContains["test"]["a"].Id).Should(Equal("a"))
			Ω(state.PeerIsIn["a"]["test"].Room).Should(Equal("test"))
		})

		It("should be able to add multiple people to a signalbox room", func() {
			state, err := announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceBAct(announceBMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(2))
			Ω(len(state.Rooms)).Should(Equal(1))
			Ω(len(state.PeerIsIn)).Should(Equal(2))
			Ω(len(state.RoomContains)).Should(Equal(1))
			Ω(len(state.PeerIsIn["a"])).Should(Equal(1))
			Ω(len(state.PeerIsIn["b"])).Should(Equal(1))
			Ω(len(state.RoomContains["test"])).Should(Equal(2))
		})

		It("Should be able to have a person leave a signalbox room", func() {
			state, err := announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = leaveAAct(leaveAMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(0))
			Ω(len(state.Rooms)).Should(Equal(0))
			Ω(len(state.PeerIsIn)).Should(Equal(0))
			Ω(len(state.RoomContains)).Should(Equal(0))
		})

		It("Should keep a room, if a person leaves but it still contains peers", func() {
			state, err := announceBAct(announceBMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceA2Act(announceA2Msg, nil, state)
			Ω(err).Should(BeNil())
			state, err = leaveBAct(leaveBMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(1))
			Ω(len(state.Rooms)).Should(Equal(2))
			Ω(len(state.PeerIsIn)).Should(Equal(1))
			Ω(len(state.RoomContains)).Should(Equal(2))
			Ω(len(state.PeerIsIn["a"])).Should(Equal(2))
			Ω(len(state.RoomContains["test"])).Should(Equal(1))
			Ω(len(state.RoomContains["test2"])).Should(Equal(1))
			Ω(state.RoomContains["test"]["a"].Id).Should(Equal("a"))
			Ω(state.RoomContains["test2"]["a"].Id).Should(Equal("a"))
		})
	})

	Context("Broadcast messages", func() {
		// Spin up the signalbox.
		go main()

		It("Should be to send announce and leave messages to peers", func() {
			a, err := connectPeer("a", "test-room")
			Ω(err).Should(BeNil())
			b, err := connectPeer("b", "test-room")
			Ω(err).Should(BeNil())

			socketShouldContain(a, "/announce|{\"id\":\"b\"}|{\"room\":\"test-room\"}")

			socketSend(a, "/leave|{\"id\":\"a\"}|{\"room\":\"test-room\"}")
			err = a.Close()
			Ω(err).Should(BeNil())

			socketShouldContain(b, "/leave|{\"id\":\"a\"}|{\"room\":\"test-room\"}")
			err = b.Close()
			Ω(err).Should(BeNil())
		})

		It("Should be able to send messages just to specified recipients", func() {
			a2, err := connectPeer("a2", "to-test")
			Ω(err).Should(BeNil())

			b2, err := connectPeer("b2", "to-test")
			Ω(err).Should(BeNil())

			c2, err := connectPeer("c2", "to-test")
			Ω(err).Should(BeNil())

			socketShouldContain(a2, "/announce|{\"id\":\"b2\"}|{\"room\":\"to-test\"}")
			socketShouldContain(a2, "/announce|{\"id\":\"c2\"}|{\"room\":\"to-test\"}")

			socketShouldContain(b2, "/announce|{\"id\":\"c2\"}|{\"room\":\"to-test\"}")
			socketSend(a2, "/to|c2|/hello|{\"id\":\"a1\"}")

			socketShouldContain(c2, "/to|c2|/hello|{\"id\":\"a1\"}")

			_, _, err = b2.ReadMessage()
			Ω(err).ShouldNot(BeNil())
		})

		It("Should be able to send custom messages to peers", func() {
			a3, err := connectPeer("a3", "custom-test")
			Ω(err).Should(BeNil())
			b3, err := connectPeer("b3", "custom-test")
			Ω(err).Should(BeNil())

			socketShouldContain(a3, "/announce|{\"id\":\"b3\"}|{\"room\":\"custom-test\"}")
			socketSend(a3, "/hello|{\"id\":\"a3\"}")
			socketShouldContain(b3, "/hello|{\"id\":\"a3\"}")
		})
	})
})

func socketSend(ws *websocket.Conn, content string) {
	msg, err := json.Marshal(content)
	Ω(err).Should(BeNil())
	ws.WriteMessage(websocket.TextMessage, msg)
}

func socketShouldContain(ws *websocket.Conn, content string) {
	_, message, err := ws.ReadMessage()
	Ω(err).Should(BeNil())
	expected, err := json.Marshal(content)
	Ω(err).Should(BeNil())
	Ω(message).Should(Equal(expected))
}

func connectPeer(id string, room string) (*websocket.Conn, error) {
	url := "ws://localhost:3000"
	res, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil || res == nil {
		return nil, err
	}

	connect, err := json.Marshal(fmt.Sprintf("/announce|{\"id\":\"%s\"}|{\"room\":\"%s\"}", id, room))
	if err != nil {
		return nil, err
	}

	err = res.WriteMessage(websocket.TextMessage, connect)
	if err != nil {
		return nil, err
	}

	res.SetReadDeadline(time.Now().Add(4 * time.Millisecond))

	return res, nil
}
<commit_msg>Added zero length message test.<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"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"reflect"
	"runtime"
	"testing"
	"time"
)

func Test(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Message Suite")
}

var _ = Describe("Message", func() {
	Context("Utf8 encoding", func() {
		It("should return an error for non-utf8 encoded messages", func() {
			_, _, err := ParseMessage(string([]byte{0xff, 0xfe, 0xfd}))
			Ω(err).ShouldNot(BeNil())
		})

		It("should should not return an error for utf8 encoded messages", func() {
			_, _, err := ParseMessage("/announce|{\"id\":\"dc6ac0ae-6e15-409b-b211-228a8f4a43b9\"}|{\"browser\":\"node\",\"browserVersion\":\"?\",\"id\":\"dc6ac0ae-6e15-409b-b211-228a8f4a43b9\",\"agent\":\"signaller@0.18.3\",\"room\":\"test-room\"}")
			Ω(err).Should(BeNil())
		})
	})

	Context("Action parsing", func() {
		It("should be able to handle zero-length messages", func() {
			action, message, err := ParseMessage("")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.ignore"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse an announce message", func() {
			action, message, err := ParseMessage("/announce")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.announce"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse a leave message", func() {
			action, message, err := ParseMessage("/leave")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.leave"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse a to message", func() {
			action, message, err := ParseMessage("/to")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.to"))
			Ω(len(message)).Should(Equal(1))
		})

		It("should be able to parse a custom message", func() {
			action, message, err := ParseMessage("/custom|part1|part2")
			Ω(err).Should(BeNil())
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.custom"))
			Ω(len(message)).Should(Equal(3))
			Ω(message[0]).Should(Equal("/custom"))
			Ω(message[1]).Should(Equal("part1"))
			Ω(message[2]).Should(Equal("part2"))
		})

		It("should ignore malformed messages", func() {
			action, message, err := ParseMessage(":lkajsd??asdj/foo")
			Ω(err).Should(BeNil())
			Ω(len(message)).Should(Equal(1))
			Ω(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()).Should(Equal("github.com/cfreeman/signalbox.ignore"))
		})
	})

	Context("ParsePeerAndRoom", func() {
		It("should return an error when their is not enough parts to a message", func() {
			_, message, _ := ParseMessage("/foo")
			_, _, err := ParsePeerAndRoom(message)
			Ω(err).ShouldNot(BeNil())
		})

		It("should parse source id and room", func() {
			_, message, _ := ParseMessage("/announce|{\"id\":\"abc\"}|{\"room\":\"test\"}")
			source, destination, err := ParsePeerAndRoom(message)
			Ω(err).Should(BeNil())
			Ω(source.Id).Should(Equal("abc"))
			Ω(destination.Room).Should(Equal("test"))
		})
	})

	Context("Test SignalBox State", func() {
		var state SignalBox
		var announceAAct messageFn
		var announceAMsg []string

		var announceA2Act messageFn
		var announceA2Msg []string

		var leaveAAct messageFn
		var leaveAMsg []string

		var leaveA2Act messageFn
		var leaveA2Msg []string

		var announceBAct messageFn
		var announceBMsg []string

		var leaveBAct messageFn
		var leaveBMsg []string

		BeforeEach(func() {
			var err error
			state = SignalBox{make(map[string]*Peer),
				make(map[string]*Room),
				make(map[string]map[string]*Peer),
				make(map[string]map[string]*Room)}

			announceAAct, announceAMsg, err = ParseMessage("/announce|{\"id\":\"a\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())

			announceA2Act, announceA2Msg, err = ParseMessage("/announce|{\"id\":\"a\"}|{\"room\":\"test2\"}")
			Ω(err).Should(BeNil())

			leaveAAct, leaveAMsg, err = ParseMessage("/leave|{\"id\":\"a\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())

			leaveA2Act, leaveA2Msg, err = ParseMessage("/leave|{\"id\":\"a\"}|{\"room\":\"test2\"}")
			Ω(err).Should(BeNil())

			announceBAct, announceBMsg, err = ParseMessage("/announce|{\"id\":\"b\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())

			leaveBAct, leaveBMsg, err = ParseMessage("/leave|{\"id\":\"b\"}|{\"room\":\"test\"}")
			Ω(err).Should(BeNil())
		})

		It("only add someone to the roome once, even if they announce more than once", func() {
			state, err := announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(1))
			Ω(len(state.Rooms)).Should(Equal(1))
			Ω(len(state.RoomContains)).Should(Equal(1))
			Ω(len(state.PeerIsIn)).Should(Equal(1))
			Ω(len(state.RoomContains["test"])).Should(Equal(1))
			Ω(len(state.PeerIsIn["a"])).Should(Equal(1))
			Ω(state.RoomContains["test"]["a"].Id).Should(Equal("a"))
			Ω(state.PeerIsIn["a"]["test"].Room).Should(Equal("test"))
		})

		It("should be able to add multiple people to a signalbox room", func() {
			state, err := announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceBAct(announceBMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(2))
			Ω(len(state.Rooms)).Should(Equal(1))
			Ω(len(state.PeerIsIn)).Should(Equal(2))
			Ω(len(state.RoomContains)).Should(Equal(1))
			Ω(len(state.PeerIsIn["a"])).Should(Equal(1))
			Ω(len(state.PeerIsIn["b"])).Should(Equal(1))
			Ω(len(state.RoomContains["test"])).Should(Equal(2))
		})

		It("Should be able to have a person leave a signalbox room", func() {
			state, err := announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = leaveAAct(leaveAMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(0))
			Ω(len(state.Rooms)).Should(Equal(0))
			Ω(len(state.PeerIsIn)).Should(Equal(0))
			Ω(len(state.RoomContains)).Should(Equal(0))
		})

		It("Should keep a room, if a person leaves but it still contains peers", func() {
			state, err := announceBAct(announceBMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceAAct(announceAMsg, nil, state)
			Ω(err).Should(BeNil())
			state, err = announceA2Act(announceA2Msg, nil, state)
			Ω(err).Should(BeNil())
			state, err = leaveBAct(leaveBMsg, nil, state)
			Ω(err).Should(BeNil())

			Ω(len(state.Peers)).Should(Equal(1))
			Ω(len(state.Rooms)).Should(Equal(2))
			Ω(len(state.PeerIsIn)).Should(Equal(1))
			Ω(len(state.RoomContains)).Should(Equal(2))
			Ω(len(state.PeerIsIn["a"])).Should(Equal(2))
			Ω(len(state.RoomContains["test"])).Should(Equal(1))
			Ω(len(state.RoomContains["test2"])).Should(Equal(1))
			Ω(state.RoomContains["test"]["a"].Id).Should(Equal("a"))
			Ω(state.RoomContains["test2"]["a"].Id).Should(Equal("a"))
		})
	})

	Context("Broadcast messages", func() {
		// Spin up the signalbox.
		go main()

		It("Should be to send announce and leave messages to peers", func() {
			a, err := connectPeer("a", "test-room")
			Ω(err).Should(BeNil())
			b, err := connectPeer("b", "test-room")
			Ω(err).Should(BeNil())

			socketShouldContain(a, "/announce|{\"id\":\"b\"}|{\"room\":\"test-room\"}")

			socketSend(a, "/leave|{\"id\":\"a\"}|{\"room\":\"test-room\"}")
			err = a.Close()
			Ω(err).Should(BeNil())

			socketShouldContain(b, "/leave|{\"id\":\"a\"}|{\"room\":\"test-room\"}")
			err = b.Close()
			Ω(err).Should(BeNil())
		})

		It("Should be able to send messages just to specified recipients", func() {
			a2, err := connectPeer("a2", "to-test")
			Ω(err).Should(BeNil())

			b2, err := connectPeer("b2", "to-test")
			Ω(err).Should(BeNil())

			c2, err := connectPeer("c2", "to-test")
			Ω(err).Should(BeNil())

			socketShouldContain(a2, "/announce|{\"id\":\"b2\"}|{\"room\":\"to-test\"}")
			socketShouldContain(a2, "/announce|{\"id\":\"c2\"}|{\"room\":\"to-test\"}")

			socketShouldContain(b2, "/announce|{\"id\":\"c2\"}|{\"room\":\"to-test\"}")
			socketSend(a2, "/to|c2|/hello|{\"id\":\"a1\"}")

			socketShouldContain(c2, "/to|c2|/hello|{\"id\":\"a1\"}")

			_, _, err = b2.ReadMessage()
			Ω(err).ShouldNot(BeNil())
		})

		It("Should be able to send custom messages to peers", func() {
			a3, err := connectPeer("a3", "custom-test")
			Ω(err).Should(BeNil())
			b3, err := connectPeer("b3", "custom-test")
			Ω(err).Should(BeNil())

			socketShouldContain(a3, "/announce|{\"id\":\"b3\"}|{\"room\":\"custom-test\"}")
			socketSend(a3, "/hello|{\"id\":\"a3\"}")
			socketShouldContain(b3, "/hello|{\"id\":\"a3\"}")
		})
	})
})

func socketSend(ws *websocket.Conn, content string) {
	msg, err := json.Marshal(content)
	Ω(err).Should(BeNil())
	ws.WriteMessage(websocket.TextMessage, msg)
}

func socketShouldContain(ws *websocket.Conn, content string) {
	_, message, err := ws.ReadMessage()
	Ω(err).Should(BeNil())
	expected, err := json.Marshal(content)
	Ω(err).Should(BeNil())
	Ω(message).Should(Equal(expected))
}

func connectPeer(id string, room string) (*websocket.Conn, error) {
	url := "ws://localhost:3000"
	res, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil || res == nil {
		return nil, err
	}

	connect, err := json.Marshal(fmt.Sprintf("/announce|{\"id\":\"%s\"}|{\"room\":\"%s\"}", id, room))
	if err != nil {
		return nil, err
	}

	err = res.WriteMessage(websocket.TextMessage, connect)
	if err != nil {
		return nil, err
	}

	res.SetReadDeadline(time.Now().Add(4 * time.Millisecond))

	return res, nil
}
<|endoftext|>


<commit_before>package IRC

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/jriddick/geoffrey/msg"
)

// IRC client
type IRC struct {
	sync.WaitGroup
	conn         net.Conn
	get          chan *msg.Message
	put          chan string
	end          chan struct{}
	err          chan error
	config       Config
	reconnecting bool
}

// NewIRC returns a new IRC client
func NewIRC(config Config) *IRC {
	return &IRC{
		config: config,
	}
}

func (m *IRC) loopPut() {
	defer m.Done()

	for {
		select {
		case <-m.end:
			return
		case msg, ok := <-m.put:
			// Make sure we received a value
			if !ok {
				m.err <- fmt.Errorf("send channel is invalid")
				return
			}

			if m.conn == nil {
				m.err <- fmt.Errorf("no connection active open")
				return
			}

			// We do not send any empty values
			if msg == "" {
				m.err <- fmt.Errorf("tried to send empty message")
				continue
			}

			// Make sure the suffix is correct
			if !strings.HasSuffix(msg, "\r\n") {
				msg = msg + "\r\n"
			}

			// Set the timeout
			m.conn.SetWriteDeadline(time.Now().Add(time.Second * 30))

			// Send the message to the server
			_, err := m.conn.Write([]byte(msg))

			// Reset the timeout
			m.conn.SetWriteDeadline(time.Time{})

			// Make sure we did not get any errors
			if err != nil {
				m.err <- err
				return
			}
		}
	}
}

func (m *IRC) loopGet() {
	defer m.Done()

	// Reader for the connection
	reader := bufio.NewReaderSize(m.conn, 1024)

	for {
		select {
		case <-m.end:
			return
		default:
			if m.conn != nil {
				// Set the read timeout
				m.conn.SetReadDeadline(time.Now().Add(time.Second * 30))
			}

			// Fetch the message from the server
			raw, err := reader.ReadString('\n')

			if err != nil {
				// Ignore any errors during reconnect
				if !m.reconnecting {
					m.err <- err
				}

				return
			}

			// Reset the timeout
			if m.conn != nil {
				m.conn.SetReadDeadline(time.Time{})
			}

			// Parse the message
			msg, err := msg.ParseMessage(raw)

			if err != nil {
				m.err <- fmt.Errorf("%v [%s]", err, raw)
				return
			}

			// Send the parsed message
			m.get <- msg
		}
	}
}

// Disconnect will disconnect the client
func (m *IRC) Disconnect() {
	// Close the channels
	if m.end != nil {
		close(m.end)
	}

	if m.put != nil {
		close(m.put)
	}

	if m.get != nil {
		close(m.get)
	}

	// Close the connection
	if m.conn != nil {
		m.conn.Close()
	}

	// Reset the connection
	m.conn = nil

	// Wait for loops
	m.Wait()
}

// Connect will connect the client, create new channels if needed
// and start the handler loops.
func (m *IRC) Connect() error {
	// Don't connect if we already are connected
	if m.conn != nil {
		return fmt.Errorf("connection already active")
	}

	// Holds any encountered errors
	var err error

	// Get the hostname
	hostname := m.config.GetHostname()

	if hostname == ":0" || hostname[0] == ':' {
		return fmt.Errorf("need hostname and port to connect")
	}

	// Create the connection
	if m.config.Secure {
		m.conn, err = tls.Dial("tcp", hostname, &tls.Config{
			InsecureSkipVerify: m.config.InsecureSkipVerify,
		})
	} else {
		m.conn, err = net.Dial("tcp", hostname)
	}

	// Check for errors
	if err != nil {
		return err
	}

	// Create the input and output channels
	if m.get == nil {
		m.get = make(chan *msg.Message, 100)
	}

	if m.put == nil {
		m.put = make(chan string, 100)
	}

	if m.end == nil {
		m.end = make(chan struct{})
	}

	if m.err == nil {
		m.err = make(chan error, 100)
	}

	// Start the loops
	m.Add(2)
	go m.loopGet()
	go m.loopPut()

	return nil
}

// Reconnect will disconnect, stop the loops and then call Connect()
func (m *IRC) Reconnect() error {
	// Flag as reconnecting
	m.reconnecting = true

	// Close the connection
	if m.conn != nil {
		m.conn.Close()
	}

	// Reset the connection
	m.conn = nil

	// Close the channel
	close(m.end)

	// Wait until loops complete
	m.Wait()

	// Create the end channel
	m.end = make(chan struct{})

	// Remove the flag
	m.reconnecting = false

	// Connect to the server again
	return m.Connect()
}

// Reader returns channel for reading messages
func (m *IRC) Reader() <-chan *msg.Message {
	return m.get
}

// Writer returns channel for sending messages
func (m *IRC) Writer() chan<- string {
	return m.put
}

// Errors returns channel for reading errors
func (m *IRC) Errors() <-chan error {
	return m.err
}
<commit_msg>fixed irc client package name<commit_after>package irc

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/jriddick/geoffrey/msg"
)

// IRC client
type IRC struct {
	sync.WaitGroup
	conn         net.Conn
	get          chan *msg.Message
	put          chan string
	end          chan struct{}
	err          chan error
	config       Config
	reconnecting bool
}

// NewIRC returns a new IRC client
func NewIRC(config Config) *IRC {
	return &IRC{
		config: config,
	}
}

func (m *IRC) loopPut() {
	defer m.Done()

	for {
		select {
		case <-m.end:
			return
		case msg, ok := <-m.put:
			// Make sure we received a value
			if !ok {
				m.err <- fmt.Errorf("send channel is invalid")
				return
			}

			if m.conn == nil {
				m.err <- fmt.Errorf("no connection active open")
				return
			}

			// We do not send any empty values
			if msg == "" {
				m.err <- fmt.Errorf("tried to send empty message")
				continue
			}

			// Make sure the suffix is correct
			if !strings.HasSuffix(msg, "\r\n") {
				msg = msg + "\r\n"
			}

			// Set the timeout
			m.conn.SetWriteDeadline(time.Now().Add(time.Second * 30))

			// Send the message to the server
			_, err := m.conn.Write([]byte(msg))

			// Reset the timeout
			m.conn.SetWriteDeadline(time.Time{})

			// Make sure we did not get any errors
			if err != nil {
				m.err <- err
				return
			}
		}
	}
}

func (m *IRC) loopGet() {
	defer m.Done()

	// Reader for the connection
	reader := bufio.NewReaderSize(m.conn, 1024)

	for {
		select {
		case <-m.end:
			return
		default:
			if m.conn != nil {
				// Set the read timeout
				m.conn.SetReadDeadline(time.Now().Add(time.Second * 30))
			}

			// Fetch the message from the server
			raw, err := reader.ReadString('\n')

			if err != nil {
				// Ignore any errors during reconnect
				if !m.reconnecting {
					m.err <- err
				}

				return
			}

			// Reset the timeout
			if m.conn != nil {
				m.conn.SetReadDeadline(time.Time{})
			}

			// Parse the message
			msg, err := msg.ParseMessage(raw)

			if err != nil {
				m.err <- fmt.Errorf("%v [%s]", err, raw)
				return
			}

			// Send the parsed message
			m.get <- msg
		}
	}
}

// Disconnect will disconnect the client
func (m *IRC) Disconnect() {
	// Close the channels
	if m.end != nil {
		close(m.end)
	}

	if m.put != nil {
		close(m.put)
	}

	if m.get != nil {
		close(m.get)
	}

	// Close the connection
	if m.conn != nil {
		m.conn.Close()
	}

	// Reset the connection
	m.conn = nil

	// Wait for loops
	m.Wait()
}

// Connect will connect the client, create new channels if needed
// and start the handler loops.
func (m *IRC) Connect() error {
	// Don't connect if we already are connected
	if m.conn != nil {
		return fmt.Errorf("connection already active")
	}

	// Holds any encountered errors
	var err error

	// Get the hostname
	hostname := m.config.GetHostname()

	if hostname == ":0" || hostname[0] == ':' {
		return fmt.Errorf("need hostname and port to connect")
	}

	// Create the connection
	if m.config.Secure {
		m.conn, err = tls.Dial("tcp", hostname, &tls.Config{
			InsecureSkipVerify: m.config.InsecureSkipVerify,
		})
	} else {
		m.conn, err = net.Dial("tcp", hostname)
	}

	// Check for errors
	if err != nil {
		return err
	}

	// Create the input and output channels
	if m.get == nil {
		m.get = make(chan *msg.Message, 100)
	}

	if m.put == nil {
		m.put = make(chan string, 100)
	}

	if m.end == nil {
		m.end = make(chan struct{})
	}

	if m.err == nil {
		m.err = make(chan error, 100)
	}

	// Start the loops
	m.Add(2)
	go m.loopGet()
	go m.loopPut()

	return nil
}

// Reconnect will disconnect, stop the loops and then call Connect()
func (m *IRC) Reconnect() error {
	// Flag as reconnecting
	m.reconnecting = true

	// Close the connection
	if m.conn != nil {
		m.conn.Close()
	}

	// Reset the connection
	m.conn = nil

	// Close the channel
	close(m.end)

	// Wait until loops complete
	m.Wait()

	// Create the end channel
	m.end = make(chan struct{})

	// Remove the flag
	m.reconnecting = false

	// Connect to the server again
	return m.Connect()
}

// Reader returns channel for reading messages
func (m *IRC) Reader() <-chan *msg.Message {
	return m.get
}

// Writer returns channel for sending messages
func (m *IRC) Writer() chan<- string {
	return m.put
}

// Errors returns channel for reading errors
func (m *IRC) Errors() <-chan error {
	return m.err
}
<|endoftext|>


<commit_before>// Copyright © 2014 Daniele Tricoli <eriol@mornie.org>.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package irc // import "eriol.xyz/perpetua/irc"

import (
	"crypto/tls"
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/thoj/go-ircevent"

	"eriol.xyz/perpetua/config"
	"eriol.xyz/perpetua/db"
)

const version = "perpetua quote bot " + config.Version

var connection *irc.Connection
var conf *config.Config
var store *db.Store

// Localizated quote and about tokens used to detect the kind of query for
// the bot.
var i18n = map[string]map[string][]string{
	"en": map[string][]string{
		"quote": []string{"quote", "what does it say"},
		"about": []string{"about"},
	},
	"it": map[string][]string{
		"quote": []string{
			"cita",
			"che dice",
			"cosa dice",
			"che cosa dice"},
		"about": []string{
			"su",
			"sul",
			"sulla",
			"sullo",
			"sui",
			"sugli",
			"sulle"},
	},
}

// Join keys from i18n using "|": used inside the regex to perform an
// OR of all keys.
func i18nKeyJoin(lang, key string) string {
	return strings.Join(i18n[lang][key], "|")
}

func connect() {
	connection = irc.IRC(conf.IRC.Nickname, conf.IRC.User)
	connection.Version = version
	connection.UseTLS = conf.Server.UseTLS
	if conf.Server.SkipVerify == true {
		connection.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	err := connection.Connect(fmt.Sprintf("%s:%d",
		conf.Server.Hostname,
		conf.Server.Port))

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

func handleEvents() {
	connection.AddCallback("001", doWelcome)
	connection.AddCallback("JOIN", doJoin)
	connection.AddCallback("PRIVMSG", doPrivmsg)
}

func doWelcome(event *irc.Event) {
	for _, channel := range conf.IRC.Channels {
		connection.Join(channel)
		connection.Log.Println("Joined to " + channel)
	}
}

func doJoin(event *irc.Event) {
	channel := event.Arguments[0]

	if event.Nick == conf.IRC.Nickname {
		connection.Privmsg(channel, "Hello! I'm "+version)
	} else {
		connection.Privmsg(channel,
			fmt.Sprintf("Hello %s! I'm %s. Do you want a quote?",
				event.Nick,
				version))
	}
}

func doPrivmsg(event *irc.Event) {
	channel := event.Arguments[0]
	var quote string

	// Don't speak in private!
	if channel == conf.IRC.Nickname {
		return
	}
	command, person, extra, argument := parseMessage(event.Message())

	if command != "" && person != "" {

		quote = store.GetQuote(person, channel)

		if extra != "" && argument != "" {
			quote = store.GetQuoteAbout(person, argument, channel)
		}

		connection.Privmsg(channel, quote)
	}
}

func parseMessage(message string) (command, person, extra, argument string) {
	var names []string
	lang := conf.I18N.Lang

	reArgument := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)` +
		`(?:\s+)` +
		`(?P<extra>` + i18nKeyJoin(lang, "about") + `)` +
		`(?:\s+)` +
		`(?P<argument>[\w\s-'\p{Latin}]+)`)

	re := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)`)

	res := reArgument.FindStringSubmatch(message)

	if res == nil {
		res = re.FindStringSubmatch(message)
		names = re.SubexpNames()
	} else {
		names = reArgument.SubexpNames()
	}

	m := map[string]string{}
	for i, n := range res {
		m[names[i]] = n
	}

	return m["command"], m["person"], m["extra"], m["argument"]
}

func Client(c *config.Config, db *db.Store) {
	conf = c
	store = db
	connect()
	handleEvents()
	connection.Loop()
}
<commit_msg>Use connection provided by Event type in callbacks<commit_after>// Copyright © 2014 Daniele Tricoli <eriol@mornie.org>.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package irc // import "eriol.xyz/perpetua/irc"

import (
	"crypto/tls"
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/thoj/go-ircevent"

	"eriol.xyz/perpetua/config"
	"eriol.xyz/perpetua/db"
)

const version = "perpetua quote bot " + config.Version

var connection *irc.Connection
var conf *config.Config
var store *db.Store

// Localizated quote and about tokens used to detect the kind of query for
// the bot.
var i18n = map[string]map[string][]string{
	"en": map[string][]string{
		"quote": []string{"quote", "what does it say"},
		"about": []string{"about"},
	},
	"it": map[string][]string{
		"quote": []string{
			"cita",
			"che dice",
			"cosa dice",
			"che cosa dice"},
		"about": []string{
			"su",
			"sul",
			"sulla",
			"sullo",
			"sui",
			"sugli",
			"sulle"},
	},
}

// Join keys from i18n using "|": used inside the regex to perform an
// OR of all keys.
func i18nKeyJoin(lang, key string) string {
	return strings.Join(i18n[lang][key], "|")
}

func connect() {
	connection = irc.IRC(conf.IRC.Nickname, conf.IRC.User)
	connection.Version = version
	connection.UseTLS = conf.Server.UseTLS
	if conf.Server.SkipVerify == true {
		connection.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	err := connection.Connect(fmt.Sprintf("%s:%d",
		conf.Server.Hostname,
		conf.Server.Port))

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

func handleEvents() {
	connection.AddCallback("001", doWelcome)
	connection.AddCallback("JOIN", doJoin)
	connection.AddCallback("PRIVMSG", doPrivmsg)
}

func doWelcome(event *irc.Event) {
	for _, channel := range conf.IRC.Channels {
		event.Connection.Join(channel)
		event.Connection.Log.Println("Joined to " + channel)
	}
}

func doJoin(event *irc.Event) {
	channel := event.Arguments[0]

	if event.Nick == conf.IRC.Nickname {
		event.Connection.Privmsg(channel, "Hello! I'm "+version)
	} else {
		event.Connection.Privmsg(channel,
			fmt.Sprintf("Hello %s! I'm %s. Do you want a quote?",
				event.Nick,
				version))
	}
}

func doPrivmsg(event *irc.Event) {
	channel := event.Arguments[0]
	var quote string

	// Don't speak in private!
	if channel == conf.IRC.Nickname {
		return
	}
	command, person, extra, argument := parseMessage(event.Message())

	if command != "" && person != "" {

		quote = store.GetQuote(person, channel)

		if extra != "" && argument != "" {
			quote = store.GetQuoteAbout(person, argument, channel)
		}

		event.Connection.Privmsg(channel, quote)
	}
}

func parseMessage(message string) (command, person, extra, argument string) {
	var names []string
	lang := conf.I18N.Lang

	reArgument := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)` +
		`(?:\s+)` +
		`(?P<extra>` + i18nKeyJoin(lang, "about") + `)` +
		`(?:\s+)` +
		`(?P<argument>[\w\s-'\p{Latin}]+)`)

	re := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)`)

	res := reArgument.FindStringSubmatch(message)

	if res == nil {
		res = re.FindStringSubmatch(message)
		names = re.SubexpNames()
	} else {
		names = reArgument.SubexpNames()
	}

	m := map[string]string{}
	for i, n := range res {
		m[names[i]] = n
	}

	return m["command"], m["person"], m["extra"], m["argument"]
}

func Client(c *config.Config, db *db.Store) {
	conf = c
	store = db
	connect()
	handleEvents()
	connection.Loop()
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Chadev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"

	"github.com/danryan/hal"
)

var isAliveHandler = hear(`is ([A-Za-z0-9\-_\{\}\[\]\\\s]+) alive`, "is (name) alive", "Find out if a user is alive", func(res *hal.Response) error {
	name := res.Match[1]
	res.Send(fmt.Sprintf("I can't find %s's heartbeat..", name))
	res.Send("But let's not jump to conclusions")
	return nil
})
<commit_msg>Make the is alive check the user list before reporting<commit_after>// Copyright 2014-2015 Chadev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"

	"github.com/danryan/hal"
)

var isAliveHandler = hear(`is ([A-Za-z0-9\-_\{\}\[\]\\\s]+) alive`, "is (name) alive", "Find out if a user is alive", func(res *hal.Response) error {
	name := res.Match[1]
	_, err := res.Robot.Users.Get(name)
	if err != nil {
		res.Send(fmt.Sprintf("I can't find %s's heartbeat..", name))
		res.Send("But let's not jump to conclusions")
	} else {
		res.Reply(fmt.Sprintf("I found %s's heartbeat, all is good", name))
	}

	return nil
})
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Magnús Örn Gylfason
//
// 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.

// The Iterator ToolKit package (itk) provides reusable components
// to help with the construction of iterators.
//
// Often when we write iterators we are simply writing adapters that wrap
// around existing iterators and change some but not all of the methods.
// The wrappers in itk exists to reduce the amount of boilerplate code we
// need to write in those cases. Simply use composition to wrap the iterator,
// provide the logic for the methods you whish to change, and let the wrapper
// handle the rest.
//
// E.g. LyingIterator is a RandomAccess iterator that lies about its length.
// It supports all other methods defined in i.RandomAccess through the wrapper.
//
// type lyingiterator struct {
//   itk.WRandomAccess
// }
//
// func LyingIterator( itr i.RandomAccess) i.RandomAccess {
//   var li lyingiterator
//   li.WRandomAccess= *(itk.WrapRandomAccess(itr))
//   return &li
// }
//
// func (li *lyingiterator) {
//   return li.WRandomAccess.Len()/2
// }
//
package itk
<commit_msg>itk: Documentation<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Magnús Örn Gylfason
//
// 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.

// The Iterator ToolKit package (itk) provides reusable components
// to help with the construction of iterators.
//
// Often when we write iterators we are simply writing adapters that wrap
// around existing iterators and change some but not all of the methods.
// The wrappers in itk exists to reduce the amount of boilerplate code we
// need to write in those cases. Simply use composition to wrap the iterator,
// provide the logic for the methods you whish to change, and let the wrapper
// handle the rest.
//
// E.g. LyingIterator is a RandomAccess iterator that lies about its length.
// It supports all other methods defined in i.RandomAccess through the wrapper.
//
//   type lyingiterator struct {
//     itk.WRandomAccess
//   }
//
//   func LyingIterator( itr i.RandomAccess) i.RandomAccess {
//     var li lyingiterator
//     li.WRandomAccess= *(itk.WrapRandomAccess(itr))
//     return &li
//   }
//
//   func (li *lyingiterator) {
//     return li.WRandomAccess.Len()/2
//   }
//
package itk
<|endoftext|>


<commit_before>package gojenkins

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

type Auth struct {
	Username string
	ApiToken string
}

type Jenkins struct {
	auth    *Auth
	baseUrl string
}

func NewJenkins(auth *Auth, baseUrl string) *Jenkins {
	return &Jenkins{
		auth:    auth,
		baseUrl: baseUrl,
	}
}

func (jenkins *Jenkins) buildUrl(path string, params url.Values) (requestUrl string) {
	requestUrl = jenkins.baseUrl + path + "/api/json"
	if params != nil {
		queryString := params.Encode()
		if queryString != "" {
			requestUrl = requestUrl + "?" + queryString
		}
	}

	return
}

func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) {
	req.SetBasicAuth(jenkins.auth.Username, jenkins.auth.ApiToken)
	return http.DefaultClient.Do(req)
}

func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) {
	defer resp.Body.Close()

	if body == nil {
		return
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	return json.Unmarshal(data, body)
}

func (jenkins *Jenkins) get(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	return jenkins.parseResponse(resp, body)
}

func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("POST", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}

	return jenkins.parseResponse(resp, body)
}

func (jenkins *Jenkins) GetJobs() (jobs []Job, err error) {
	var payload = struct {
		Jobs []Job `json:"jobs"`
	}{
		Jobs: jobs,
	}
	err = jenkins.get("", nil, &payload)
	return
}

func (jenkins *Jenkins) GetJob(name string) (job Job, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s", name), nil, &job)
	return
}

func (jenkins *Jenkins) GetBuild(job Job, number int) (build Build, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s/%d", job.Name, number), nil, &build)
	return
}

// Create a new build for this job.
// Params can be nil.
func (jenkins *Jenkins) Build(job Job, params url.Values) error {
	if params == nil {
		return jenkins.post(fmt.Sprintf("/job/%s/build", job.Name), params, nil)
	} else {
		return jenkins.post(fmt.Sprintf("/job/%s/buildWithParameters", job.Name), params, nil)
	}
}

// Get the console output from a build.
func (jenkins *Jenkins) GetBuildConsoleOutput(build Build) ([]byte, error) {
	requestUrl := fmt.Sprintf("%s/logText/progressiveText?start=0", build.Url)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return nil, err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	return ioutil.ReadAll(res.Body)
}
<commit_msg>use consoleText instead of progressiveText for console output<commit_after>package gojenkins

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

type Auth struct {
	Username string
	ApiToken string
}

type Jenkins struct {
	auth    *Auth
	baseUrl string
}

func NewJenkins(auth *Auth, baseUrl string) *Jenkins {
	return &Jenkins{
		auth:    auth,
		baseUrl: baseUrl,
	}
}

func (jenkins *Jenkins) buildUrl(path string, params url.Values) (requestUrl string) {
	requestUrl = jenkins.baseUrl + path + "/api/json"
	if params != nil {
		queryString := params.Encode()
		if queryString != "" {
			requestUrl = requestUrl + "?" + queryString
		}
	}

	return
}

func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) {
	req.SetBasicAuth(jenkins.auth.Username, jenkins.auth.ApiToken)
	return http.DefaultClient.Do(req)
}

func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) {
	defer resp.Body.Close()

	if body == nil {
		return
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	return json.Unmarshal(data, body)
}

func (jenkins *Jenkins) get(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	return jenkins.parseResponse(resp, body)
}

func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("POST", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}

	return jenkins.parseResponse(resp, body)
}

func (jenkins *Jenkins) GetJobs() (jobs []Job, err error) {
	var payload = struct {
		Jobs []Job `json:"jobs"`
	}{
		Jobs: jobs,
	}
	err = jenkins.get("", nil, &payload)
	return
}

func (jenkins *Jenkins) GetJob(name string) (job Job, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s", name), nil, &job)
	return
}

func (jenkins *Jenkins) GetBuild(job Job, number int) (build Build, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s/%d", job.Name, number), nil, &build)
	return
}

// Create a new build for this job.
// Params can be nil.
func (jenkins *Jenkins) Build(job Job, params url.Values) error {
	if params == nil {
		return jenkins.post(fmt.Sprintf("/job/%s/build", job.Name), params, nil)
	} else {
		return jenkins.post(fmt.Sprintf("/job/%s/buildWithParameters", job.Name), params, nil)
	}
}

// Get the console output from a build.
func (jenkins *Jenkins) GetBuildConsoleOutput(build Build) ([]byte, error) {
	requestUrl := fmt.Sprintf("%s/consoleText", build.Url)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return nil, err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	return ioutil.ReadAll(res.Body)
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"reflect"
	"strings"

	"github.com/animenotifier/arn/validate"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/fatih/color"
)

// Force interface implementations
var (
	_ api.Editable = (*User)(nil)
)

// Authorize returns an error if the given API POST request is not authorized.
func (user *User) Authorize(ctx *aero.Context, action string) error {
	editor := GetUserFromContext(ctx)

	if editor == nil {
		return errors.New("Not authorized")
	}

	if editor.ID != ctx.Get("id") && editor.Role != "admin" {
		return errors.New("Can not modify data from other users")
	}

	return nil
}

// Edit updates the user object.
func (user *User) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	switch key {
	case "Nick":
		newNick := newValue.String()
		err := user.SetNick(newNick)
		return true, err

	case "Email":
		newEmail := newValue.String()
		err := user.SetEmail(newEmail)
		return true, err

	case "Website":
		newSite := newValue.String()

		if autocorrect.IsTrackerLink(newSite) {
			return true, errors.New("Not an actual website or homepage")
		}

		user.Website = autocorrect.Website(newSite)
		return true, nil

	case "BirthDay":
		newBirthDay := newValue.String()

		if AgeInYears(newBirthDay) <= 0 {
			return true, errors.New("Invalid birthday (make sure to use YYYY-MM-DD format, e.g. 2000-01-17)")
		}

		user.BirthDay = newBirthDay
		return true, nil

	case "ProExpires":
		user := GetUserFromContext(ctx)

		if user == nil || user.Role != "admin" {
			return true, errors.New("Not authorized to edit")
		}

	case "Accounts.Discord.Nick":
		newNick := newValue.String()

		if newNick == "" {
			value.SetString(newNick)
			return true, nil
		}

		if !validate.DiscordNick(newNick) {
			return true, errors.New("Discord username must include your name and the 4-digit Discord tag (e.g. Yandere#1234)")
		}

		// Trim spaces
		parts := strings.Split(newNick, "#")
		parts[0] = strings.TrimSpace(parts[0])
		parts[1] = strings.TrimSpace(parts[1])
		newNick = strings.Join(parts, "#")

		value.SetString(newNick)
		return true, nil

	case "Accounts.Overwatch.BattleTag":
		newBattleTag := newValue.String()
		value.SetString(newBattleTag)

		if newBattleTag == "" {
			user.Accounts.Overwatch.SkillRating = 0
			user.Accounts.Overwatch.Tier = ""
		} else {
			// Refresh Overwatch info if the battletag changed
			go func() {
				err := user.RefreshOverwatchInfo()

				if err != nil {
					color.Red("Error refreshing Overwatch info of user '%s' with Overwatch battle tag '%s': %v", user.Nick, newBattleTag, err)
					return
				}

				color.Green("Refreshed Overwatch info of user '%s' with Overwatch battle tag '%s': %v", user.Nick, newBattleTag, user.Accounts.Overwatch.SkillRating)
				user.Save()
			}()
		}

		return true, nil

	case "Accounts.FinalFantasyXIV.Nick", "Accounts.FinalFantasyXIV.Server":
		newValue := newValue.String()
		value.SetString(newValue)

		if newValue == "" {
			user.Accounts.FinalFantasyXIV.Class = ""
			user.Accounts.FinalFantasyXIV.Level = 0
			user.Accounts.FinalFantasyXIV.ItemLevel = 0
		} else if user.Accounts.FinalFantasyXIV.Nick != "" && user.Accounts.FinalFantasyXIV.Server != "" {
			// Refresh FinalFantasyXIV info if the name or server changed
			go func() {
				err := user.RefreshFFXIVInfo()

				if err != nil {
					color.Red("Error refreshing FinalFantasy XIV info of user '%s' with nick '%s' on server '%s': %v", user.Nick, user.Accounts.FinalFantasyXIV.Nick, user.Accounts.FinalFantasyXIV.Server, err)
					return
				}

				user.Save()
			}()
		}

		return true, nil
	}

	// Automatically correct account nicks
	if strings.HasPrefix(key, "Accounts.") && strings.HasSuffix(key, ".Nick") {
		newNick := newValue.String()
		newNick = autocorrect.AccountNick(newNick)
		value.SetString(newNick)

		// Refresh osu info if the name changed
		if key == "Accounts.Osu.Nick" {
			if newNick == "" {
				user.Accounts.Osu.PP = 0
				user.Accounts.Osu.Level = 0
				user.Accounts.Osu.Accuracy = 0
			} else {
				go func() {
					err := user.RefreshOsuInfo()

					if err != nil {
						color.Red("Error refreshing osu info of user '%s' with osu nick '%s': %v", user.Nick, newNick, err)
						return
					}

					color.Green("Refreshed osu info of user '%s' with osu nick '%s': %v", user.Nick, newNick, user.Accounts.Osu.PP)
					user.Save()
				}()
			}
		}

		return true, nil
	}

	return false, nil
}

// Save saves the user object in the database.
func (user *User) Save() {
	DB.Set("User", user.ID, user)
}

// Filter removes privacy critical fields from the user object.
func (user *User) Filter() {
	user.Email = ""
	user.Gender = ""
	user.FirstName = ""
	user.LastName = ""
	user.IP = ""
	user.UserAgent = ""
	user.LastLogin = ""
	user.LastSeen = ""
	user.Accounts.Facebook.ID = ""
	user.Accounts.Google.ID = ""
	user.Accounts.Twitter.ID = ""
	user.BirthDay = ""
	user.Location = &Location{}
	user.Browser = UserBrowser{}
	user.OS = UserOS{}
}

// ShouldFilter tells whether data needs to be filtered in the given context.
func (user *User) ShouldFilter(ctx *aero.Context) bool {
	ctxUser := GetUserFromContext(ctx)

	if ctxUser != nil && ctxUser.Role == "admin" {
		return false
	}

	return true
}
<commit_msg>Editing Discord account will reset verified status<commit_after>package arn

import (
	"errors"
	"reflect"
	"strings"

	"github.com/animenotifier/arn/validate"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/fatih/color"
)

// Force interface implementations
var (
	_ api.Editable = (*User)(nil)
)

// Authorize returns an error if the given API POST request is not authorized.
func (user *User) Authorize(ctx *aero.Context, action string) error {
	editor := GetUserFromContext(ctx)

	if editor == nil {
		return errors.New("Not authorized")
	}

	if editor.ID != ctx.Get("id") && editor.Role != "admin" {
		return errors.New("Can not modify data from other users")
	}

	return nil
}

// Edit updates the user object.
func (user *User) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	switch key {
	case "Nick":
		newNick := newValue.String()
		err := user.SetNick(newNick)
		return true, err

	case "Email":
		newEmail := newValue.String()
		err := user.SetEmail(newEmail)
		return true, err

	case "Website":
		newSite := newValue.String()

		if autocorrect.IsTrackerLink(newSite) {
			return true, errors.New("Not an actual website or homepage")
		}

		user.Website = autocorrect.Website(newSite)
		return true, nil

	case "BirthDay":
		newBirthDay := newValue.String()

		if AgeInYears(newBirthDay) <= 0 {
			return true, errors.New("Invalid birthday (make sure to use YYYY-MM-DD format, e.g. 2000-01-17)")
		}

		user.BirthDay = newBirthDay
		return true, nil

	case "ProExpires":
		user := GetUserFromContext(ctx)

		if user == nil || user.Role != "admin" {
			return true, errors.New("Not authorized to edit")
		}

	case "Accounts.Discord.Nick":
		newNick := newValue.String()

		if newNick == "" {
			value.SetString(newNick)
			user.Accounts.Discord.Verified = false
			return true, nil
		}

		if !validate.DiscordNick(newNick) {
			return true, errors.New("Discord username must include your name and the 4-digit Discord tag (e.g. Yandere#1234)")
		}

		// Trim spaces
		parts := strings.Split(newNick, "#")
		parts[0] = strings.TrimSpace(parts[0])
		parts[1] = strings.TrimSpace(parts[1])
		newNick = strings.Join(parts, "#")

		if value.String() != newNick {
			value.SetString(newNick)
			user.Accounts.Discord.Verified = false
		}

		return true, nil

	case "Accounts.Overwatch.BattleTag":
		newBattleTag := newValue.String()
		value.SetString(newBattleTag)

		if newBattleTag == "" {
			user.Accounts.Overwatch.SkillRating = 0
			user.Accounts.Overwatch.Tier = ""
		} else {
			// Refresh Overwatch info if the battletag changed
			go func() {
				err := user.RefreshOverwatchInfo()

				if err != nil {
					color.Red("Error refreshing Overwatch info of user '%s' with Overwatch battle tag '%s': %v", user.Nick, newBattleTag, err)
					return
				}

				color.Green("Refreshed Overwatch info of user '%s' with Overwatch battle tag '%s': %v", user.Nick, newBattleTag, user.Accounts.Overwatch.SkillRating)
				user.Save()
			}()
		}

		return true, nil

	case "Accounts.FinalFantasyXIV.Nick", "Accounts.FinalFantasyXIV.Server":
		newValue := newValue.String()
		value.SetString(newValue)

		if newValue == "" {
			user.Accounts.FinalFantasyXIV.Class = ""
			user.Accounts.FinalFantasyXIV.Level = 0
			user.Accounts.FinalFantasyXIV.ItemLevel = 0
		} else if user.Accounts.FinalFantasyXIV.Nick != "" && user.Accounts.FinalFantasyXIV.Server != "" {
			// Refresh FinalFantasyXIV info if the name or server changed
			go func() {
				err := user.RefreshFFXIVInfo()

				if err != nil {
					color.Red("Error refreshing FinalFantasy XIV info of user '%s' with nick '%s' on server '%s': %v", user.Nick, user.Accounts.FinalFantasyXIV.Nick, user.Accounts.FinalFantasyXIV.Server, err)
					return
				}

				user.Save()
			}()
		}

		return true, nil
	}

	// Automatically correct account nicks
	if strings.HasPrefix(key, "Accounts.") && strings.HasSuffix(key, ".Nick") {
		newNick := newValue.String()
		newNick = autocorrect.AccountNick(newNick)
		value.SetString(newNick)

		// Refresh osu info if the name changed
		if key == "Accounts.Osu.Nick" {
			if newNick == "" {
				user.Accounts.Osu.PP = 0
				user.Accounts.Osu.Level = 0
				user.Accounts.Osu.Accuracy = 0
			} else {
				go func() {
					err := user.RefreshOsuInfo()

					if err != nil {
						color.Red("Error refreshing osu info of user '%s' with osu nick '%s': %v", user.Nick, newNick, err)
						return
					}

					color.Green("Refreshed osu info of user '%s' with osu nick '%s': %v", user.Nick, newNick, user.Accounts.Osu.PP)
					user.Save()
				}()
			}
		}

		return true, nil
	}

	return false, nil
}

// Save saves the user object in the database.
func (user *User) Save() {
	DB.Set("User", user.ID, user)
}

// Filter removes privacy critical fields from the user object.
func (user *User) Filter() {
	user.Email = ""
	user.Gender = ""
	user.FirstName = ""
	user.LastName = ""
	user.IP = ""
	user.UserAgent = ""
	user.LastLogin = ""
	user.LastSeen = ""
	user.Accounts.Facebook.ID = ""
	user.Accounts.Google.ID = ""
	user.Accounts.Twitter.ID = ""
	user.BirthDay = ""
	user.Location = &Location{}
	user.Browser = UserBrowser{}
	user.OS = UserOS{}
}

// ShouldFilter tells whether data needs to be filtered in the given context.
func (user *User) ShouldFilter(ctx *aero.Context) bool {
	ctxUser := GetUserFromContext(ctx)

	if ctxUser != nil && ctxUser.Role == "admin" {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package graval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	welcomeMessage = "Welcome to the Go FTP Server"
)

type FTPConn struct {
	cwd           string
	conn          *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
	driver        FTPDriver
	reqUser       string
	user          string
}

// NewFTPConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDrive that
// will handle all auth and persistence details.
func NewFTPConn(tcpConn *net.TCPConn, driver FTPDriver) *FTPConn {
	c := new(FTPConn)
	c.cwd = "/"
	c.conn = tcpConn
	c.controlReader = bufio.NewReader(tcpConn)
	c.controlWriter = bufio.NewWriter(tcpConn)
	c.driver = driver
	return c
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err == nil {
			ftpConn.receiveLine(line)
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *FTPConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *FTPConn) receiveLine(line string) {
	log.Print(line)
	command, param := ftpConn.parseLine(line)
	switch command {
	case "ALLO":
		ftpConn.cmdAllo()
		break
	case "MODE":
		ftpConn.cmdMode(param)
		break
	case "NOOP":
		ftpConn.cmdNoop()
		break
	case "PASS":
		ftpConn.cmdPass(param)
		break
	case "QUIT":
		ftpConn.Close()
		break
	case "STRU":
		ftpConn.cmdStru(param)
		break
	case "SYST":
		ftpConn.cmdSyst()
		break
	case "TYPE":
		ftpConn.cmdType(param)
		break
	case "USER":
		ftpConn.cmdUser(param)
		break
	default:
		ftpConn.writeMessage(500, "Command not found")
	}
}

// cmdNoop responds to the ALLO FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic OK message.
func (ftpConn *FTPConn) cmdAllo() {
	ftpConn.writeMessage(202, "Obsolete")
}

// cmdMode responds to the MODE FTP command.
//
// the original FTP spec had various options for hosts to negotiate how data
// would be sent over the data socket, In reality these days (S)tream mode
// is all that is used for the mode - data is just streamed down the data
// socket unchanged.
func (ftpConn *FTPConn) cmdMode(param string) {
	if strings.ToUpper(param) == "S" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "MODE is an obsolete command")
	}
}

// cmdNoop responds to the NOOP FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic 200 message.
func (ftpConn *FTPConn) cmdNoop() {
	ftpConn.writeMessage(200, "OK")
}

// cmdPass respond to the PASS FTP command by asking the driver if the supplied
// username and password are valid
func (ftpConn *FTPConn) cmdPass(param string) {
	if ftpConn.driver.Authenticate(ftpConn.reqUser, param) {
		ftpConn.user = ftpConn.reqUser
		ftpConn.reqUser = ""
		ftpConn.writeMessage(230, "Password ok, continue")
	} else {
		ftpConn.writeMessage(530, "Incorrect password, not logged in")
	}
}

// cmdStru responds to the STRU FTP command.
//
// like the MODE and TYPE commands, stru[cture] dates back to a time when the
// FTP protocol was more aware of the content of the files it was transferring,
// and would sometimes be expected to translate things like EOL markers on the
// fly.
//
// These days files are sent unmodified, and F(ile) mode is the only one we
// really need to support.
func (ftpConn *FTPConn) cmdStru(param string) {
	if strings.ToUpper(param) == "F" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "STRU is an obsolete command")
	}
}

// cmdSyst responds to the SYST FTP command by providing a canned response.
func (ftpConn *FTPConn) cmdSyst() {
	ftpConn.writeMessage(215, "UNIX Type: L8")
}

// cmdType responds to the TYPE FTP command.
//
//  like the MODE and STRU commands, TYPE dates back to a time when the FTP
//  protocol was more aware of the content of the files it was transferring, and
//  would sometimes be expected to translate things like EOL markers on the fly.
//
//  Valid options were A(SCII), I(mage), E(BCDIC) or LN (for local type). Since
//  we plan to just accept bytes from the client unchanged, I think Image mode is
//  adequate. The RFC requires we accept ASCII mode however, so accept it, but
//  ignore it.
func (ftpConn *FTPConn) cmdType(param string) {
	if strings.ToUpper(param) == "A" {
		ftpConn.writeMessage(200, "Type set to ASCII")
	} else if strings.ToUpper(param) == "I" {
		ftpConn.writeMessage(200, "Type set to binary")
	} else {
		ftpConn.writeMessage(500, "Invalid type")
	}
}

// cmdUser responds to the USER FTP command by asking for the password
func (ftpConn *FTPConn) cmdUser(param string) {
	ftpConn.reqUser = param
	ftpConn.writeMessage(331, "User name ok, password required")
}

func (ftpConn *FTPConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], params[1]
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *FTPConn) writeMessage(code int, message string) (wrote int, err error) {
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}
<commit_msg>log the response including code<commit_after>package graval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	welcomeMessage = "Welcome to the Go FTP Server"
)

type FTPConn struct {
	cwd           string
	conn          *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
	driver        FTPDriver
	reqUser       string
	user          string
}

// NewFTPConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDrive that
// will handle all auth and persistence details.
func NewFTPConn(tcpConn *net.TCPConn, driver FTPDriver) *FTPConn {
	c := new(FTPConn)
	c.cwd = "/"
	c.conn = tcpConn
	c.controlReader = bufio.NewReader(tcpConn)
	c.controlWriter = bufio.NewWriter(tcpConn)
	c.driver = driver
	return c
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err == nil {
			ftpConn.receiveLine(line)
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *FTPConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *FTPConn) receiveLine(line string) {
	log.Print(line)
	command, param := ftpConn.parseLine(line)
	switch command {
	case "ALLO":
		ftpConn.cmdAllo()
		break
	case "MODE":
		ftpConn.cmdMode(param)
		break
	case "NOOP":
		ftpConn.cmdNoop()
		break
	case "PASS":
		ftpConn.cmdPass(param)
		break
	case "QUIT":
		ftpConn.Close()
		break
	case "STRU":
		ftpConn.cmdStru(param)
		break
	case "SYST":
		ftpConn.cmdSyst()
		break
	case "TYPE":
		ftpConn.cmdType(param)
		break
	case "USER":
		ftpConn.cmdUser(param)
		break
	default:
		ftpConn.writeMessage(500, "Command not found")
	}
}

// cmdNoop responds to the ALLO FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic OK message.
func (ftpConn *FTPConn) cmdAllo() {
	ftpConn.writeMessage(202, "Obsolete")
}

// cmdMode responds to the MODE FTP command.
//
// the original FTP spec had various options for hosts to negotiate how data
// would be sent over the data socket, In reality these days (S)tream mode
// is all that is used for the mode - data is just streamed down the data
// socket unchanged.
func (ftpConn *FTPConn) cmdMode(param string) {
	if strings.ToUpper(param) == "S" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "MODE is an obsolete command")
	}
}

// cmdNoop responds to the NOOP FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic 200 message.
func (ftpConn *FTPConn) cmdNoop() {
	ftpConn.writeMessage(200, "OK")
}

// cmdPass respond to the PASS FTP command by asking the driver if the supplied
// username and password are valid
func (ftpConn *FTPConn) cmdPass(param string) {
	if ftpConn.driver.Authenticate(ftpConn.reqUser, param) {
		ftpConn.user = ftpConn.reqUser
		ftpConn.reqUser = ""
		ftpConn.writeMessage(230, "Password ok, continue")
	} else {
		ftpConn.writeMessage(530, "Incorrect password, not logged in")
	}
}

// cmdStru responds to the STRU FTP command.
//
// like the MODE and TYPE commands, stru[cture] dates back to a time when the
// FTP protocol was more aware of the content of the files it was transferring,
// and would sometimes be expected to translate things like EOL markers on the
// fly.
//
// These days files are sent unmodified, and F(ile) mode is the only one we
// really need to support.
func (ftpConn *FTPConn) cmdStru(param string) {
	if strings.ToUpper(param) == "F" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "STRU is an obsolete command")
	}
}

// cmdSyst responds to the SYST FTP command by providing a canned response.
func (ftpConn *FTPConn) cmdSyst() {
	ftpConn.writeMessage(215, "UNIX Type: L8")
}

// cmdType responds to the TYPE FTP command.
//
//  like the MODE and STRU commands, TYPE dates back to a time when the FTP
//  protocol was more aware of the content of the files it was transferring, and
//  would sometimes be expected to translate things like EOL markers on the fly.
//
//  Valid options were A(SCII), I(mage), E(BCDIC) or LN (for local type). Since
//  we plan to just accept bytes from the client unchanged, I think Image mode is
//  adequate. The RFC requires we accept ASCII mode however, so accept it, but
//  ignore it.
func (ftpConn *FTPConn) cmdType(param string) {
	if strings.ToUpper(param) == "A" {
		ftpConn.writeMessage(200, "Type set to ASCII")
	} else if strings.ToUpper(param) == "I" {
		ftpConn.writeMessage(200, "Type set to binary")
	} else {
		ftpConn.writeMessage(500, "Invalid type")
	}
}

// cmdUser responds to the USER FTP command by asking for the password
func (ftpConn *FTPConn) cmdUser(param string) {
	ftpConn.reqUser = param
	ftpConn.writeMessage(331, "User name ok, password required")
}

func (ftpConn *FTPConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], params[1]
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *FTPConn) writeMessage(code int, message string) (wrote int, err error) {
	line := fmt.Sprintf("%d %s\r\n", code, message)
	log.Print(line)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	return
}
<|endoftext|>