<commit_before>package sparse

import (
	"bytes"
	"fmt"
	"unsafe"

	"gonum.org/v1/gonum/mat"
)

var (
	_ mat.Matrix = (*BinaryVec)(nil)
	_ mat.Vector = (*BinaryVec)(nil)

	_ mat.Matrix    = (*Binary)(nil)
	_ mat.ColViewer = (*Binary)(nil)
)

const (
	// maxLen is the biggest slice/array len one can create on a 32/64b platform.
	//bitLen = ^uint(0) >> 1
	maxDataLen = ^uint(0)

	// the wordSize of a binary vector.  Bits are stored in slices of words.
	wordSize = uint(64)

	// log2WordSize is the binary logarithm (log base 2) of (wordSize)
	log2WordSize = uint(6)
)

// BinaryVec is a Binary Vector or Bit Vector type.  This is useful
// for representing vectors of features with a binary state (1 or 0).
// Although part of the sparse package, this type is not sparse itself
// and stores all bits even 0s.  However, as it makes use of 64 bit
// integers to store each set of 64 bits and then bitwise operators to
// manipulate the elements it will be more efficient in terms of both
// storage requirements and performance than a slice of bool values
// (8 bits per element) or even a typical Dense matrix of float64
// elements.  A compressed bitmap scheme could be used to take advantage
// of sparseness but may have an associated overhead.
type BinaryVec struct {
	length int
	data   []uint64
}

// NewBinaryVec creates a new BitSet with a hint that length bits will be required
func NewBinaryVec(length int) *BinaryVec {
	maxSize := (maxDataLen - wordSize + uint(1))
	if uint(length) > maxSize {
		panic(fmt.Errorf("sparse: Requested bit length of Binary vector (%d) too large.  %d is the maximum allowed", length, maxSize))
	}
	elements := int((uint(length) + (wordSize - 1)) >> log2WordSize)

	vec := &BinaryVec{
		length: length,
		data:   make([]uint64, elements),
	}

	return vec
}

// DistanceFrom is the number of bits that are different between the
// receiver and rhs i.e.
// 	recevier 	= 1001001
//	rhs 		= 1010101
// 	Distance	= 3
// because there are three bits that are different between the 2
// binary vectors.  This is sometimes referred to as the `Hamming
// distance` or `Matching distance`.  In this case, the distance
// is not normalised and is simply the raw count of differences.  To
// normalise the value simply divide this value by the vector's length.
func (b *BinaryVec) DistanceFrom(rhs *BinaryVec) int {
	differences := uint64(0)
	for i, word := range b.data {
		differences += popcount(word ^ rhs.data[i])
	}
	return int(differences)
}

// Dims returns the dimensions of the matrix as the number of rows, columns.
// As this is a vector, the second value representing the number of columns
// will be 1. This method is part of the Gonum mat.Matrix interface
func (b *BinaryVec) Dims() (int, int) {
	return b.Len(), 1
}

// At returns the value of the element at row i and column j.
// As this is a vector (only one column), j must be 0 otherwise the
// method panics.  This method is part of the Gonum mat.Matrix interface.
func (b *BinaryVec) At(i, j int) float64 {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	if j != 0 {
		panic(mat.ErrColAccess)
	}

	if b.bitIsSet(i) {
		return 1.0
	}
	return 0.0
}

// AtVec returns the value of the element at row i.  This method will panic if
// i > Len().  This method is part of the Gonum mat.Vector interface.
func (b *BinaryVec) AtVec(i int) float64 {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}

	if b.bitIsSet(i) {
		return 1.0
	}
	return 0.0
}

// T performs an implicit transpose by returning the receiver inside a Transpose.
// This method is part of the Gonum mat.Matrix interface
func (b *BinaryVec) T() mat.Matrix {
	return mat.TransposeVec{Vector: b}
}

// NNZ returns the Number of Non-Zero elements (bits).  This is the number of set
// bits (represented by 1s rather than 0s) in the vector.  This is also known as the
// `Hamming weight` or `population count` (popcount).
func (b *BinaryVec) NNZ() int {
	nnz := uint64(0)
	for _, word := range b.data {
		nnz += popcount(word)
	}
	return int(nnz)
}

// Len returns the length of the vector or the total number of elements.  This method
// is part of the Gonum mat.Vector interface
func (b *BinaryVec) Len() int {
	return b.length
}

// BitIsSet tests whether the element (bit) at position i is set (equals 1) and
// returns true if so.  If the element (bit) is not set or has been unset (equal
// to 0) the the method will return false.  The method will panic if i is greater
// than Len().
func (b *BinaryVec) BitIsSet(i int) bool {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	return b.bitIsSet(i)
}

// bitIsSet tests whether the element (bit) at position i is set (equals 1) and
// returns true if so.  If the element (bit) is not set or has been unset (equal
// to 0) the the method will return false.
func (b *BinaryVec) bitIsSet(i int) bool {
	return b.data[i>>log2WordSize]&(1<<(uint(i)&(wordSize-1))) != 0
}

// SetBit sets the bit at the specified index (i) to 1.  If the bit is already set
// there are no adverse effects.  The method will panic if index is larger
// than Len()
func (b *BinaryVec) SetBit(i int) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	b.setBit(i)
}

// setBit sets the bit at the specified index (i) to 1.  If the bit is already set
// there are no adverse effects.
func (b *BinaryVec) setBit(i int) {
	b.data[i>>log2WordSize] |= 1 << (uint(i) & (wordSize - 1))
}

// UnsetBit unsets the bit at the specified index (i) (sets it to 0).  If the bit
// is already unset or has simply never been set (default bit values are 0)
// there are no adverse effects.  The method will panic if index is larger
// than Len()
func (b *BinaryVec) UnsetBit(i int) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	b.unsetBit(i)
}

// unsetBit unsets the bit at the specified index (i) (sets it to 0).  If the bit
// is already unset or has simply never been set (default bit values are 0)
// there are no adverse effects.
func (b *BinaryVec) unsetBit(i int) {
	b.data[i>>log2WordSize] &^= 1 << (uint(i) & (wordSize - 1))
}

// Set sets the element of the matrix located at row i and column j to 1 if v != 0
// or 0 otherwise. Set will panic if specified values for i or j fall outside the
// dimensions of the matrix.
func (b *BinaryVec) Set(i int, j int, v float64) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	if j != 0 {
		panic(mat.ErrColAccess)
	}

	if v != 0 {
		b.setBit(i)
		return
	}
	b.unsetBit(i)
}

// SetVec sets the element of the vector located at row i to 1 if v != 0
// or 0 otherwise. The method will panic if i is greater than Len().
func (b *BinaryVec) SetVec(i int, v float64) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}

	if v != 0 {
		b.setBit(i)
		return
	}
	b.unsetBit(i)
}

// SliceToUint64 returns a new uint64.
// The returned matrix starts at element from of the receiver and extends
// to - from rows. The final row in the resulting matrix is to-1.
// Slice panics with ErrIndexOutOfRange if the slice is outside the capacity
// of the receiver.
func (b *BinaryVec) SliceToUint64(from, to int) uint64 {
	if from < 0 || to <= from || to >= b.length || to-from > 64 {
		panic(mat.ErrIndexOutOfRange)
	}

	var result uint64
	var k uint64
	for i := from; i < to; i++ {
		if b.bitIsSet(i) {
			result |= 1 << k
		}
		k++
	}

	return result
}

// String will output the vector as a string representation of its bits
// This method implements the fmt.Stringer interface.
func (b BinaryVec) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, b.Len()))

	width := b.length % int(wordSize)
	if width == 0 {
		width = 64
	}

	fmt.Fprintf(buf, fmt.Sprintf("%%0%db", width), b.data[len(b.data)-1])
	for i := len(b.data) - 2; i >= 0; i-- {
		fmt.Fprintf(buf, "%064b", b.data[i])
	}

	s := buf.Bytes()
	return *(*string)(unsafe.Pointer(&s))
}

// Format outputs the vector to f and allows the output format
// to be specified.  Supported values of c are `x`, `X`, `b`` and `s`
// to format the bits of the vector as a hex digit or binary digit string.
// `s` (the default format) will output as binary digits.
// Please refer to the fmt package documentation for more information.
// This method implements the fmt.Formatter interface.
func (b BinaryVec) Format(f fmt.State, c rune) {
	var buf bytes.Buffer
	var format string
	var leadFormat string
	switch c {
	case 'x':
		format = ".%x"
		leadFormat = "%x"
	case 'X':
		format = ".%X"
		leadFormat = "%X"
	case 'b':
		f.Write([]byte(b.String()))
		return
	case 's':
		f.Write([]byte(b.String()))
		return
	default:
		panic(fmt.Errorf("sparse: unsupported format verb '%c' for Binary vector", c))
	}
	fmt.Fprintf(&buf, leadFormat, b.data[len(b.data)-1])
	for i := len(b.data) - 2; i >= 0; i-- {
		fmt.Fprintf(&buf, format, b.data[i])
	}
	f.Write(buf.Bytes())
}

// popcount calculates the population count of the vector (also known
// as `Hamming weight`).  This uses fewer arithmetic operations than
// any other known implementation on machines with fast multiplication.
// Thanks to Wikipedia and Hacker's Delight.
func popcount(x uint64) (n uint64) {
	x -= (x >> 1) & 0x5555555555555555
	x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
	x += x >> 4
	x &= 0x0f0f0f0f0f0f0f0f
	x *= 0x0101010101010101
	return x >> 56
}

// Binary is a Binary Matrix or Bit Matrix type.
// Although part of the sparse package, this type is not sparse itself
// and stores all bits even 0s.  However, as it makes use of 64 bit
// integers to store each set of 64 bits and then bitwise operators to
// manipulate the elements it will be more efficient in terms of both
// storage requirements and performance than a slice of bool values
// (8 bits per element) or even a typical Dense matrix of float64
// elements.  A compressed bitmap scheme could be used to take advantage
// of sparseness but may have an associated overhead.
type Binary struct {
	r, c int
	cols []BinaryVec
}

// NewBinary constructs a new Binary matrix or r rows and c columns.
// If vecs is not nil, it will be used as the underlying binary column vectors.
// If vecs is nil, new storage will be allocated.
func NewBinary(r, c int, vecs []BinaryVec) *Binary {
	if vecs == nil {
		vecs = make([]BinaryVec, c)
		for i := 0; i < c; i++ {
			vecs[i] = *NewBinaryVec(r)
		}
	}

	return &Binary{r: r, c: c, cols: vecs}
}

// Dims returns the dimensions of the matrix as the number of rows, columns.
// This method is part of the Gonum mat.Matrix interface
func (b *Binary) Dims() (int, int) {
	return b.r, b.c
}

// At returns the value of the element at row i and column k.
// i (row) and j (col) must be within the dimensions of the matrix otherwise the
// method panics.  This method is part of the Gonum mat.Matrix interface.
func (b *Binary) At(i int, j int) float64 {
	if j < 0 || j >= b.c {
		panic(mat.ErrColAccess)
	}
	return b.cols[j].AtVec(i)
}

// T performs an implicit transpose by returning the receiver inside a Transpose.
// This method is part of the Gonum mat.Matrix interface
func (b *Binary) T() mat.Matrix {
	return mat.Transpose{Matrix: b}
}

// ColView returns the mat.Vector representing the column j.  This vector will
// be a BinaryVec and will share the same storage as the matrix so any changes
// to the vector will be reflected in the matrix and vice versa.
// if j is outside the dimensions of the matrix the method will panic.
func (b *Binary) ColView(j int) mat.Vector {
	if j < 0 || j >= b.c {
		panic(mat.ErrColAccess)
	}
	return &b.cols[j]
}
<commit_msg>corrected bounds check on slice method<commit_after>package sparse

import (
	"bytes"
	"fmt"
	"unsafe"

	"gonum.org/v1/gonum/mat"
)

var (
	_ mat.Matrix = (*BinaryVec)(nil)
	_ mat.Vector = (*BinaryVec)(nil)

	_ mat.Matrix    = (*Binary)(nil)
	_ mat.ColViewer = (*Binary)(nil)
)

const (
	// maxLen is the biggest slice/array len one can create on a 32/64b platform.
	//bitLen = ^uint(0) >> 1
	maxDataLen = ^uint(0)

	// the wordSize of a binary vector.  Bits are stored in slices of words.
	wordSize = uint(64)

	// log2WordSize is the binary logarithm (log base 2) of (wordSize)
	log2WordSize = uint(6)
)

// BinaryVec is a Binary Vector or Bit Vector type.  This is useful
// for representing vectors of features with a binary state (1 or 0).
// Although part of the sparse package, this type is not sparse itself
// and stores all bits even 0s.  However, as it makes use of 64 bit
// integers to store each set of 64 bits and then bitwise operators to
// manipulate the elements it will be more efficient in terms of both
// storage requirements and performance than a slice of bool values
// (8 bits per element) or even a typical Dense matrix of float64
// elements.  A compressed bitmap scheme could be used to take advantage
// of sparseness but may have an associated overhead.
type BinaryVec struct {
	length int
	data   []uint64
}

// NewBinaryVec creates a new BitSet with a hint that length bits will be required
func NewBinaryVec(length int) *BinaryVec {
	maxSize := (maxDataLen - wordSize + uint(1))
	if uint(length) > maxSize {
		panic(fmt.Errorf("sparse: Requested bit length of Binary vector (%d) too large.  %d is the maximum allowed", length, maxSize))
	}
	elements := int((uint(length) + (wordSize - 1)) >> log2WordSize)

	vec := &BinaryVec{
		length: length,
		data:   make([]uint64, elements),
	}

	return vec
}

// DistanceFrom is the number of bits that are different between the
// receiver and rhs i.e.
// 	recevier 	= 1001001
//	rhs 		= 1010101
// 	Distance	= 3
// because there are three bits that are different between the 2
// binary vectors.  This is sometimes referred to as the `Hamming
// distance` or `Matching distance`.  In this case, the distance
// is not normalised and is simply the raw count of differences.  To
// normalise the value simply divide this value by the vector's length.
func (b *BinaryVec) DistanceFrom(rhs *BinaryVec) int {
	differences := uint64(0)
	for i, word := range b.data {
		differences += popcount(word ^ rhs.data[i])
	}
	return int(differences)
}

// Dims returns the dimensions of the matrix as the number of rows, columns.
// As this is a vector, the second value representing the number of columns
// will be 1. This method is part of the Gonum mat.Matrix interface
func (b *BinaryVec) Dims() (int, int) {
	return b.Len(), 1
}

// At returns the value of the element at row i and column j.
// As this is a vector (only one column), j must be 0 otherwise the
// method panics.  This method is part of the Gonum mat.Matrix interface.
func (b *BinaryVec) At(i, j int) float64 {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	if j != 0 {
		panic(mat.ErrColAccess)
	}

	if b.bitIsSet(i) {
		return 1.0
	}
	return 0.0
}

// AtVec returns the value of the element at row i.  This method will panic if
// i > Len().  This method is part of the Gonum mat.Vector interface.
func (b *BinaryVec) AtVec(i int) float64 {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}

	if b.bitIsSet(i) {
		return 1.0
	}
	return 0.0
}

// T performs an implicit transpose by returning the receiver inside a Transpose.
// This method is part of the Gonum mat.Matrix interface
func (b *BinaryVec) T() mat.Matrix {
	return mat.TransposeVec{Vector: b}
}

// NNZ returns the Number of Non-Zero elements (bits).  This is the number of set
// bits (represented by 1s rather than 0s) in the vector.  This is also known as the
// `Hamming weight` or `population count` (popcount).
func (b *BinaryVec) NNZ() int {
	nnz := uint64(0)
	for _, word := range b.data {
		nnz += popcount(word)
	}
	return int(nnz)
}

// Len returns the length of the vector or the total number of elements.  This method
// is part of the Gonum mat.Vector interface
func (b *BinaryVec) Len() int {
	return b.length
}

// BitIsSet tests whether the element (bit) at position i is set (equals 1) and
// returns true if so.  If the element (bit) is not set or has been unset (equal
// to 0) the the method will return false.  The method will panic if i is greater
// than Len().
func (b *BinaryVec) BitIsSet(i int) bool {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	return b.bitIsSet(i)
}

// bitIsSet tests whether the element (bit) at position i is set (equals 1) and
// returns true if so.  If the element (bit) is not set or has been unset (equal
// to 0) the the method will return false.
func (b *BinaryVec) bitIsSet(i int) bool {
	return b.data[i>>log2WordSize]&(1<<(uint(i)&(wordSize-1))) != 0
}

// SetBit sets the bit at the specified index (i) to 1.  If the bit is already set
// there are no adverse effects.  The method will panic if index is larger
// than Len()
func (b *BinaryVec) SetBit(i int) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	b.setBit(i)
}

// setBit sets the bit at the specified index (i) to 1.  If the bit is already set
// there are no adverse effects.
func (b *BinaryVec) setBit(i int) {
	b.data[i>>log2WordSize] |= 1 << (uint(i) & (wordSize - 1))
}

// UnsetBit unsets the bit at the specified index (i) (sets it to 0).  If the bit
// is already unset or has simply never been set (default bit values are 0)
// there are no adverse effects.  The method will panic if index is larger
// than Len()
func (b *BinaryVec) UnsetBit(i int) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	b.unsetBit(i)
}

// unsetBit unsets the bit at the specified index (i) (sets it to 0).  If the bit
// is already unset or has simply never been set (default bit values are 0)
// there are no adverse effects.
func (b *BinaryVec) unsetBit(i int) {
	b.data[i>>log2WordSize] &^= 1 << (uint(i) & (wordSize - 1))
}

// Set sets the element of the matrix located at row i and column j to 1 if v != 0
// or 0 otherwise. Set will panic if specified values for i or j fall outside the
// dimensions of the matrix.
func (b *BinaryVec) Set(i int, j int, v float64) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}
	if j != 0 {
		panic(mat.ErrColAccess)
	}

	if v != 0 {
		b.setBit(i)
		return
	}
	b.unsetBit(i)
}

// SetVec sets the element of the vector located at row i to 1 if v != 0
// or 0 otherwise. The method will panic if i is greater than Len().
func (b *BinaryVec) SetVec(i int, v float64) {
	if i < 0 || i >= b.length {
		panic(mat.ErrRowAccess)
	}

	if v != 0 {
		b.setBit(i)
		return
	}
	b.unsetBit(i)
}

// SliceToUint64 returns a new uint64.
// The returned matrix starts at element from of the receiver and extends
// to - from rows. The final row in the resulting matrix is to-1.
// Slice panics with ErrIndexOutOfRange if the slice is outside the capacity
// of the receiver.
func (b *BinaryVec) SliceToUint64(from, to int) uint64 {
	if from < 0 || to <= from || to > b.length || to-from > 64 {
		panic(mat.ErrIndexOutOfRange)
	}

	var result uint64
	var k uint64
	for i := from; i < to; i++ {
		if b.bitIsSet(i) {
			result |= 1 << k
		}
		k++
	}

	return result
}

// String will output the vector as a string representation of its bits
// This method implements the fmt.Stringer interface.
func (b BinaryVec) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, b.Len()))

	width := b.length % int(wordSize)
	if width == 0 {
		width = 64
	}

	fmt.Fprintf(buf, fmt.Sprintf("%%0%db", width), b.data[len(b.data)-1])
	for i := len(b.data) - 2; i >= 0; i-- {
		fmt.Fprintf(buf, "%064b", b.data[i])
	}

	s := buf.Bytes()
	return *(*string)(unsafe.Pointer(&s))
}

// Format outputs the vector to f and allows the output format
// to be specified.  Supported values of c are `x`, `X`, `b`` and `s`
// to format the bits of the vector as a hex digit or binary digit string.
// `s` (the default format) will output as binary digits.
// Please refer to the fmt package documentation for more information.
// This method implements the fmt.Formatter interface.
func (b BinaryVec) Format(f fmt.State, c rune) {
	var buf bytes.Buffer
	var format string
	var leadFormat string
	switch c {
	case 'x':
		format = ".%x"
		leadFormat = "%x"
	case 'X':
		format = ".%X"
		leadFormat = "%X"
	case 'b':
		f.Write([]byte(b.String()))
		return
	case 's':
		f.Write([]byte(b.String()))
		return
	default:
		panic(fmt.Errorf("sparse: unsupported format verb '%c' for Binary vector", c))
	}
	fmt.Fprintf(&buf, leadFormat, b.data[len(b.data)-1])
	for i := len(b.data) - 2; i >= 0; i-- {
		fmt.Fprintf(&buf, format, b.data[i])
	}
	f.Write(buf.Bytes())
}

// popcount calculates the population count of the vector (also known
// as `Hamming weight`).  This uses fewer arithmetic operations than
// any other known implementation on machines with fast multiplication.
// Thanks to Wikipedia and Hacker's Delight.
func popcount(x uint64) (n uint64) {
	x -= (x >> 1) & 0x5555555555555555
	x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
	x += x >> 4
	x &= 0x0f0f0f0f0f0f0f0f
	x *= 0x0101010101010101
	return x >> 56
}

// Binary is a Binary Matrix or Bit Matrix type.
// Although part of the sparse package, this type is not sparse itself
// and stores all bits even 0s.  However, as it makes use of 64 bit
// integers to store each set of 64 bits and then bitwise operators to
// manipulate the elements it will be more efficient in terms of both
// storage requirements and performance than a slice of bool values
// (8 bits per element) or even a typical Dense matrix of float64
// elements.  A compressed bitmap scheme could be used to take advantage
// of sparseness but may have an associated overhead.
type Binary struct {
	r, c int
	cols []BinaryVec
}

// NewBinary constructs a new Binary matrix or r rows and c columns.
// If vecs is not nil, it will be used as the underlying binary column vectors.
// If vecs is nil, new storage will be allocated.
func NewBinary(r, c int, vecs []BinaryVec) *Binary {
	if vecs == nil {
		vecs = make([]BinaryVec, c)
		for i := 0; i < c; i++ {
			vecs[i] = *NewBinaryVec(r)
		}
	}

	return &Binary{r: r, c: c, cols: vecs}
}

// Dims returns the dimensions of the matrix as the number of rows, columns.
// This method is part of the Gonum mat.Matrix interface
func (b *Binary) Dims() (int, int) {
	return b.r, b.c
}

// At returns the value of the element at row i and column k.
// i (row) and j (col) must be within the dimensions of the matrix otherwise the
// method panics.  This method is part of the Gonum mat.Matrix interface.
func (b *Binary) At(i int, j int) float64 {
	if j < 0 || j >= b.c {
		panic(mat.ErrColAccess)
	}
	return b.cols[j].AtVec(i)
}

// T performs an implicit transpose by returning the receiver inside a Transpose.
// This method is part of the Gonum mat.Matrix interface
func (b *Binary) T() mat.Matrix {
	return mat.Transpose{Matrix: b}
}

// ColView returns the mat.Vector representing the column j.  This vector will
// be a BinaryVec and will share the same storage as the matrix so any changes
// to the vector will be reflected in the matrix and vice versa.
// if j is outside the dimensions of the matrix the method will panic.
func (b *Binary) ColView(j int) mat.Vector {
	if j < 0 || j >= b.c {
		panic(mat.ErrColAccess)
	}
	return &b.cols[j]
}
<|endoftext|>


<commit_before>package greyhound

import (
	"bufio"
	"errors"
	"fmt"
	"html/template"
	"io"
	"math"
	"net/http"
	"os/exec"
	"sync"
	"time"
)

type PhpProcess struct {
	dir string
	port int
	host string
	cmd *exec.Cmd
	stderr *bufio.Reader
	phpErrors chan string
	mutex *sync.Mutex
}

func NewPhpProcess(dir string) (ph *PhpProcess, err error) {
	for p := 8001; p < int(math.Pow(2, 16)); p++ {
		ph = &PhpProcess{
			dir: dir,
			// Use 127.0.0.1 here instead of localhost
			// otherwise PHP only listens on ::1
			host: fmt.Sprintf("127.0.0.1:%d", p),
		}
		cmd, stderr, err := runPhp(ph.dir, ph.host)

		if err == nil {
			ph.cmd = cmd
			ph.stderr = bufio.NewReader(stderr)
			ph.phpErrors = make(chan string)
			ph.mutex = &sync.Mutex{}
			go ph.listenForErrors()
			return ph, nil
		}
	}
	return nil, errors.New("No free ports found")
}

func (ph *PhpProcess) Close() {
	err := ph.cmd.Process.Kill()
	if err != nil {
		panic(err)
	}
}

func (ph *PhpProcess) MakeRequest(w http.ResponseWriter, r *http.Request) (err error) {
	ph.mutex.Lock()
	defer ph.mutex.Unlock()

	r.URL.Scheme = "http"
	r.URL.Host = ph.host

	// Make the request
	tr := &http.Transport{}

	resp, err := tr.RoundTrip(r)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	// Check for errors
	thereWereErrors := false
	// Experimentally, 1ms works, 100mcs doesn't work
	c := time.After(time.Millisecond)

	FOR: for {
		select {
		case <-c:
			break FOR
		case line := <-ph.phpErrors:
			renderError(w, line)
			thereWereErrors = true
		}
	}

	if thereWereErrors {
		return
	}

	// Headers
	headers := w.Header()
	for k, v := range resp.Header {
		headers[k] = v
	}

	// Status code
	w.WriteHeader(resp.StatusCode)

	// Body
	bufWriter := bufio.NewWriter(w)
	bufWriter.ReadFrom(resp.Body)
	bufWriter.Flush()

	return
}

func (ph *PhpProcess) listenForErrors() {
	for {
		line, err := ph.stderr.ReadString('\n')
		if err != nil {
			if err.Error() == "EOF" {
				return
			} else {
				panic(err)
			}
		}
		if line[25:37] != "] 127.0.0.1:" {
			ph.phpErrors <- line[40:]
		}
	}
}

func renderError(w http.ResponseWriter, s string) {
	w.WriteHeader(http.StatusInternalServerError)

	tmpl := template.Must(template.New("").Parse(`<pre>{{.}}</pre>`))
	tmpl.Execute(w, s)
}

// A low-level command
// Starts PHP running, waits one second, returns an error if PHP stopped during that time
func runPhp(dir string, host string) (cmd *exec.Cmd, stderr io.ReadCloser, err error) {
	cmd = exec.Command(
		"php",
		"-n", // do not read php.ini
		"-S", host,
		"-t", dir,
		"-d", "display_errors=Off",
		"-d", "log_errors=On",
		"-d", "error_reporting=E_ALL",
	)

	// Connect stderr
	stderr, err = cmd.StderrPipe()
	if err != nil {
		return
	}

	// Let's go
	err = cmd.Start()
	if err != nil {
		return
	}

	// Wait 1 second for the command to terminate
	// If it exits early, that's bad whatever the exit status
	e := make(chan error)

	go func() {
		err := cmd.Wait()
		if err != nil {
			e <- err
		} else {
			e <- errors.New("Command exited early")
		}
	}()

	select {
		// Experimentally, 100ms works and 10ms doesn't
	case <-time.After(time.Millisecond*100):
		return
	case err = <-e:
		return
	}
}
<commit_msg>Replace a <-time.After() with something better<commit_after>package greyhound

import (
	"bufio"
	"errors"
	"fmt"
	"html/template"
	"io"
	"math"
	"net/http"
	"os/exec"
	"sync"
	"time"
)

type PhpProcess struct {
	dir string
	port int
	host string
	cmd *exec.Cmd
	stderr *bufio.Reader
	errorLog chan string
	requestLog chan string
	mutex *sync.Mutex
}

func NewPhpProcess(dir string) (ph *PhpProcess, err error) {
	for p := 8001; p < int(math.Pow(2, 16)); p++ {
		ph = &PhpProcess{
			dir: dir,
			// Use 127.0.0.1 here instead of localhost
			// otherwise PHP only listens on ::1
			host: fmt.Sprintf("127.0.0.1:%d", p),
		}
		cmd, stderr, err := runPhp(ph.dir, ph.host)

		if err == nil {
			ph.cmd = cmd
			ph.stderr = bufio.NewReader(stderr)
			ph.errorLog = make(chan string)
			ph.requestLog = make(chan string)
			ph.mutex = &sync.Mutex{}
			go ph.listenForErrors()
			return ph, nil
		}
	}
	return nil, errors.New("No free ports found")
}

func (ph *PhpProcess) Close() {
	err := ph.cmd.Process.Kill()
	if err != nil {
		panic(err)
	}
}

func (ph *PhpProcess) MakeRequest(w http.ResponseWriter, r *http.Request) (err error) {
	ph.mutex.Lock()
	defer ph.mutex.Unlock()

	r.URL.Scheme = "http"
	r.URL.Host = ph.host

	// Make the request
	tr := &http.Transport{}

	resp, err := tr.RoundTrip(r)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	// Check for errors
	thereWereErrors := false

	// The request gets printed to STDERR only after the errors
	// So it's a reliable way to confirm that the page was returned

	FOR: for {
		select {
		case <-ph.requestLog:
			break FOR
		case line := <-ph.errorLog:
			renderError(w, line)
			thereWereErrors = true
		}
	}

	if thereWereErrors {
		return
	}

	// Headers
	headers := w.Header()
	for k, v := range resp.Header {
		headers[k] = v
	}

	// Status code
	w.WriteHeader(resp.StatusCode)

	// Body
	bufWriter := bufio.NewWriter(w)
	bufWriter.ReadFrom(resp.Body)
	bufWriter.Flush()

	return
}

func (ph *PhpProcess) listenForErrors() {
	for {
		line, err := ph.stderr.ReadString('\n')
		if err != nil {
			if err.Error() == "EOF" {
				return
			} else {
				panic(err)
			}
		}

		if line[25:37] != "] 127.0.0.1:" {
			ph.errorLog <- line[40:]
		} else {
			ph.requestLog <- line[38:]
		}
	}
}

func renderError(w http.ResponseWriter, s string) {
	w.WriteHeader(http.StatusInternalServerError)

	tmpl := template.Must(template.New("").Parse(`<pre>{{.}}</pre>`))
	tmpl.Execute(w, s)
}

// A low-level command
// Starts PHP running, waits one second, returns an error if PHP stopped during that time
func runPhp(dir string, host string) (cmd *exec.Cmd, stderr io.ReadCloser, err error) {
	cmd = exec.Command(
		"php",
		"-n", // do not read php.ini
		"-S", host,
		"-t", dir,
		"-d", "display_errors=Off",
		"-d", "log_errors=On",
		"-d", "error_reporting=E_ALL",
	)

	// Connect stderr
	stderr, err = cmd.StderrPipe()
	if err != nil {
		return
	}

	// Let's go
	err = cmd.Start()
	if err != nil {
		return
	}

	// Wait 1 second for the command to terminate
	// If it exits early, that's bad whatever the exit status
	e := make(chan error)

	go func() {
		err := cmd.Wait()
		if err != nil {
			e <- err
		} else {
			e <- errors.New("Command exited early")
		}
	}()

	select {
		// Experimentally, 100ms works and 10ms doesn't
	case <-time.After(time.Millisecond*100):
		return
	case err = <-e:
		return
	}
}
<|endoftext|>


<commit_before>package echo

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/webx-top/webx/lib/tagfast"
)

var DefaultHtmlFilter = func(v string) (r string) {
	return v
}

const (
	defaultMaxMemory = 32 << 20 // 32 MB
)

type (
	// Binder is the interface that wraps the Bind method.
	Binder interface {
		Bind(*http.Request, interface{}) error
	}
	binder struct {
		*Echo
		maxMemory int64
	}
)

// SetMaxBodySize sets multipart forms max body size
func (b *binder) SetMaxMemory(size int64) {
	b.maxMemory = size
}

// MaxBodySize return multipart forms max body size
func (b *binder) MaxMemory() int64 {
	return b.maxMemory
}

func (b binder) Bind(r *http.Request, i interface{}) (err error) {
	if r.Body == nil {
		err = NewHTTPError(http.StatusBadRequest, "Request body can't be nil")
		return
	}
	defer r.Body.Close()
	ct := r.Header.Get(ContentType)
	err = UnsupportedMediaType
	if strings.HasPrefix(ct, ApplicationJSON) {
		err = json.NewDecoder(r.Body).Decode(i)
	} else if strings.HasPrefix(ct, ApplicationXML) {
		err = xml.NewDecoder(r.Body).Decode(i)
	} else if strings.HasPrefix(ct, ApplicationForm) {
		if r.Form == nil {
			r.ParseForm()
		}
		err = b.structMap(i, r)
	} else if strings.Contains(ct, MultipartForm) {
		if r.Form == nil {
			if b.maxMemory == 0 {
				b.maxMemory = defaultMaxMemory
			}
			r.ParseMultipartForm(b.maxMemory)
			if len(r.PostForm) == 0 {
				r.PostForm = r.MultipartForm.Value
			}
		}
		err = b.structMap(i, r)
	}
	return
}

// StructMap function mapping params to controller's properties
func (b binder) structMap(m interface{}, r *http.Request) error {
	return NamedStructMap(b.Echo, m, r, "")
}

// user[name][test]
func SplitJson(s string) ([]string, error) {
	res := make([]string, 0)
	var begin, end int
	var isleft bool
	for i, r := range s {
		switch r {
		case '[':
			isleft = true
			if i > 0 && s[i-1] != ']' {
				if begin == end {
					return nil, errors.New("unknow character")
				}
				res = append(res, s[begin:end+1])
			}
			begin = i + 1
			end = begin
		case ']':
			if !isleft {
				return nil, errors.New("unknow character")
			}
			isleft = false
			if begin != end {
				//return nil, errors.New("unknow character")

				res = append(res, s[begin:end+1])
				begin = i + 1
				end = begin
			}
		default:
			end = i
		}
		if i == len(s)-1 && begin != end {
			res = append(res, s[begin:end+1])
		}
	}
	return res, nil
}

func NamedStructMap(e *Echo, m interface{}, r *http.Request, topName string) error {
	vc := reflect.ValueOf(m)
	tc := reflect.TypeOf(m)

	switch tc.Kind() {
	case reflect.Struct:
	case reflect.Ptr:
		vc = vc.Elem()
		tc = tc.Elem()
	}

	for k, t := range r.Form {

		if k == "" || k[0] == '_' {
			continue
		}

		if topName != "" {
			if !strings.HasPrefix(k, topName) {
				continue
			}
			k = k[len(topName)+1:]
		}

		v := t[0]
		names := strings.Split(k, ".")
		var err error
		length := len(names)
		if length == 1 {
			names, err = SplitJson(k)
			if err != nil {
				e.Logger().Warn("Unrecognize form key %v %v", k, err)
				continue
			}
		}
		length = len(names)
		var value reflect.Value = vc
		for i, name := range names {
			name = strings.Title(name)

			//不是最后一个元素
			if i != length-1 {
				if value.Kind() != reflect.Struct {
					e.Logger().Warn("arg error, value kind is %v", value.Kind())
					break
				}
				value = value.FieldByName(name)
				if !value.IsValid() {
					e.Logger().Warn("(%v value is not valid %v)", name, value)
					break
				}
				if !value.CanSet() {
					e.Logger().Warn("can not set %v -> %v", name, value.Interface())
					break
				}
				f, _ := value.Type().FieldByName(name)
				if tagfast.Value(tc, f, "form_options") == "-" {
					continue
				}
				if value.Kind() == reflect.Ptr {
					if value.IsNil() {
						value.Set(reflect.New(value.Type().Elem()))
					}
					value = value.Elem()
				}
			} else {
				if value.Kind() != reflect.Struct {
					e.Logger().Warn("arg error, value %v kind is %v", name, value.Kind())
					break
				}
				tv := value.FieldByName(name)
				if !tv.IsValid() {
					break
				}
				if !tv.CanSet() {
					e.Logger().Warn("can not set %v to %v", k, tv)
					break
				}
				f, _ := tv.Type().FieldByName(name)
				if tagfast.Value(tc, f, "form_options") == "-" {
					continue
				}
				if tv.Kind() == reflect.Ptr {
					tv.Set(reflect.New(tv.Type().Elem()))
					tv = tv.Elem()
				}

				var l interface{}
				switch k := tv.Kind(); k {
				case reflect.String:
					switch tagfast.Value(tc, f, "form_filter") {
					case "html":
						v = DefaultHtmlFilter(v)
					}
					l = v
					tv.Set(reflect.ValueOf(l))
				case reflect.Bool:
					l = (v != "false" && v != "0")
					tv.Set(reflect.ValueOf(l))
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
					x, err := strconv.Atoi(v)
					if err != nil {
						e.Logger().Warn("arg %v as int: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Int64:
					x, err := strconv.ParseInt(v, 10, 64)
					if err != nil {
						e.Logger().Warn("arg %v as int64: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Float32, reflect.Float64:
					x, err := strconv.ParseFloat(v, 64)
					if err != nil {
						e.Logger().Warn("arg %v as float64: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
					x, err := strconv.ParseUint(v, 10, 64)
					if err != nil {
						e.Logger().Warn("arg %v as uint: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Struct:
					if tvf, ok := tv.Interface().(FromConversion); ok {
						err := tvf.FromString(v)
						if err != nil {
							e.Logger().Warn("struct %v invoke FromString faild", tvf)
						}
					} else if tv.Type().String() == "time.Time" {
						x, err := time.Parse("2006-01-02 15:04:05.000 -0700", v)
						if err != nil {
							x, err = time.Parse("2006-01-02 15:04:05", v)
							if err != nil {
								x, err = time.Parse("2006-01-02", v)
								if err != nil {
									e.Logger().Warn("unsupported time format %v, %v", v, err)
									break
								}
							}
						}
						l = x
						tv.Set(reflect.ValueOf(l))
					} else {
						e.Logger().Warn("can not set an struct which is not implement Fromconversion interface")
					}
				case reflect.Ptr:
					e.Logger().Warn("can not set an ptr of ptr")
				case reflect.Slice, reflect.Array:
					tt := tv.Type().Elem()
					tk := tt.Kind()

					if tv.IsNil() {
						tv.Set(reflect.MakeSlice(tv.Type(), len(t), len(t)))
					}

					for i, s := range t {
						var err error
						switch tk {
						case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int8, reflect.Int64:
							var v int64
							v, err = strconv.ParseInt(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetInt(v)
							}
						case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
							var v uint64
							v, err = strconv.ParseUint(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetUint(v)
							}
						case reflect.Float32, reflect.Float64:
							var v float64
							v, err = strconv.ParseFloat(s, tt.Bits())
							if err == nil {
								tv.Index(i).SetFloat(v)
							}
						case reflect.Bool:
							var v bool
							v, err = strconv.ParseBool(s)
							if err == nil {
								tv.Index(i).SetBool(v)
							}
						case reflect.String:
							tv.Index(i).SetString(s)
						case reflect.Complex64, reflect.Complex128:
							// TODO:
							err = fmt.Errorf("unsupported slice element type %v", tk.String())
						default:
							err = fmt.Errorf("unsupported slice element type %v", tk.String())
						}
						if err != nil {
							e.Logger().Warn("slice error: %v, %v", name, err)
							break
						}
					}
				default:
					break
				}
			}
		}
	}
	return nil
}

// a struct implements this interface can be convert from request param to a struct
type FromConversion interface {
	FromString(content string) error
}

// a struct implements this interface can be convert from struct to template variable
// Not Implemented
type ToConversion interface {
	ToString() string
}
<commit_msg>update<commit_after>package echo

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/webx-top/webx/lib/tagfast"
)

var DefaultHtmlFilter = func(v string) (r string) {
	return v
}

const (
	defaultMaxMemory = 32 << 20 // 32 MB
)

type (
	// Binder is the interface that wraps the Bind method.
	Binder interface {
		Bind(*http.Request, interface{}) error
	}
	binder struct {
		*Echo
		maxMemory int64
	}
)

// SetMaxBodySize sets multipart forms max body size
func (b *binder) SetMaxMemory(size int64) {
	b.maxMemory = size
}

// MaxBodySize return multipart forms max body size
func (b *binder) MaxMemory() int64 {
	return b.maxMemory
}

func (b binder) Bind(r *http.Request, i interface{}) (err error) {
	if r.Body == nil {
		err = NewHTTPError(http.StatusBadRequest, "Request body can't be nil")
		return
	}
	defer r.Body.Close()
	ct := r.Header.Get(ContentType)
	err = UnsupportedMediaType
	if strings.HasPrefix(ct, ApplicationJSON) {
		err = json.NewDecoder(r.Body).Decode(i)
	} else if strings.HasPrefix(ct, ApplicationXML) {
		err = xml.NewDecoder(r.Body).Decode(i)
	} else if strings.HasPrefix(ct, ApplicationForm) {
		if r.Form == nil {
			r.ParseForm()
		}
		err = b.structMap(i, r)
	} else if strings.Contains(ct, MultipartForm) {
		if r.Form == nil {
			if b.maxMemory == 0 {
				b.maxMemory = defaultMaxMemory
			}
			r.ParseMultipartForm(b.maxMemory)
			if len(r.PostForm) == 0 {
				r.PostForm = r.MultipartForm.Value
			}
		}
		err = b.structMap(i, r)
	}
	return
}

// StructMap function mapping params to controller's properties
func (b binder) structMap(m interface{}, r *http.Request) error {
	return NamedStructMap(b.Echo, m, r, "")
}

// user[name][test]
func SplitJson(s string) ([]string, error) {
	res := make([]string, 0)
	var begin, end int
	var isleft bool
	for i, r := range s {
		switch r {
		case '[':
			isleft = true
			if i > 0 && s[i-1] != ']' {
				if begin == end {
					return nil, errors.New("unknow character")
				}
				res = append(res, s[begin:end+1])
			}
			begin = i + 1
			end = begin
		case ']':
			if !isleft {
				return nil, errors.New("unknow character")
			}
			isleft = false
			if begin != end {
				//return nil, errors.New("unknow character")

				res = append(res, s[begin:end+1])
				begin = i + 1
				end = begin
			}
		default:
			end = i
		}
		if i == len(s)-1 && begin != end {
			res = append(res, s[begin:end+1])
		}
	}
	return res, nil
}

func NamedStructMap(e *Echo, m interface{}, r *http.Request, topName string) error {
	vc := reflect.ValueOf(m)
	tc := reflect.TypeOf(m)

	switch tc.Kind() {
	case reflect.Struct:
	case reflect.Ptr:
		vc = vc.Elem()
		tc = tc.Elem()
	}

	for k, t := range r.Form {

		if k == "" || k[0] == '_' {
			continue
		}

		if topName != "" {
			if !strings.HasPrefix(k, topName) {
				continue
			}
			k = k[len(topName)+1:]
		}

		v := t[0]
		names := strings.Split(k, ".")
		var err error
		length := len(names)
		if length == 1 {
			names, err = SplitJson(k)
			if err != nil {
				e.Logger().Warn("Unrecognize form key %v %v", k, err)
				continue
			}
		}
		length = len(names)
		var value reflect.Value = vc
		var typev reflect.Type = tc
		for i, name := range names {
			name = strings.Title(name)

			//不是最后一个元素
			if i != length-1 {
				if value.Kind() != reflect.Struct {
					e.Logger().Warn("arg error, value kind is %v", value.Kind())
					break
				}
				value = value.FieldByName(name)
				if !value.IsValid() {
					e.Logger().Warn("(%v value is not valid %v)", name, value)
					break
				}
				if !value.CanSet() {
					e.Logger().Warn("can not set %v -> %v", name, value.Interface())
					break
				}
				typev = value.Type()
				f, _ := typev.FieldByName(name)
				if tagfast.Value(tc, f, "form_options") == "-" {
					continue
				}
				if value.Kind() == reflect.Ptr {
					if value.IsNil() {
						value.Set(reflect.New(value.Type().Elem()))
					}
					value = value.Elem()
				}
			} else {
				if value.Kind() != reflect.Struct {
					e.Logger().Warn("arg error, value %v kind is %v", name, value.Kind())
					break
				}
				tv := value.FieldByName(name)
				if !tv.IsValid() {
					break
				}
				if !tv.CanSet() {
					e.Logger().Warn("can not set %v to %v", k, tv)
					break
				}
				f, _ := typev.FieldByName(name)
				if tagfast.Value(tc, f, "form_options") == "-" {
					continue
				}
				if tv.Kind() == reflect.Ptr {
					tv.Set(reflect.New(tv.Type().Elem()))
					tv = tv.Elem()
				}

				var l interface{}
				switch k := tv.Kind(); k {
				case reflect.String:
					switch tagfast.Value(tc, f, "form_filter") {
					case "html":
						v = DefaultHtmlFilter(v)
					}
					l = v
					tv.Set(reflect.ValueOf(l))
				case reflect.Bool:
					l = (v != "false" && v != "0")
					tv.Set(reflect.ValueOf(l))
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
					x, err := strconv.Atoi(v)
					if err != nil {
						e.Logger().Warn("arg %v as int: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Int64:
					x, err := strconv.ParseInt(v, 10, 64)
					if err != nil {
						e.Logger().Warn("arg %v as int64: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Float32, reflect.Float64:
					x, err := strconv.ParseFloat(v, 64)
					if err != nil {
						e.Logger().Warn("arg %v as float64: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
					x, err := strconv.ParseUint(v, 10, 64)
					if err != nil {
						e.Logger().Warn("arg %v as uint: %v", v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Struct:
					if tvf, ok := tv.Interface().(FromConversion); ok {
						err := tvf.FromString(v)
						if err != nil {
							e.Logger().Warn("struct %v invoke FromString faild", tvf)
						}
					} else if tv.Type().String() == "time.Time" {
						x, err := time.Parse("2006-01-02 15:04:05.000 -0700", v)
						if err != nil {
							x, err = time.Parse("2006-01-02 15:04:05", v)
							if err != nil {
								x, err = time.Parse("2006-01-02", v)
								if err != nil {
									e.Logger().Warn("unsupported time format %v, %v", v, err)
									break
								}
							}
						}
						l = x
						tv.Set(reflect.ValueOf(l))
					} else {
						e.Logger().Warn("can not set an struct which is not implement Fromconversion interface")
					}
				case reflect.Ptr:
					e.Logger().Warn("can not set an ptr of ptr")
				case reflect.Slice, reflect.Array:
					tt := tv.Type().Elem()
					tk := tt.Kind()

					if tv.IsNil() {
						tv.Set(reflect.MakeSlice(tv.Type(), len(t), len(t)))
					}

					for i, s := range t {
						var err error
						switch tk {
						case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int8, reflect.Int64:
							var v int64
							v, err = strconv.ParseInt(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetInt(v)
							}
						case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
							var v uint64
							v, err = strconv.ParseUint(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetUint(v)
							}
						case reflect.Float32, reflect.Float64:
							var v float64
							v, err = strconv.ParseFloat(s, tt.Bits())
							if err == nil {
								tv.Index(i).SetFloat(v)
							}
						case reflect.Bool:
							var v bool
							v, err = strconv.ParseBool(s)
							if err == nil {
								tv.Index(i).SetBool(v)
							}
						case reflect.String:
							tv.Index(i).SetString(s)
						case reflect.Complex64, reflect.Complex128:
							// TODO:
							err = fmt.Errorf("unsupported slice element type %v", tk.String())
						default:
							err = fmt.Errorf("unsupported slice element type %v", tk.String())
						}
						if err != nil {
							e.Logger().Warn("slice error: %v, %v", name, err)
							break
						}
					}
				default:
					break
				}
			}
		}
	}
	return nil
}

// a struct implements this interface can be convert from request param to a struct
type FromConversion interface {
	FromString(content string) error
}

// a struct implements this interface can be convert from struct to template variable
// Not Implemented
type ToConversion interface {
	ToString() string
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"github.com/voxelbrain/goptions"
)

var (
	options = struct {
		Server string        `goptions:"-s, --server, description='Pixelpixel server to push to'"`
		Help   goptions.Help `goptions:"-h, --help, description='Show this help'"`
		goptions.Verbs
		Upload struct{} `goptions:"upload"`
		Logs   struct{} `goptions:"logs"`
		Format struct{} `goptions:"format"`
	}{
		Server: "localhost:8080",
	}
)

const (
	keyFile = ".key"
)

func main() {
	fs := goptions.NewFlagSet("picli", &options)
	err := fs.Parse(os.Args[1:])
	key := prepareKey()

	switch options.Verbs {
	case "upload":
		options.Server = validateServer(options.Server)
		upload(key)
	case "logs":
		options.Server = validateServer(options.Server)
		logs(key)
	case "format":
		format()
	default:
		if err != nil {
			fmt.Printf("Error: %s\n", err)
		}
		fs.PrintHelp(os.Stderr)
		return
	}
}

func makeApiCall(method, subpath string, body io.Reader) (int, string, error) {
	url := path.Join(options.Server, "pixels") + "/"
	url = slashify(url + subpath)
	req, _ := http.NewRequest(method, url, body)
	resp, err := http.DefaultClient.Do(req)
	buf := &bytes.Buffer{}

	code := -1
	if resp != nil {
		defer resp.Body.Close()
		code = resp.StatusCode
		io.Copy(buf, resp.Body)
	}

	// TODO: Sadly, this seems to be necassary. Can't seem to get it
	// working without it. I always get `malformed HTTP response ""`
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()

	return code, buf.String(), err
}

func prepareKey() string {
	f, err := os.Open(keyFile)
	if err != nil {
		return ""
	}
	defer f.Close()
	key, err := ioutil.ReadAll(f)
	if err != nil {
		return ""
	}
	skey := strings.TrimSpace(string(key))
	code, _, err := makeApiCall("GET", "/"+skey, nil)
	if err == nil && code == http.StatusOK {
		return skey
	}
	return ""
}

func slashify(url string) string {
	url = path.Clean(url)
	if !strings.HasSuffix(url, "/") {
		url = url + "/"
	}
	return "http://" + strings.TrimPrefix(url, "/")
}

const (
	DEFAULT_REMOTE_SERVER = "pixelpixel.haxigon.com"
)

func validateServer(server string) string {
	t := time.AfterFunc(3*time.Second, func() {
		log.Fatalf("Attempt to connect to server timed out")
	})
	defer func() {
		t.Stop()
	}()

	resp, err := http.Get("http://" + path.Join(server, "handshake"))
	if err == nil && readAll(resp.Body) == "PIXELPIXEL OK" {
		return server
	}

	resp, err = http.Get("http://" + path.Join(DEFAULT_REMOTE_SERVER, "handshake"))
	if err == nil && readAll(resp.Body) == "PIXELPIXEL OK" {
		return DEFAULT_REMOTE_SERVER
	}
	log.Fatalf("Could not connect to server")
	return ""
}

func readAll(r io.Reader) string {
	data, _ := ioutil.ReadAll(r)
	return string(data)
}
<commit_msg>picli: Format on upload<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"github.com/voxelbrain/goptions"
)

var (
	options = struct {
		Server string        `goptions:"-s, --server, description='Pixelpixel server to push to'"`
		Help   goptions.Help `goptions:"-h, --help, description='Show this help'"`
		goptions.Verbs
		Upload struct{} `goptions:"upload"`
		Logs   struct{} `goptions:"logs"`
		Format struct{} `goptions:"format"`
	}{
		Server: "localhost:8080",
	}
)

const (
	keyFile = ".key"
)

func main() {
	fs := goptions.NewFlagSet("picli", &options)
	err := fs.Parse(os.Args[1:])
	key := prepareKey()

	switch options.Verbs {
	case "upload":
		options.Server = validateServer(options.Server)
		format()
		upload(key)
	case "logs":
		options.Server = validateServer(options.Server)
		logs(key)
	case "format":
		format()
	default:
		if err != nil {
			fmt.Printf("Error: %s\n", err)
		}
		fs.PrintHelp(os.Stderr)
		return
	}
}

func makeApiCall(method, subpath string, body io.Reader) (int, string, error) {
	url := path.Join(options.Server, "pixels") + "/"
	url = slashify(url + subpath)
	req, _ := http.NewRequest(method, url, body)
	resp, err := http.DefaultClient.Do(req)
	buf := &bytes.Buffer{}

	code := -1
	if resp != nil {
		defer resp.Body.Close()
		code = resp.StatusCode
		io.Copy(buf, resp.Body)
	}

	// TODO: Sadly, this seems to be necassary. Can't seem to get it
	// working without it. I always get `malformed HTTP response ""`
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()

	return code, buf.String(), err
}

func prepareKey() string {
	f, err := os.Open(keyFile)
	if err != nil {
		return ""
	}
	defer f.Close()
	key, err := ioutil.ReadAll(f)
	if err != nil {
		return ""
	}
	skey := strings.TrimSpace(string(key))
	code, _, err := makeApiCall("GET", "/"+skey, nil)
	if err == nil && code == http.StatusOK {
		return skey
	}
	return ""
}

func slashify(url string) string {
	url = path.Clean(url)
	if !strings.HasSuffix(url, "/") {
		url = url + "/"
	}
	return "http://" + strings.TrimPrefix(url, "/")
}

const (
	DEFAULT_REMOTE_SERVER = "pixelpixel.haxigon.com"
)

func validateServer(server string) string {
	t := time.AfterFunc(3*time.Second, func() {
		log.Fatalf("Attempt to connect to server timed out")
	})
	defer func() {
		t.Stop()
	}()

	resp, err := http.Get("http://" + path.Join(server, "handshake"))
	if err == nil && readAll(resp.Body) == "PIXELPIXEL OK" {
		return server
	}

	resp, err = http.Get("http://" + path.Join(DEFAULT_REMOTE_SERVER, "handshake"))
	if err == nil && readAll(resp.Body) == "PIXELPIXEL OK" {
		return DEFAULT_REMOTE_SERVER
	}
	log.Fatalf("Could not connect to server")
	return ""
}

func readAll(r io.Reader) string {
	data, _ := ioutil.ReadAll(r)
	return string(data)
}
<|endoftext|>


<commit_before>/*
Package router implements wrappers around
github.com/julienschmidt/httprouter.Router to enable the use of
golang.org/x/net/context.Context in endpoint handlers.

A simple example looks like this:

	package main

	import (
		"fmt"
		"log"
		"net/http"

		"github.com/teepark/contextual"
		"github.com/teepark/contextual/router"
		"golang.org/x/net/context"
	)

	func index(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Welcome!")
	}

	func hello(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		params := router.Params(ctx)
		fmt.Fprintf(w, "Hello, %s!\n", params.ByName("name"))
	}

	func main() {
		router := router.New(nil, nil) // take defaults
		router.GET("/", contextual.HandlerFunc(index))
		router.GET("/hello/:name", contextual.HandlerFunc(hello))

		log.Fatal(http.ListenAndServe(":8080", router))
	}
*/
package router

import (
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/teepark/contextual"
	"github.com/teepark/contextual/middleware"
	"golang.org/x/net/context"
)

// ParamKey is the key in a context.Context under which the httprouter.Params
// will be stored.
const ParamKey = "params"

// Router wraps an httprouter.Router to accept contextual.Handlers. For
// each request it adds the httprouter.Params to the context as
// Value(router.ParamKey), then passes it to the appropriate (optionally
// middleware-wrapped) handler.
type Router struct {
	router *httprouter.Router
	mw     middleware.Middleware
}

// New creates a new Router around a given httprouter.Router.
// All arguments may be nil, in which case the Router would wrap a
// Router created with httprouter.New() and there would be no
// middleware applied.
func New(router *httprouter.Router, mw middleware.Middleware) *Router {
	if router == nil {
		router = httprouter.New()
	}

	return &Router{
		router: router,
		mw:     mw,
	}
}

// ServeHTTP implements http.Handler
func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	router.router.ServeHTTP(w, r)
}

// ServeFiles serves files from a given filesystem root. The path must
// end with "/*filepath". See httprouter's documentation for details.
func (router *Router) ServeFiles(path string, root http.FileSystem) {
	router.router.ServeFiles(path, root)
}

// Handle adds a method/path handler with a context.Context argument
func (router *Router) Handle(method, path string, handle contextual.Handler) {
	router.router.Handle(method, path, handlerShim(router, handle))
}

// Handler adds an http.Handler for a method/path
func (router *Router) Handler(method, path string, handler http.Handler) {
	router.router.Handler(method, path, handler)
}

// HandlerFunc adds an http.HandlerFunc for a method/path
func (router *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	router.router.HandlerFunc(method, path, handler)
}

// GET is a shortcut for Handle("GET", ...)
func (router *Router) GET(path string, handle contextual.Handler) {
	router.Handle("GET", path, handle)
}

// HEAD is a shortcut for Handle("HEAD", ...)
func (router *Router) HEAD(path string, handle contextual.Handler) {
	router.Handle("HEAD", path, handle)
}

// POST is a shortcut for Handle("POST", ...)
func (router *Router) POST(path string, handle contextual.Handler) {
	router.Handle("POST", path, handle)
}

// PUT is a shortcut for Handle("PUT", ...)
func (router *Router) PUT(path string, handle contextual.Handler) {
	router.Handle("PUT", path, handle)
}

// DELETE is a shortcut for Handle("DELETE", ...)
func (router *Router) DELETE(path string, handle contextual.Handler) {
	router.Handle("DELETE", path, handle)
}

// OPTIONS is a shortcut for Handle("OPTIONS", ...)
func (router *Router) OPTIONS(path string, handle contextual.Handler) {
	router.Handle("OPTIONS", path, handle)
}

// PATCH is a shortcut for Handle("PATCH", ...)
func (router *Router) PATCH(path string, handle contextual.Handler) {
	router.Handle("PATCH", path, handle)
}

func handlerShim(router *Router, handle contextual.Handler) httprouter.Handle {
	if router.mw != nil {
		handle = router.mw(handle)
	}

	return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		ctx := context.WithValue(context.Background(), ParamKey, p)
		handle.Serve(ctx, w, r)
	})
}

// Params retrieves the httprouter Params from a context
func Params(c context.Context) httprouter.Params {
	p := c.Value(ParamKey)
	if p == nil {
		return nil
	}
	return p.(httprouter.Params)
}
<commit_msg>Lookup method though we can't get the contextual handler out of it<commit_after>/*
Package router implements wrappers around
github.com/julienschmidt/httprouter.Router to enable the use of
golang.org/x/net/context.Context in endpoint handlers.

A simple example looks like this:

	package main

	import (
		"fmt"
		"log"
		"net/http"

		"github.com/teepark/contextual"
		"github.com/teepark/contextual/router"
		"golang.org/x/net/context"
	)

	func index(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Welcome!")
	}

	func hello(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		params := router.Params(ctx)
		fmt.Fprintf(w, "Hello, %s!\n", params.ByName("name"))
	}

	func main() {
		router := router.New(nil, nil) // take defaults
		router.GET("/", contextual.HandlerFunc(index))
		router.GET("/hello/:name", contextual.HandlerFunc(hello))

		log.Fatal(http.ListenAndServe(":8080", router))
	}
*/
package router

import (
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/teepark/contextual"
	"github.com/teepark/contextual/middleware"
	"golang.org/x/net/context"
)

// ParamKey is the key in a context.Context under which the httprouter.Params
// will be stored.
const ParamKey = "params"

// Router wraps an httprouter.Router to accept contextual.Handlers. For
// each request it adds the httprouter.Params to the context as
// Value(router.ParamKey), then passes it to the appropriate (optionally
// middleware-wrapped) handler.
type Router struct {
	router *httprouter.Router
	mw     middleware.Middleware
}

// New creates a new Router around a given httprouter.Router.
// All arguments may be nil, in which case the Router would wrap a
// Router created with httprouter.New() and there would be no
// middleware applied.
func New(router *httprouter.Router, mw middleware.Middleware) *Router {
	if router == nil {
		router = httprouter.New()
	}

	return &Router{
		router: router,
		mw:     mw,
	}
}

// ServeHTTP implements http.Handler
func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	router.router.ServeHTTP(w, r)
}

// ServeFiles serves files from a given filesystem root. The path must
// end with "/*filepath". See httprouter's documentation for details.
func (router *Router) ServeFiles(path string, root http.FileSystem) {
	router.router.ServeFiles(path, root)
}

// Lookup indicates whether a request would be handled,
// and what url parameters would be extracted.
func (router *Router) Lookup(method, path string) (httprouter.Params, bool) {
	_, p, ok := router.router.Lookup(method, path)
	return p, ok
}

// Handle adds a method/path handler with a context.Context argument
func (router *Router) Handle(method, path string, handle contextual.Handler) {
	router.router.Handle(method, path, handlerShim(router, handle))
}

// Handler adds an http.Handler for a method/path
func (router *Router) Handler(method, path string, handler http.Handler) {
	router.router.Handler(method, path, handler)
}

// HandlerFunc adds an http.HandlerFunc for a method/path
func (router *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	router.router.HandlerFunc(method, path, handler)
}

// GET is a shortcut for Handle("GET", ...)
func (router *Router) GET(path string, handle contextual.Handler) {
	router.Handle("GET", path, handle)
}

// HEAD is a shortcut for Handle("HEAD", ...)
func (router *Router) HEAD(path string, handle contextual.Handler) {
	router.Handle("HEAD", path, handle)
}

// POST is a shortcut for Handle("POST", ...)
func (router *Router) POST(path string, handle contextual.Handler) {
	router.Handle("POST", path, handle)
}

// PUT is a shortcut for Handle("PUT", ...)
func (router *Router) PUT(path string, handle contextual.Handler) {
	router.Handle("PUT", path, handle)
}

// DELETE is a shortcut for Handle("DELETE", ...)
func (router *Router) DELETE(path string, handle contextual.Handler) {
	router.Handle("DELETE", path, handle)
}

// OPTIONS is a shortcut for Handle("OPTIONS", ...)
func (router *Router) OPTIONS(path string, handle contextual.Handler) {
	router.Handle("OPTIONS", path, handle)
}

// PATCH is a shortcut for Handle("PATCH", ...)
func (router *Router) PATCH(path string, handle contextual.Handler) {
	router.Handle("PATCH", path, handle)
}

func handlerShim(router *Router, handle contextual.Handler) httprouter.Handle {
	if router.mw != nil {
		handle = router.mw(handle)
	}

	return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		ctx := context.WithValue(context.Background(), ParamKey, p)
		handle.Serve(ctx, w, r)
	})
}

// Params retrieves the httprouter Params from a context
func Params(c context.Context) httprouter.Params {
	p := c.Value(ParamKey)
	if p == nil {
		return nil
	}
	return p.(httprouter.Params)
}
<|endoftext|>


<commit_before>package pinterface

import "net/http"

// Database interfaces

type IList interface {
	Add(value string) error
	GetAll() ([]string, error)
	GetLast() (string, error)
	GetLastN(n int) ([]string, error)
	Remove() error
	Clear() error
}

type ISet interface {
	Add(value string) error
	Has(value string) (bool, error)
	GetAll() ([]string, error)
	Del(value string) error
	Remove() error
	Clear() error
}

type IHashMap interface {
	Set(owner, key, value string) error
	Get(owner, key string) (string, error)
	Has(owner, key string) (bool, error)
	Exists(owner string) (bool, error)
	GetAll() ([]string, error)
	DelKey(owner, key string) error
	Del(key string) error
	Remove() error
	Clear() error
}

type IKeyValue interface {
	Set(key, value string) error
	Get(key string) (string, error)
	Del(key string) error
	Remove() error
	Clear() error
}

// Interface for making it possible to depend on different versions of the permission package, or other packages that implement userstates.
type IUserState interface {
	UserRights(req *http.Request) bool
	HasUser(username string) bool
	BooleanField(username, fieldname string) bool
	SetBooleanField(username, fieldname string, val bool)
	IsConfirmed(username string) bool
	IsLoggedIn(username string) bool
	AdminRights(req *http.Request) bool
	IsAdmin(username string) bool
	UsernameCookie(req *http.Request) (string, error)
	SetUsernameCookie(w http.ResponseWriter, username string) error
	AllUsernames() ([]string, error)
	Email(username string) (string, error)
	PasswordHash(username string) (string, error)
	AllUnconfirmedUsernames() ([]string, error)
	ConfirmationCode(username string) (string, error)
	AddUnconfirmed(username, confirmationCode string)
	RemoveUnconfirmed(username string)
	MarkConfirmed(username string)
	RemoveUser(username string)
	SetAdminStatus(username string)
	RemoveAdminStatus(username string)
	AddUser(username, password, email string)
	SetLoggedIn(username string)
	SetLoggedOut(username string)
	Login(w http.ResponseWriter, username string) error
	ClearCookie(w http.ResponseWriter)
	Logout(username string)
	Username(req *http.Request) string
	CookieTimeout(username string) int64
	SetCookieTimeout(cookieTime int64)
	PasswordAlgo() string
	SetPasswordAlgo(algorithm string) error
	HashPassword(username, password string) string
	CorrectPassword(username, password string) bool
	AlreadyHasConfirmationCode(confirmationCode string) bool
	FindUserByConfirmationCode(confirmationcode string) (string, error)
	Confirm(username string)
	ConfirmUserByConfirmationCode(confirmationcode string) error
	SetMinimumConfirmationCodeLength(length int)
	GenerateUniqueConfirmationCode() (string, error)

	// Related to the database backend
	Users() IHashMap
	Host() IHost
}

// A Database file or host
type IHost interface {
	Ping() error
	Close()
}
<commit_msg>Additional interfaces<commit_after>package pinterface

import "net/http"

// Database interfaces

type IList interface {
	Add(value string) error
	GetAll() ([]string, error)
	GetLast() (string, error)
	GetLastN(n int) ([]string, error)
	Remove() error
	Clear() error
}

type ISet interface {
	Add(value string) error
	Has(value string) (bool, error)
	GetAll() ([]string, error)
	Del(value string) error
	Remove() error
	Clear() error
}

type IHashMap interface {
	Set(owner, key, value string) error
	Get(owner, key string) (string, error)
	Has(owner, key string) (bool, error)
	Exists(owner string) (bool, error)
	GetAll() ([]string, error)
	DelKey(owner, key string) error
	Del(key string) error
	Remove() error
	Clear() error
}

type IKeyValue interface {
	Set(key, value string) error
	Get(key string) (string, error)
	Del(key string) error
	Remove() error
	Clear() error
}

// Interface for making it possible to depend on different versions of the permission package, or other packages that implement userstates.
type IUserState interface {
	UserRights(req *http.Request) bool
	HasUser(username string) bool
	BooleanField(username, fieldname string) bool
	SetBooleanField(username, fieldname string, val bool)
	IsConfirmed(username string) bool
	IsLoggedIn(username string) bool
	AdminRights(req *http.Request) bool
	IsAdmin(username string) bool
	UsernameCookie(req *http.Request) (string, error)
	SetUsernameCookie(w http.ResponseWriter, username string) error
	AllUsernames() ([]string, error)
	Email(username string) (string, error)
	PasswordHash(username string) (string, error)
	AllUnconfirmedUsernames() ([]string, error)
	ConfirmationCode(username string) (string, error)
	AddUnconfirmed(username, confirmationCode string)
	RemoveUnconfirmed(username string)
	MarkConfirmed(username string)
	RemoveUser(username string)
	SetAdminStatus(username string)
	RemoveAdminStatus(username string)
	AddUser(username, password, email string)
	SetLoggedIn(username string)
	SetLoggedOut(username string)
	Login(w http.ResponseWriter, username string) error
	ClearCookie(w http.ResponseWriter)
	Logout(username string)
	Username(req *http.Request) string
	CookieTimeout(username string) int64
	SetCookieTimeout(cookieTime int64)
	PasswordAlgo() string
	SetPasswordAlgo(algorithm string) error
	HashPassword(username, password string) string
	CorrectPassword(username, password string) bool
	AlreadyHasConfirmationCode(confirmationCode string) bool
	FindUserByConfirmationCode(confirmationcode string) (string, error)
	Confirm(username string)
	ConfirmUserByConfirmationCode(confirmationcode string) error
	SetMinimumConfirmationCodeLength(length int)
	GenerateUniqueConfirmationCode() (string, error)

	// Related to the database backend
	Users() IHashMap
	Host() IHost
	Creator() ICreator
}

// Database host (or file)
type IHost interface {
	Ping() error
	Close()
}

// Redis host
type IRedisHost interface {
	Pool()
	DatabaseIndex()
}

// Can be used to create new data structures
type ICreator interface {
	NewList(id string) IList
	NewSet(id string) ISet
	NewHashMap(id string) IHashMap
	NewKeyValue(id string) IKeyValue
}
<|endoftext|>


<commit_before>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016
	All Rights Reserved

	For Licensing and Usage information, please see LICENSE.md
*/
package egl

/* TYPES */
type Error struct {
    msg    string // description of error
    code   int32  // error code
}

/* CONSTANTS */
const (
	SUCCESS             = 0x3000
	NOT_INITIALIZED     = 0x3001
	BAD_ACCESS          = 0x3002
	BAD_ALLOC           = 0x3003
	BAD_ATTRIBUTE       = 0x3004
	BAD_CONFIG          = 0x3005
	BAD_CONTEXT         = 0x3006
	BAD_CURRENT_SURFACE = 0x3007
	BAD_DISPLAY         = 0x3008
	BAD_MATCH           = 0x3009
	BAD_NATIVE_PIXMAP   = 0x300A
	BAD_NATIVE_WINDOW   = 0x300B
	BAD_PARAMETER       = 0x300C
	BAD_SURFACE         = 0x300D
	CONTEXT_LOST        = 0x300E
)

/* METHODS */
func (e *Error) Error() string {
	return e.msg
}

func toError(code int32) Error {
	switch code {
		case SUCCESS:
			return Error{ "Success", code }
		case NOT_INITIALIZED:
			return Error{ "Not Initialized", code }
		case BAD_ACCESS:
			return Error{ "Bad Access", code }
		default:
			return Error{ "Other Error", code }
	}
}

<commit_msg>Updated<commit_after>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016
	All Rights Reserved

	For Licensing and Usage information, please see LICENSE.md
*/
package egl

/* TYPES */
type Error struct {
    msg    string // description of error
    code   int32  // error code
}

/* CONSTANTS */
const (
	SUCCESS             = 0x3000
	NOT_INITIALIZED     = 0x3001
	BAD_ACCESS          = 0x3002
	BAD_ALLOC           = 0x3003
	BAD_ATTRIBUTE       = 0x3004
	BAD_CONFIG          = 0x3005
	BAD_CONTEXT         = 0x3006
	BAD_CURRENT_SURFACE = 0x3007
	BAD_DISPLAY         = 0x3008
	BAD_MATCH           = 0x3009
	BAD_NATIVE_PIXMAP   = 0x300A
	BAD_NATIVE_WINDOW   = 0x300B
	BAD_PARAMETER       = 0x300C
	BAD_SURFACE         = 0x300D
	CONTEXT_LOST        = 0x300E
)

/* METHODS */
func (e *Error) Error() string {
	return e.msg
}

func toError(code int32) *Error {
	switch code {
		case SUCCESS:
			return &Error{ "Success", code }
		case NOT_INITIALIZED:
			return &Error{ "Not Initialized", code }
		case BAD_ACCESS:
			return &Error{ "Bad Access", code }
		default:
			return &Error{ "Other Error", code }
	}
}

<|endoftext|>


<commit_before>package gpio

/*
#cgo LDFLAGS: -lrt

#include "gpio.h"

uint16_t DHT11(unsigned pin) {
	uint8_t pulses[5] = {0};
	gpioSetMode(pin, PI_OUTPUT);
	gpioTrigger(pin, 18000, 0);
	gpioSetMode(pin, PI_INPUT);
	usleep(20);
	if (gpioReadPulse(pin, 100, 0) > 100 ||
		gpioReadPulse(pin, 100, 1) > 100) {
		return 0;
	}
	if (gpioReadPulses(pin, 1000, sizeof(pulses) << 3, pulses)) {
		if (pulses[0] + pulses[1] + pulses[2] + pulses[3] == pulses[4]) {
			return (pulses[0] << 8) + pulses[2];
		}
	}
	return 0;
}
*/
import "C"
import "time"

func init() {
	C.gpioInitialise()
}

// Pin type to operate single GPIO pin state, mode and value
type Pin byte

// Mode represents pin mode (see options below)
type Mode byte

// Pin operating mode
const (
	// INPUT (available for read) mode
	INPUT  Mode = C.PI_INPUT
	OUTPUT      = C.PI_OUTPUT
	ALT0        = C.PI_ALT0
	ALT1        = C.PI_ALT1
	ALT2        = C.PI_ALT2
	ALT3        = C.PI_ALT3
	ALT4        = C.PI_ALT4
	ALT5        = C.PI_ALT5
)

// PullState is a pin pull-up/down state
type PullState byte

// Pull states
const (
	// Pull off
	OFF  PullState = C.PI_PUD_OFF
	DOWN           = C.PI_PUD_DOWN
	UP             = C.PI_PUD_UP
)

// SetMode sets pin operating mode
func (pin Pin) SetMode(mode Mode) {
	C.gpioSetMode(C.uint(pin), C.uint(mode))
}

// GetMode gets pin operating mode
func (pin Pin) GetMode() Mode {
	return Mode(C.gpioGetMode(C.uint(pin)))
}

func (pin Pin) Read() bool {
	return C.gpioRead(C.uint(pin)) > 0
}

func (pin Pin) Write(value bool) {
	var intValue C.uint
	if value {
		intValue = 1
	}
	C.gpioWrite(C.uint(pin), intValue)
}

// Trigger the state of the pin to value for t
func (pin Pin) Trigger(t time.Duration, value bool) {
	var intValue C.uint
	if value {
		intValue = 1
	}
	C.gpioTrigger(C.uint(pin), C.uint(t.Nanoseconds()/1000), intValue)
}

// DHT11 reads humidity and temperature from the sensor
func (pin Pin) DHT11() (byte, byte) {
	humidityAndTemperature := int(C.DHT11(C.uint(pin)))
	return byte(humidityAndTemperature >> 8),
		byte(humidityAndTemperature & 255)
}

/*
void gpioSetPullUpDown(unsigned gpio, unsigned pud)
*/
<commit_msg>Add RPi A+ V1.1 GPIO Layout comment<commit_after>package gpio

/*
#cgo LDFLAGS: -lrt

#include "gpio.h"

uint16_t DHT11(unsigned pin) {
	uint8_t pulses[5] = {0};
	gpioSetMode(pin, PI_OUTPUT);
	gpioTrigger(pin, 18000, 0);
	gpioSetMode(pin, PI_INPUT);
	usleep(20);
	if (gpioReadPulse(pin, 100, 0) > 100 ||
		gpioReadPulse(pin, 100, 1) > 100) {
		return 0;
	}
	if (gpioReadPulses(pin, 1000, sizeof(pulses) << 3, pulses)) {
		if (pulses[0] + pulses[1] + pulses[2] + pulses[3] == pulses[4]) {
			return (pulses[0] << 8) + pulses[2];
		}
	}
	return 0;
}
*/
import "C"
import "time"

/*
RPi A+ V1.1 GPIO Layout

             3V3 . . 5V
(I2C SDA) GPIO 2 . . 5V
(I2C SCL) GPIO 3 . . GND
          GPIO 4 . . GPIO14 (UART_TXD)
             GND . . GPIO15 (UART_RXD)
          GPIO17 . . GPIO18 (PWM0, PCM_CLK)
          GPIO27 . . GND
          GPIO22 . . GPIO23
             3V3 . . GPIO24
   (MOSI) GPIO10 . . GND
   (MISO) GPIO 9 . . GPIO25
   (SCLK) GPIO11 . . GPIO 8 (CE0_N)
             GND . . GPIO 7 (CE1_N)
I2C ID EEPROM SD . . I2C ID EEPROM SC
          GPIO 5 . . GND
          GPIO 6 . . GPIO12 (PWM1)
   (PWM1) GPIO13 . . GND
   (PWM0) GPIO19 . . GPIO16
          GPIO26 . . GPIO20
             GND . . GPIO21

             ... USB ...
*/

func init() {
	C.gpioInitialise()
}

// Pin type to operate single GPIO pin state, mode and value
type Pin byte

// Mode represents pin mode (see options below)
type Mode byte

// Pin operating mode
const (
	// INPUT (available for read) mode
	INPUT  Mode = C.PI_INPUT
	OUTPUT      = C.PI_OUTPUT
	ALT0        = C.PI_ALT0
	ALT1        = C.PI_ALT1
	ALT2        = C.PI_ALT2
	ALT3        = C.PI_ALT3
	ALT4        = C.PI_ALT4
	ALT5        = C.PI_ALT5
)

// PullState is a pin pull-up/down state
type PullState byte

// Pull states
const (
	// Pull off
	OFF  PullState = C.PI_PUD_OFF
	DOWN           = C.PI_PUD_DOWN
	UP             = C.PI_PUD_UP
)

// SetMode sets pin operating mode
func (pin Pin) SetMode(mode Mode) {
	C.gpioSetMode(C.uint(pin), C.uint(mode))
}

// GetMode gets pin operating mode
func (pin Pin) GetMode() Mode {
	return Mode(C.gpioGetMode(C.uint(pin)))
}

func (pin Pin) Read() bool {
	return C.gpioRead(C.uint(pin)) > 0
}

func (pin Pin) Write(value bool) {
	var intValue C.uint
	if value {
		intValue = 1
	}
	C.gpioWrite(C.uint(pin), intValue)
}

// Trigger the state of the pin to value for t
func (pin Pin) Trigger(t time.Duration, value bool) {
	var intValue C.uint
	if value {
		intValue = 1
	}
	C.gpioTrigger(C.uint(pin), C.uint(t.Nanoseconds()/1000), intValue)
}

// DHT11 reads humidity and temperature from the sensor
func (pin Pin) DHT11() (byte, byte) {
	humidityAndTemperature := int(C.DHT11(C.uint(pin)))
	return byte(humidityAndTemperature >> 8),
		byte(humidityAndTemperature & 255)
}

/*
void gpioSetPullUpDown(unsigned gpio, unsigned pud)
*/
<|endoftext|>


<commit_before>// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package naming provides:
//	- subpackage endpoints: an abstraction layer to store and read endpoints
//		information from etcd.
//	- subpackage resolver: an etcd-backed gRPC resolver for discovering gRPC
//		services based on the endpoints configuration
//
// To use, first import the packages:
//
//	import (
//		"go.etcd.io/etcd/client/v3"
//		"go.etcd.io/etcd/client/v3/naming/endpoints"
//		"go.etcd.io/etcd/client/v3/naming/resolver"
//		"google.golang.org/grpc"
//	)
//
// First, register new endpoint addresses for a service:
//
//	func etcdAdd(c *clientv3.Client, service, addr string) error {
//		em := endpoints.NewManager(c, service)
//		return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr});
//	}
//
// Dial an RPC service using the etcd gRPC resolver and a gRPC Balancer:
//
//	func etcdDial(c *clientv3.Client, service string) (*grpc.ClientConn, error) {
//		etcdResolver, err := resolver.NewBuilder(c);
//		if err { return nil, err }
//		return  grpc.Dial("etcd://foo", grpc.WithResolvers(etcdResolver))
//	}
//
// Optionally, force delete an endpoint:
//
//	func etcdDelete(c *clientv3, service, addr string) error {
//		em := endpoints.NewManager(c, service)
//		return em.DeleteEndpoint(c.Ctx(), service+"/"+addr)
//	}
//
// Or register an expiring endpoint with a lease:
//
//	func etcdAdd(c *clientv3.Client, lid clientv3.LeaseID, service, addr string) error {
//		em := endpoints.NewManager(c, service)
//		return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr}, clientv3.WithLease(lid));
//	}
//
package naming
<commit_msg>Update doc.go<commit_after>// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package naming provides:
//	- subpackage endpoints: an abstraction layer to store and read endpoints
//		information from etcd.
//	- subpackage resolver: an etcd-backed gRPC resolver for discovering gRPC
//		services based on the endpoints configuration
//
// To use, first import the packages:
//
//	import (
//		"go.etcd.io/etcd/client/v3"
//		"go.etcd.io/etcd/client/v3/naming/endpoints"
//		"go.etcd.io/etcd/client/v3/naming/resolver"
//		"google.golang.org/grpc"
//	)
//
// First, register new endpoint addresses for a service:
//
//	func etcdAdd(c *clientv3.Client, service, addr string) error {
//		em := endpoints.NewManager(c, service)
//		return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr});
//	}
//
// Dial an RPC service using the etcd gRPC resolver and a gRPC Balancer:
//
//	func etcdDial(c *clientv3.Client, service string) (*grpc.ClientConn, error) {
//		etcdResolver, err := resolver.NewBuilder(c);
//		if err { return nil, err }
//		return  grpc.Dial("etcd:///foo", grpc.WithResolvers(etcdResolver))
//	}
//
// Optionally, force delete an endpoint:
//
//	func etcdDelete(c *clientv3, service, addr string) error {
//		em := endpoints.NewManager(c, service)
//		return em.DeleteEndpoint(c.Ctx(), service+"/"+addr)
//	}
//
// Or register an expiring endpoint with a lease:
//
//	func etcdAdd(c *clientv3.Client, lid clientv3.LeaseID, service, addr string) error {
//		em := endpoints.NewManager(c, service)
//		return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr}, clientv3.WithLease(lid));
//	}
//
package naming
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019 Andrew Ayer
 *
 * 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.
 *
 * Except as contained in this notice, the name(s) of the above copyright
 * holders shall not be used in advertising or otherwise to promote the
 * sale, use or other dealings in this Software without prior written
 * authorization.
 */

package smsxmpp

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"log"
	"time"

	"src.agwa.name/sms-over-xmpp/config"
	"src.agwa.name/go-xmpp/component"
	"src.agwa.name/go-xmpp"
)

type user struct {
	phoneNumber string // e.g. "+19255551212"
	provider    Provider
}

type Service struct {
	publicURL       string
	users           map[xmpp.Address]user // Map from bare JID -> user
	providers       map[string]Provider
	xmppParams      component.Params
	xmppSendChan    chan interface{}
}

func NewService(config *config.Config) (*Service, error) {
	service := &Service{
		publicURL:   config.PublicURL,
		users:       make(map[xmpp.Address]user),
		providers:   make(map[string]Provider),
		xmppParams:  component.Params{
			Domain: config.XMPPDomain,
			Secret: config.XMPPSecret,
			Server: config.XMPPServer,
			Logger: log.New(os.Stderr, "", log.Ldate | log.Ltime | log.Lmicroseconds),
		},
		xmppSendChan: make(chan interface{}),
	}

	for providerName, providerConfig := range config.Providers {
		provider, err := MakeProvider(providerConfig.Type, service, providerConfig.Params)
		if err != nil {
			return nil, fmt.Errorf("Provider %s: %s", providerName, err)
		}
		service.providers[providerName] = provider
	}

	for userJID, userConfig := range config.Users {
		userAddress, err := xmpp.ParseAddress(userJID)
		if err != nil {
			return nil, fmt.Errorf("User %s has malformed JID: %s", userJID, err)
		}
		userProvider, providerExists := service.providers[userConfig.Provider]
		if !providerExists {
			return nil, fmt.Errorf("User %s refers to non-existent provider %s", userJID, userConfig.Provider)
		}
		service.users[userAddress] = user{
			phoneNumber: userConfig.PhoneNumber,
			provider: userProvider,
		}
	}
	return service, nil
}

func (service *Service) HTTPHandler() http.Handler {
	mux := http.NewServeMux()
	for name, provider := range service.providers {
		if providerHandler := provider.HTTPHandler(); providerHandler != nil {
			mux.Handle("/" + name + "/", http.StripPrefix("/" + name, providerHandler))
		}
	}
	return mux
}

func (service *Service) RunXMPPComponent() error {
	callbacks := component.Callbacks{
		Message: service.receiveXMPPMessage,
		Presence: service.receiveXMPPPresence,
	}

	return component.Run(context.Background(), service.xmppParams, callbacks, service.xmppSendChan)
}

func (service *Service) Receive(message *Message) error {
	address, known := service.addressForPhoneNumber(message.To)
	if !known {
		return errors.New("Unknown phone number " + message.To)
	}

	xmppMessage := xmpp.Message{
		Header: xmpp.Header{
			From: &xmpp.Address{message.From, service.xmppParams.Domain, ""},
			To:   &address,
		},
		Body: message.Body,
		Type: xmpp.CHAT,
	}
	// TODO: if message.MediaURLs is non-empty, include them using XEP-0066

	select {
	case service.xmppSendChan <- xmppMessage:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timed out when sending XMPP message")
	}
}

func (service *Service) receiveXMPPMessage(ctx context.Context, xmppMessage *xmpp.Message) error {
	if xmppMessage.From == nil || xmppMessage.To == nil {
		return errors.New("Received malformed XMPP message: From and To not set")
	}
	user, userExists := service.users[*xmppMessage.From.Bare()]
	if !userExists {
		// TODO: more helpful error message: echo back xmppMessage.From.Bare() and say it's not in users map
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Not authorized")
	}

	toPhoneNumber := xmppMessage.To.LocalPart
	if !isValidPhoneNumber(toPhoneNumber) {
		// TODO: more helpful error message: echo back toPhoneNumber and say what format it should be in
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Invalid phone number")
	}

	message := &Message{
		From: user.phoneNumber,
		To:   toPhoneNumber,
		Body: xmppMessage.Body,
	}
	// TODO: if xmppMessage has XEP-0066 URLs, include them in message.MediaURLs

	go func() {
		err := user.provider.Send(message)
		if err != nil {
			// TODO: if sendXMPPError fails, log the error
			service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Sending SMS failed: " + err.Error())
		}
	}()

	return nil
}

func (service *Service) receiveXMPPPresence(ctx context.Context, presence *xmpp.Presence) error {
	if presence.From == nil || presence.To == nil {
		return errors.New("Received malformed XMPP presence: From and To not set")
	}

	if _, userExists := service.users[*presence.From.Bare()]; !userExists {
		return nil
	}

	if presence.Type == xmpp.SUBSCRIBE {
		if err := service.sendXMPPPresence(presence.To, presence.From, xmpp.SUBSCRIBED, ""); err != nil {
			return err
		}
	}

	if presence.Type == xmpp.SUBSCRIBE || presence.Type == xmpp.PROBE {
		var err error
		if isValidPhoneNumber(presence.To.LocalPart) {
			err = service.sendXMPPPresence(presence.To, presence.From, "", "")
		} else {
			err = service.sendXMPPPresence(presence.To, presence.From, "error", "Invalid phone number")
		}
		if err != nil {
			return err
		}
	}

	return nil
}

func (service *Service) sendXMPPError(from *xmpp.Address, to *xmpp.Address, message string) error {
	xmppMessage := &xmpp.Message{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: xmpp.ERROR,
		Body: message,
	}
	select {
	case service.xmppSendChan <- xmppMessage:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timed out when sending XMPP error message")
	}
}

func (service *Service) sendXMPPPresence(from *xmpp.Address, to *xmpp.Address, presenceType string, status string) error {
	xmppPresence := &xmpp.Presence{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: presenceType,
		Status: status,
	}
	select {
	case service.xmppSendChan <- xmppPresence:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timed out when sending XMPP presence")
	}
}

func (service *Service) addressForPhoneNumber(phoneNumber string) (xmpp.Address, bool) {
	for address, user := range service.users {
		if user.phoneNumber == phoneNumber {
			return address, true
		}
	}
	return xmpp.Address{}, false
}
<commit_msg>Factor out code to send an XMPP chat<commit_after>/*
 * Copyright (c) 2019 Andrew Ayer
 *
 * 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.
 *
 * Except as contained in this notice, the name(s) of the above copyright
 * holders shall not be used in advertising or otherwise to promote the
 * sale, use or other dealings in this Software without prior written
 * authorization.
 */

package smsxmpp

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"log"
	"time"

	"src.agwa.name/sms-over-xmpp/config"
	"src.agwa.name/go-xmpp/component"
	"src.agwa.name/go-xmpp"
)

type user struct {
	phoneNumber string // e.g. "+19255551212"
	provider    Provider
}

type Service struct {
	publicURL       string
	users           map[xmpp.Address]user // Map from bare JID -> user
	providers       map[string]Provider
	xmppParams      component.Params
	xmppSendChan    chan interface{}
}

func NewService(config *config.Config) (*Service, error) {
	service := &Service{
		publicURL:   config.PublicURL,
		users:       make(map[xmpp.Address]user),
		providers:   make(map[string]Provider),
		xmppParams:  component.Params{
			Domain: config.XMPPDomain,
			Secret: config.XMPPSecret,
			Server: config.XMPPServer,
			Logger: log.New(os.Stderr, "", log.Ldate | log.Ltime | log.Lmicroseconds),
		},
		xmppSendChan: make(chan interface{}),
	}

	for providerName, providerConfig := range config.Providers {
		provider, err := MakeProvider(providerConfig.Type, service, providerConfig.Params)
		if err != nil {
			return nil, fmt.Errorf("Provider %s: %s", providerName, err)
		}
		service.providers[providerName] = provider
	}

	for userJID, userConfig := range config.Users {
		userAddress, err := xmpp.ParseAddress(userJID)
		if err != nil {
			return nil, fmt.Errorf("User %s has malformed JID: %s", userJID, err)
		}
		userProvider, providerExists := service.providers[userConfig.Provider]
		if !providerExists {
			return nil, fmt.Errorf("User %s refers to non-existent provider %s", userJID, userConfig.Provider)
		}
		service.users[userAddress] = user{
			phoneNumber: userConfig.PhoneNumber,
			provider: userProvider,
		}
	}
	return service, nil
}

func (service *Service) HTTPHandler() http.Handler {
	mux := http.NewServeMux()
	for name, provider := range service.providers {
		if providerHandler := provider.HTTPHandler(); providerHandler != nil {
			mux.Handle("/" + name + "/", http.StripPrefix("/" + name, providerHandler))
		}
	}
	return mux
}

func (service *Service) RunXMPPComponent() error {
	callbacks := component.Callbacks{
		Message: service.receiveXMPPMessage,
		Presence: service.receiveXMPPPresence,
	}

	return component.Run(context.Background(), service.xmppParams, callbacks, service.xmppSendChan)
}

func (service *Service) Receive(message *Message) error {
	address, known := service.addressForPhoneNumber(message.To)
	if !known {
		return errors.New("Unknown phone number " + message.To)
	}
	from := xmpp.Address{message.From, service.xmppParams.Domain, ""}

	if err := service.sendXMPPChat(from, address, message.Body); err != nil {
		return err
	}

	// TODO: if message.MediaURLs is non-empty, send them using XEP-0066

	return nil
}

func (service *Service) sendXMPPChat(from xmpp.Address, to xmpp.Address, body string) error {
	xmppMessage := xmpp.Message{
		Header: xmpp.Header{
			From: &from,
			To:   &to,
		},
		Body: body,
		Type: xmpp.CHAT,
	}

	select {
	case service.xmppSendChan <- xmppMessage:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timed out when sending XMPP message")
	}
}

func (service *Service) receiveXMPPMessage(ctx context.Context, xmppMessage *xmpp.Message) error {
	if xmppMessage.From == nil || xmppMessage.To == nil {
		return errors.New("Received malformed XMPP message: From and To not set")
	}
	user, userExists := service.users[*xmppMessage.From.Bare()]
	if !userExists {
		// TODO: more helpful error message: echo back xmppMessage.From.Bare() and say it's not in users map
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Not authorized")
	}

	toPhoneNumber := xmppMessage.To.LocalPart
	if !isValidPhoneNumber(toPhoneNumber) {
		// TODO: more helpful error message: echo back toPhoneNumber and say what format it should be in
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Invalid phone number")
	}

	message := &Message{
		From: user.phoneNumber,
		To:   toPhoneNumber,
		Body: xmppMessage.Body,
	}
	// TODO: if xmppMessage has XEP-0066 URLs, include them in message.MediaURLs

	go func() {
		err := user.provider.Send(message)
		if err != nil {
			// TODO: if sendXMPPError fails, log the error
			service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Sending SMS failed: " + err.Error())
		}
	}()

	return nil
}

func (service *Service) receiveXMPPPresence(ctx context.Context, presence *xmpp.Presence) error {
	if presence.From == nil || presence.To == nil {
		return errors.New("Received malformed XMPP presence: From and To not set")
	}

	if _, userExists := service.users[*presence.From.Bare()]; !userExists {
		return nil
	}

	if presence.Type == xmpp.SUBSCRIBE {
		if err := service.sendXMPPPresence(presence.To, presence.From, xmpp.SUBSCRIBED, ""); err != nil {
			return err
		}
	}

	if presence.Type == xmpp.SUBSCRIBE || presence.Type == xmpp.PROBE {
		var err error
		if isValidPhoneNumber(presence.To.LocalPart) {
			err = service.sendXMPPPresence(presence.To, presence.From, "", "")
		} else {
			err = service.sendXMPPPresence(presence.To, presence.From, "error", "Invalid phone number")
		}
		if err != nil {
			return err
		}
	}

	return nil
}

func (service *Service) sendXMPPError(from *xmpp.Address, to *xmpp.Address, message string) error {
	xmppMessage := &xmpp.Message{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: xmpp.ERROR,
		Body: message,
	}
	select {
	case service.xmppSendChan <- xmppMessage:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timed out when sending XMPP error message")
	}
}

func (service *Service) sendXMPPPresence(from *xmpp.Address, to *xmpp.Address, presenceType string, status string) error {
	xmppPresence := &xmpp.Presence{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: presenceType,
		Status: status,
	}
	select {
	case service.xmppSendChan <- xmppPresence:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timed out when sending XMPP presence")
	}
}

func (service *Service) addressForPhoneNumber(phoneNumber string) (xmpp.Address, bool) {
	for address, user := range service.users {
		if user.phoneNumber == phoneNumber {
			return address, true
		}
	}
	return xmpp.Address{}, false
}
<|endoftext|>