<commit_before>// $G $F.go && $L $F.$A && ./$A.out

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Semi-exhaustive test for append()

package main

import (
	"fmt"
	"reflect"
)

func verify(name string, result, expected interface{}) {
	if !reflect.DeepEqual(result, expected) {
		panic(name)
	}
}

func main() {
	for _, t := range tests {
		verify(t.name, t.result, t.expected)
	}
	verifyStruct()
	verifyInterface()
}

var tests = []struct {
	name             string
	result, expected interface{}
}{
	{"bool a", append([]bool{}), []bool{}},
	{"bool b", append([]bool{}, true), []bool{true}},
	{"bool c", append([]bool{}, true, false, true, true), []bool{true, false, true, true}},

	{"bool d", append([]bool{true, false, true}), []bool{true, false, true}},
	{"bool e", append([]bool{true, false, true}, false), []bool{true, false, true, false}},
	{"bool f", append([]bool{true, false, true}, false, false, false), []bool{true, false, true, false, false, false}},

	{"bool g", append([]bool{}, []bool{true}...), []bool{true}},
	{"bool h", append([]bool{}, []bool{true, false, true, false}...), []bool{true, false, true, false}},

	{"bool i", append([]bool{true, false, true}, []bool{true}...), []bool{true, false, true, true}},
	{"bool j", append([]bool{true, false, true}, []bool{true, true, true}...), []bool{true, false, true, true, true, true}},

	{"byte a", append([]byte{}), []byte{}},
	{"byte b", append([]byte{}, 0), []byte{0}},
	{"byte c", append([]byte{}, 0, 1, 2, 3), []byte{0, 1, 2, 3}},

	{"byte d", append([]byte{0, 1, 2}), []byte{0, 1, 2}},
	{"byte e", append([]byte{0, 1, 2}, 3), []byte{0, 1, 2, 3}},
	{"byte f", append([]byte{0, 1, 2}, 3, 4, 5), []byte{0, 1, 2, 3, 4, 5}},

	{"byte g", append([]byte{}, []byte{0}...), []byte{0}},
	{"byte h", append([]byte{}, []byte{0, 1, 2, 3}...), []byte{0, 1, 2, 3}},

	{"byte i", append([]byte{0, 1, 2}, []byte{3}...), []byte{0, 1, 2, 3}},
	{"byte j", append([]byte{0, 1, 2}, []byte{3, 4, 5}...), []byte{0, 1, 2, 3, 4, 5}},

	{"bytestr a", append([]byte{}, "0"...), []byte("0")},
	{"bytestr b", append([]byte{}, "0123"...), []byte("0123")},

	{"bytestr c", append([]byte("012"), "3"...), []byte("0123")},
	{"bytestr d", append([]byte("012"), "345"...), []byte("012345")},

	{"int16 a", append([]int16{}), []int16{}},
	{"int16 b", append([]int16{}, 0), []int16{0}},
	{"int16 c", append([]int16{}, 0, 1, 2, 3), []int16{0, 1, 2, 3}},

	{"int16 d", append([]int16{0, 1, 2}), []int16{0, 1, 2}},
	{"int16 e", append([]int16{0, 1, 2}, 3), []int16{0, 1, 2, 3}},
	{"int16 f", append([]int16{0, 1, 2}, 3, 4, 5), []int16{0, 1, 2, 3, 4, 5}},

	{"int16 g", append([]int16{}, []int16{0}...), []int16{0}},
	{"int16 h", append([]int16{}, []int16{0, 1, 2, 3}...), []int16{0, 1, 2, 3}},

	{"int16 i", append([]int16{0, 1, 2}, []int16{3}...), []int16{0, 1, 2, 3}},
	{"int16 j", append([]int16{0, 1, 2}, []int16{3, 4, 5}...), []int16{0, 1, 2, 3, 4, 5}},

	{"uint32 a", append([]uint32{}), []uint32{}},
	{"uint32 b", append([]uint32{}, 0), []uint32{0}},
	{"uint32 c", append([]uint32{}, 0, 1, 2, 3), []uint32{0, 1, 2, 3}},

	{"uint32 d", append([]uint32{0, 1, 2}), []uint32{0, 1, 2}},
	{"uint32 e", append([]uint32{0, 1, 2}, 3), []uint32{0, 1, 2, 3}},
	{"uint32 f", append([]uint32{0, 1, 2}, 3, 4, 5), []uint32{0, 1, 2, 3, 4, 5}},

	{"uint32 g", append([]uint32{}, []uint32{0}...), []uint32{0}},
	{"uint32 h", append([]uint32{}, []uint32{0, 1, 2, 3}...), []uint32{0, 1, 2, 3}},

	{"uint32 i", append([]uint32{0, 1, 2}, []uint32{3}...), []uint32{0, 1, 2, 3}},
	{"uint32 j", append([]uint32{0, 1, 2}, []uint32{3, 4, 5}...), []uint32{0, 1, 2, 3, 4, 5}},

	{"float64 a", append([]float64{}), []float64{}},
	{"float64 b", append([]float64{}, 0), []float64{0}},
	{"float64 c", append([]float64{}, 0, 1, 2, 3), []float64{0, 1, 2, 3}},

	{"float64 d", append([]float64{0, 1, 2}), []float64{0, 1, 2}},
	{"float64 e", append([]float64{0, 1, 2}, 3), []float64{0, 1, 2, 3}},
	{"float64 f", append([]float64{0, 1, 2}, 3, 4, 5), []float64{0, 1, 2, 3, 4, 5}},

	{"float64 g", append([]float64{}, []float64{0}...), []float64{0}},
	{"float64 h", append([]float64{}, []float64{0, 1, 2, 3}...), []float64{0, 1, 2, 3}},

	{"float64 i", append([]float64{0, 1, 2}, []float64{3}...), []float64{0, 1, 2, 3}},
	{"float64 j", append([]float64{0, 1, 2}, []float64{3, 4, 5}...), []float64{0, 1, 2, 3, 4, 5}},

	{"complex128 a", append([]complex128{}), []complex128{}},
	{"complex128 b", append([]complex128{}, 0), []complex128{0}},
	{"complex128 c", append([]complex128{}, 0, 1, 2, 3), []complex128{0, 1, 2, 3}},

	{"complex128 d", append([]complex128{0, 1, 2}), []complex128{0, 1, 2}},
	{"complex128 e", append([]complex128{0, 1, 2}, 3), []complex128{0, 1, 2, 3}},
	{"complex128 f", append([]complex128{0, 1, 2}, 3, 4, 5), []complex128{0, 1, 2, 3, 4, 5}},

	{"complex128 g", append([]complex128{}, []complex128{0}...), []complex128{0}},
	{"complex128 h", append([]complex128{}, []complex128{0, 1, 2, 3}...), []complex128{0, 1, 2, 3}},

	{"complex128 i", append([]complex128{0, 1, 2}, []complex128{3}...), []complex128{0, 1, 2, 3}},
	{"complex128 j", append([]complex128{0, 1, 2}, []complex128{3, 4, 5}...), []complex128{0, 1, 2, 3, 4, 5}},

	{"string a", append([]string{}), []string{}},
	{"string b", append([]string{}, "0"), []string{"0"}},
	{"string c", append([]string{}, "0", "1", "2", "3"), []string{"0", "1", "2", "3"}},

	{"string d", append([]string{"0", "1", "2"}), []string{"0", "1", "2"}},
	{"string e", append([]string{"0", "1", "2"}, "3"), []string{"0", "1", "2", "3"}},
	{"string f", append([]string{"0", "1", "2"}, "3", "4", "5"), []string{"0", "1", "2", "3", "4", "5"}},

	{"string g", append([]string{}, []string{"0"}...), []string{"0"}},
	{"string h", append([]string{}, []string{"0", "1", "2", "3"}...), []string{"0", "1", "2", "3"}},

	{"string i", append([]string{"0", "1", "2"}, []string{"3"}...), []string{"0", "1", "2", "3"}},
	{"string j", append([]string{"0", "1", "2"}, []string{"3", "4", "5"}...), []string{"0", "1", "2", "3", "4", "5"}},
}

func verifyStruct() {
	type T struct {
		a, b, c string
	}
	type S []T
	e := make(S, 100)
	for i := range e {
		e[i] = T{"foo", fmt.Sprintf("%d", i), "bar"}
	}

	verify("struct a", append(S{}), S{})
	verify("struct b", append(S{}, e[0]), e[0:1])
	verify("struct c", append(S{}, e[0], e[1], e[2]), e[0:3])

	verify("struct d", append(e[0:1]), e[0:1])
	verify("struct e", append(e[0:1], e[1]), e[0:2])
	verify("struct f", append(e[0:1], e[1], e[2], e[3]), e[0:4])

	verify("struct g", append(e[0:3]), e[0:3])
	verify("struct h", append(e[0:3], e[3]), e[0:4])
	verify("struct i", append(e[0:3], e[3], e[4], e[5], e[6]), e[0:7])

	for i := range e {
		verify("struct j", append(S{}, e[0:i]...), e[0:i])
		input := make(S, i)
		copy(input, e[0:i])
		verify("struct k", append(input, e[i:]...), e)
		verify("struct k - input modified", input, e[0:i])
	}

	s := make(S, 10, 20)
	r := make(S, len(s)+len(e))
	for i, x := range e {
		r[len(s)+i] = x
	}
	verify("struct l", append(s), s)
	verify("struct m", append(s, e...), r)
}

func verifyInterface() {
	type T interface{}
	type S []T
	e := make(S, 100)
	for i := range e {
		switch i % 4 {
		case 0:
			e[i] = i
		case 1:
			e[i] = "foo"
		case 2:
			e[i] = fmt.Sprintf("%d", i)
		case 3:
			e[i] = float64(i)
		}
	}

	verify("interface a", append(S{}), S{})
	verify("interface b", append(S{}, e[0]), e[0:1])
	verify("interface c", append(S{}, e[0], e[1], e[2]), e[0:3])

	verify("interface d", append(e[0:1]), e[0:1])
	verify("interface e", append(e[0:1], e[1]), e[0:2])
	verify("interface f", append(e[0:1], e[1], e[2], e[3]), e[0:4])

	verify("interface g", append(e[0:3]), e[0:3])
	verify("interface h", append(e[0:3], e[3]), e[0:4])
	verify("interface i", append(e[0:3], e[3], e[4], e[5], e[6]), e[0:7])

	for i := range e {
		verify("interface j", append(S{}, e[0:i]...), e[0:i])
		input := make(S, i)
		copy(input, e[0:i])
		verify("interface k", append(input, e[i:]...), e)
		verify("interface k - input modified", input, e[0:i])
	}

	s := make(S, 10, 20)
	r := make(S, len(s)+len(e))
	for i, x := range e {
		r[len(s)+i] = x
	}
	verify("interface l", append(s), s)
	verify("interface m", append(s, e...), r)
}
<commit_msg>test: test append with two different named types with same element type<commit_after>// $G $F.go && $L $F.$A && ./$A.out

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Semi-exhaustive test for append()

package main

import (
	"fmt"
	"reflect"
)

func verify(name string, result, expected interface{}) {
	if !reflect.DeepEqual(result, expected) {
		panic(name)
	}
}

func main() {
	for _, t := range tests {
		verify(t.name, t.result, t.expected)
	}
	verifyStruct()
	verifyInterface()
	verifyType()
}

var tests = []struct {
	name             string
	result, expected interface{}
}{
	{"bool a", append([]bool{}), []bool{}},
	{"bool b", append([]bool{}, true), []bool{true}},
	{"bool c", append([]bool{}, true, false, true, true), []bool{true, false, true, true}},

	{"bool d", append([]bool{true, false, true}), []bool{true, false, true}},
	{"bool e", append([]bool{true, false, true}, false), []bool{true, false, true, false}},
	{"bool f", append([]bool{true, false, true}, false, false, false), []bool{true, false, true, false, false, false}},

	{"bool g", append([]bool{}, []bool{true}...), []bool{true}},
	{"bool h", append([]bool{}, []bool{true, false, true, false}...), []bool{true, false, true, false}},

	{"bool i", append([]bool{true, false, true}, []bool{true}...), []bool{true, false, true, true}},
	{"bool j", append([]bool{true, false, true}, []bool{true, true, true}...), []bool{true, false, true, true, true, true}},

	{"byte a", append([]byte{}), []byte{}},
	{"byte b", append([]byte{}, 0), []byte{0}},
	{"byte c", append([]byte{}, 0, 1, 2, 3), []byte{0, 1, 2, 3}},

	{"byte d", append([]byte{0, 1, 2}), []byte{0, 1, 2}},
	{"byte e", append([]byte{0, 1, 2}, 3), []byte{0, 1, 2, 3}},
	{"byte f", append([]byte{0, 1, 2}, 3, 4, 5), []byte{0, 1, 2, 3, 4, 5}},

	{"byte g", append([]byte{}, []byte{0}...), []byte{0}},
	{"byte h", append([]byte{}, []byte{0, 1, 2, 3}...), []byte{0, 1, 2, 3}},

	{"byte i", append([]byte{0, 1, 2}, []byte{3}...), []byte{0, 1, 2, 3}},
	{"byte j", append([]byte{0, 1, 2}, []byte{3, 4, 5}...), []byte{0, 1, 2, 3, 4, 5}},

	{"bytestr a", append([]byte{}, "0"...), []byte("0")},
	{"bytestr b", append([]byte{}, "0123"...), []byte("0123")},

	{"bytestr c", append([]byte("012"), "3"...), []byte("0123")},
	{"bytestr d", append([]byte("012"), "345"...), []byte("012345")},

	{"int16 a", append([]int16{}), []int16{}},
	{"int16 b", append([]int16{}, 0), []int16{0}},
	{"int16 c", append([]int16{}, 0, 1, 2, 3), []int16{0, 1, 2, 3}},

	{"int16 d", append([]int16{0, 1, 2}), []int16{0, 1, 2}},
	{"int16 e", append([]int16{0, 1, 2}, 3), []int16{0, 1, 2, 3}},
	{"int16 f", append([]int16{0, 1, 2}, 3, 4, 5), []int16{0, 1, 2, 3, 4, 5}},

	{"int16 g", append([]int16{}, []int16{0}...), []int16{0}},
	{"int16 h", append([]int16{}, []int16{0, 1, 2, 3}...), []int16{0, 1, 2, 3}},

	{"int16 i", append([]int16{0, 1, 2}, []int16{3}...), []int16{0, 1, 2, 3}},
	{"int16 j", append([]int16{0, 1, 2}, []int16{3, 4, 5}...), []int16{0, 1, 2, 3, 4, 5}},

	{"uint32 a", append([]uint32{}), []uint32{}},
	{"uint32 b", append([]uint32{}, 0), []uint32{0}},
	{"uint32 c", append([]uint32{}, 0, 1, 2, 3), []uint32{0, 1, 2, 3}},

	{"uint32 d", append([]uint32{0, 1, 2}), []uint32{0, 1, 2}},
	{"uint32 e", append([]uint32{0, 1, 2}, 3), []uint32{0, 1, 2, 3}},
	{"uint32 f", append([]uint32{0, 1, 2}, 3, 4, 5), []uint32{0, 1, 2, 3, 4, 5}},

	{"uint32 g", append([]uint32{}, []uint32{0}...), []uint32{0}},
	{"uint32 h", append([]uint32{}, []uint32{0, 1, 2, 3}...), []uint32{0, 1, 2, 3}},

	{"uint32 i", append([]uint32{0, 1, 2}, []uint32{3}...), []uint32{0, 1, 2, 3}},
	{"uint32 j", append([]uint32{0, 1, 2}, []uint32{3, 4, 5}...), []uint32{0, 1, 2, 3, 4, 5}},

	{"float64 a", append([]float64{}), []float64{}},
	{"float64 b", append([]float64{}, 0), []float64{0}},
	{"float64 c", append([]float64{}, 0, 1, 2, 3), []float64{0, 1, 2, 3}},

	{"float64 d", append([]float64{0, 1, 2}), []float64{0, 1, 2}},
	{"float64 e", append([]float64{0, 1, 2}, 3), []float64{0, 1, 2, 3}},
	{"float64 f", append([]float64{0, 1, 2}, 3, 4, 5), []float64{0, 1, 2, 3, 4, 5}},

	{"float64 g", append([]float64{}, []float64{0}...), []float64{0}},
	{"float64 h", append([]float64{}, []float64{0, 1, 2, 3}...), []float64{0, 1, 2, 3}},

	{"float64 i", append([]float64{0, 1, 2}, []float64{3}...), []float64{0, 1, 2, 3}},
	{"float64 j", append([]float64{0, 1, 2}, []float64{3, 4, 5}...), []float64{0, 1, 2, 3, 4, 5}},

	{"complex128 a", append([]complex128{}), []complex128{}},
	{"complex128 b", append([]complex128{}, 0), []complex128{0}},
	{"complex128 c", append([]complex128{}, 0, 1, 2, 3), []complex128{0, 1, 2, 3}},

	{"complex128 d", append([]complex128{0, 1, 2}), []complex128{0, 1, 2}},
	{"complex128 e", append([]complex128{0, 1, 2}, 3), []complex128{0, 1, 2, 3}},
	{"complex128 f", append([]complex128{0, 1, 2}, 3, 4, 5), []complex128{0, 1, 2, 3, 4, 5}},

	{"complex128 g", append([]complex128{}, []complex128{0}...), []complex128{0}},
	{"complex128 h", append([]complex128{}, []complex128{0, 1, 2, 3}...), []complex128{0, 1, 2, 3}},

	{"complex128 i", append([]complex128{0, 1, 2}, []complex128{3}...), []complex128{0, 1, 2, 3}},
	{"complex128 j", append([]complex128{0, 1, 2}, []complex128{3, 4, 5}...), []complex128{0, 1, 2, 3, 4, 5}},

	{"string a", append([]string{}), []string{}},
	{"string b", append([]string{}, "0"), []string{"0"}},
	{"string c", append([]string{}, "0", "1", "2", "3"), []string{"0", "1", "2", "3"}},

	{"string d", append([]string{"0", "1", "2"}), []string{"0", "1", "2"}},
	{"string e", append([]string{"0", "1", "2"}, "3"), []string{"0", "1", "2", "3"}},
	{"string f", append([]string{"0", "1", "2"}, "3", "4", "5"), []string{"0", "1", "2", "3", "4", "5"}},

	{"string g", append([]string{}, []string{"0"}...), []string{"0"}},
	{"string h", append([]string{}, []string{"0", "1", "2", "3"}...), []string{"0", "1", "2", "3"}},

	{"string i", append([]string{"0", "1", "2"}, []string{"3"}...), []string{"0", "1", "2", "3"}},
	{"string j", append([]string{"0", "1", "2"}, []string{"3", "4", "5"}...), []string{"0", "1", "2", "3", "4", "5"}},
}

func verifyStruct() {
	type T struct {
		a, b, c string
	}
	type S []T
	e := make(S, 100)
	for i := range e {
		e[i] = T{"foo", fmt.Sprintf("%d", i), "bar"}
	}

	verify("struct a", append(S{}), S{})
	verify("struct b", append(S{}, e[0]), e[0:1])
	verify("struct c", append(S{}, e[0], e[1], e[2]), e[0:3])

	verify("struct d", append(e[0:1]), e[0:1])
	verify("struct e", append(e[0:1], e[1]), e[0:2])
	verify("struct f", append(e[0:1], e[1], e[2], e[3]), e[0:4])

	verify("struct g", append(e[0:3]), e[0:3])
	verify("struct h", append(e[0:3], e[3]), e[0:4])
	verify("struct i", append(e[0:3], e[3], e[4], e[5], e[6]), e[0:7])

	for i := range e {
		verify("struct j", append(S{}, e[0:i]...), e[0:i])
		input := make(S, i)
		copy(input, e[0:i])
		verify("struct k", append(input, e[i:]...), e)
		verify("struct k - input modified", input, e[0:i])
	}

	s := make(S, 10, 20)
	r := make(S, len(s)+len(e))
	for i, x := range e {
		r[len(s)+i] = x
	}
	verify("struct l", append(s), s)
	verify("struct m", append(s, e...), r)
}

func verifyInterface() {
	type T interface{}
	type S []T
	e := make(S, 100)
	for i := range e {
		switch i % 4 {
		case 0:
			e[i] = i
		case 1:
			e[i] = "foo"
		case 2:
			e[i] = fmt.Sprintf("%d", i)
		case 3:
			e[i] = float64(i)
		}
	}

	verify("interface a", append(S{}), S{})
	verify("interface b", append(S{}, e[0]), e[0:1])
	verify("interface c", append(S{}, e[0], e[1], e[2]), e[0:3])

	verify("interface d", append(e[0:1]), e[0:1])
	verify("interface e", append(e[0:1], e[1]), e[0:2])
	verify("interface f", append(e[0:1], e[1], e[2], e[3]), e[0:4])

	verify("interface g", append(e[0:3]), e[0:3])
	verify("interface h", append(e[0:3], e[3]), e[0:4])
	verify("interface i", append(e[0:3], e[3], e[4], e[5], e[6]), e[0:7])

	for i := range e {
		verify("interface j", append(S{}, e[0:i]...), e[0:i])
		input := make(S, i)
		copy(input, e[0:i])
		verify("interface k", append(input, e[i:]...), e)
		verify("interface k - input modified", input, e[0:i])
	}

	s := make(S, 10, 20)
	r := make(S, len(s)+len(e))
	for i, x := range e {
		r[len(s)+i] = x
	}
	verify("interface l", append(s), s)
	verify("interface m", append(s, e...), r)
}

type T1 []int
type T2 []int

func verifyType() {
	// The second argument to append has type []E where E is the
	// element type of the first argument.  Test that the compiler
	// accepts two slice types that meet that requirement but are
	// not assignment compatible.  The return type of append is
	// the type of the first argument.
	t1 := T1{1}
	t2 := T2{2}
	verify("T1", append(t1, t2...), T1{1, 2})
}
<|endoftext|>


<commit_before>package test

import (
	"fmt"
	"net"
	"sync"
	"time"
)

// bridgeConn is a net.Conn that represents an endpoint of the bridge.
type bridgeConn struct {
	br       *Bridge
	id       int
	isClosed bool
	mutex    sync.RWMutex
	readCh   chan []byte
}

// Read reads data, block until the data becomes available.
func (conn *bridgeConn) Read(b []byte) (int, error) {
	if data, ok := <-conn.readCh; ok {
		n := copy(b, data)
		return n, nil
	}
	return 0, fmt.Errorf("bridgeConn closed")
}

// Write writes data to the bridge.
func (conn *bridgeConn) Write(b []byte) (int, error) {
	conn.br.Push(b, conn.id)
	return len(b), nil
}

// Close closes the bridge (releases resources used).
func (conn *bridgeConn) Close() error {
	conn.mutex.Lock()
	defer conn.mutex.Unlock()

	if conn.isClosed {
		return fmt.Errorf("bridge has already been closed")
	}

	conn.isClosed = true
	close(conn.readCh)
	return nil
}

// LocalAddr is not used
func (conn *bridgeConn) LocalAddr() net.Addr { return nil }

// RemoteAddr is not used
func (conn *bridgeConn) RemoteAddr() net.Addr { return nil }

// SetDeadline is not used
func (conn *bridgeConn) SetDeadline(t time.Time) error { return nil }

// SetReadDeadline is not used
func (conn *bridgeConn) SetReadDeadline(t time.Time) error { return nil }

// SetWriteDeadline is not used
func (conn *bridgeConn) SetWriteDeadline(t time.Time) error { return nil }

// Bridge represents a network between the two endpoints.
type Bridge struct {
	mutex sync.RWMutex
	conn0 *bridgeConn
	conn1 *bridgeConn

	queue0to1 [][]byte
	queue1to0 [][]byte
}

func inverse(s [][]byte) error {
	if len(s) < 2 {
		return fmt.Errorf("inverse requires more than one item in the array")
	}

	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return nil
}

// drop n packets from the slice starting from offset
func drop(s [][]byte, offset, n int) [][]byte {
	if offset+n > len(s) {
		n = len(s) - offset
	}
	return append(s[:offset], s[offset+n:]...)
}

// NewBridge creates a new bridge with two endpoints.
func NewBridge() *Bridge {
	br := &Bridge{
		queue0to1: make([][]byte, 0),
		queue1to0: make([][]byte, 0),
	}

	br.conn0 = &bridgeConn{
		br:     br,
		id:     0,
		readCh: make(chan []byte),
	}
	br.conn1 = &bridgeConn{
		br:     br,
		id:     1,
		readCh: make(chan []byte),
	}

	return br
}

// GetConn0 returns an endpoint of the bridge, conn0.
func (br *Bridge) GetConn0() net.Conn {
	return br.conn0
}

// GetConn1 returns an endpoint of the bridge, conn1.
func (br *Bridge) GetConn1() net.Conn {
	return br.conn1
}

// Push pushes a packet into the specified queue.
func (br *Bridge) Push(d []byte, fromID int) {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	if fromID == 0 {
		br.queue0to1 = append(br.queue0to1, d)
	} else {
		br.queue1to0 = append(br.queue1to0, d)
	}
}

// Reorder inverses the order of packets currently in the specified queue.
func (br *Bridge) Reorder(fromID int) error {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	if fromID == 0 {
		return inverse(br.queue0to1)
	}
	return inverse(br.queue1to0)
}

// Drop drops the specified number of packets from the given offset index
// of the specified queue.
func (br *Bridge) Drop(fromID, offset, n int) {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	if fromID == 0 {
		br.queue0to1 = drop(br.queue0to1, offset, n)
	} else {
		br.queue1to0 = drop(br.queue1to0, offset, n)
	}
}

// Tick attempts to hand a packet from the queue for each directions, to readers,
// if there are waiting on the queue. If there's no reader, it will return
// immediately.
func (br *Bridge) Tick() int {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	var n int

	if len(br.queue0to1) > 0 {
		select {
		case br.conn1.readCh <- br.queue0to1[0]:
			n++
			//fmt.Printf("conn1 received data (%d bytes)\n", len(br.queue0to1[0]))
			br.queue0to1 = br.queue0to1[1:]
		default:
		}
	}

	if len(br.queue1to0) > 0 {
		select {
		case br.conn0.readCh <- br.queue1to0[0]:
			n++
			//fmt.Printf("conn0 received data (%d bytes)\n", len(br.queue1to0[0]))
			br.queue1to0 = br.queue1to0[1:]
		default:
		}
	}

	return n
}

// Process repeats tick() calls until no more outstanding packet in the queues.
func (br *Bridge) Process() {
	for {
		time.Sleep(10 * time.Millisecond)
		n := br.Tick()
		if n == 0 {
			break
		}
	}
}
<commit_msg>Add lossChance to bridge<commit_after>package test

import (
	"fmt"
	"math/rand"
	"net"
	"sync"
	"time"
)

// bridgeConn is a net.Conn that represents an endpoint of the bridge.
type bridgeConn struct {
	br         *Bridge
	id         int
	isClosed   bool
	mutex      sync.RWMutex
	readCh     chan []byte
	lossChance int
}

// Read reads data, block until the data becomes available.
func (conn *bridgeConn) Read(b []byte) (int, error) {
	if data, ok := <-conn.readCh; ok {
		n := copy(b, data)
		return n, nil
	}
	return 0, fmt.Errorf("bridgeConn closed")
}

// Write writes data to the bridge.
func (conn *bridgeConn) Write(b []byte) (int, error) {
	if rand.Intn(100) < conn.lossChance {
		return len(b), nil
	}

	conn.br.Push(b, conn.id)
	return len(b), nil
}

// Close closes the bridge (releases resources used).
func (conn *bridgeConn) Close() error {
	conn.mutex.Lock()
	defer conn.mutex.Unlock()

	if conn.isClosed {
		return fmt.Errorf("bridge has already been closed")
	}

	conn.isClosed = true
	close(conn.readCh)
	return nil
}

// LocalAddr is not used
func (conn *bridgeConn) LocalAddr() net.Addr { return nil }

// RemoteAddr is not used
func (conn *bridgeConn) RemoteAddr() net.Addr { return nil }

// SetDeadline is not used
func (conn *bridgeConn) SetDeadline(t time.Time) error { return nil }

// SetReadDeadline is not used
func (conn *bridgeConn) SetReadDeadline(t time.Time) error { return nil }

// SetWriteDeadline is not used
func (conn *bridgeConn) SetWriteDeadline(t time.Time) error { return nil }

// Bridge represents a network between the two endpoints.
type Bridge struct {
	mutex sync.RWMutex
	conn0 *bridgeConn
	conn1 *bridgeConn

	queue0to1 [][]byte
	queue1to0 [][]byte
}

func inverse(s [][]byte) error {
	if len(s) < 2 {
		return fmt.Errorf("inverse requires more than one item in the array")
	}

	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return nil
}

// drop n packets from the slice starting from offset
func drop(s [][]byte, offset, n int) [][]byte {
	if offset+n > len(s) {
		n = len(s) - offset
	}
	return append(s[:offset], s[offset+n:]...)
}

// NewBridge creates a new bridge with two endpoints.
func NewBridge() *Bridge {
	br := &Bridge{
		queue0to1: make([][]byte, 0),
		queue1to0: make([][]byte, 0),
	}

	br.conn0 = &bridgeConn{
		br:     br,
		id:     0,
		readCh: make(chan []byte),
	}
	br.conn1 = &bridgeConn{
		br:     br,
		id:     1,
		readCh: make(chan []byte),
	}

	return br
}

// GetConn0 returns an endpoint of the bridge, conn0.
func (br *Bridge) GetConn0() net.Conn {
	return br.conn0
}

// GetConn1 returns an endpoint of the bridge, conn1.
func (br *Bridge) GetConn1() net.Conn {
	return br.conn1
}

// Push pushes a packet into the specified queue.
func (br *Bridge) Push(d []byte, fromID int) {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	if fromID == 0 {
		br.queue0to1 = append(br.queue0to1, d)
	} else {
		br.queue1to0 = append(br.queue1to0, d)
	}
}

// Reorder inverses the order of packets currently in the specified queue.
func (br *Bridge) Reorder(fromID int) error {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	if fromID == 0 {
		return inverse(br.queue0to1)
	}
	return inverse(br.queue1to0)
}

// Drop drops the specified number of packets from the given offset index
// of the specified queue.
func (br *Bridge) Drop(fromID, offset, n int) {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	if fromID == 0 {
		br.queue0to1 = drop(br.queue0to1, offset, n)
	} else {
		br.queue1to0 = drop(br.queue1to0, offset, n)
	}
}

// Tick attempts to hand a packet from the queue for each directions, to readers,
// if there are waiting on the queue. If there's no reader, it will return
// immediately.
func (br *Bridge) Tick() int {
	br.mutex.Lock()
	defer br.mutex.Unlock()

	var n int

	if len(br.queue0to1) > 0 {
		select {
		case br.conn1.readCh <- br.queue0to1[0]:
			n++
			//fmt.Printf("conn1 received data (%d bytes)\n", len(br.queue0to1[0]))
			br.queue0to1 = br.queue0to1[1:]
		default:
		}
	}

	if len(br.queue1to0) > 0 {
		select {
		case br.conn0.readCh <- br.queue1to0[0]:
			n++
			//fmt.Printf("conn0 received data (%d bytes)\n", len(br.queue1to0[0]))
			br.queue1to0 = br.queue1to0[1:]
		default:
		}
	}

	return n
}

// Process repeats tick() calls until no more outstanding packet in the queues.
func (br *Bridge) Process() {
	for {
		time.Sleep(10 * time.Millisecond)
		n := br.Tick()
		if n == 0 {
			break
		}
	}
}

// SetLossChance sets the probability of writes being discard (to introduce artificial loss)
func (br *Bridge) SetLossChance(chance int) error {
	if chance > 100 || chance < 0 {
		return fmt.Errorf("loss must be < 100 && > 0")
	}

	rand.Seed(time.Now().UTC().UnixNano())
	br.conn0.lossChance = chance
	br.conn1.lossChance = chance
	return nil
}
<|endoftext|>


<commit_before>package push

import "fmt"

type Platform int

const (
	IOS     Platform = 1
	Android Platform = 2
)

// Token represents a token associated to a uuid (typically a user)
type Token struct {
	UUID     string   `json:"uuid"`
	Value    string   `json:"token"`
	Platform Platform `json:"platform"`
}

// Validate returns an error if the Token is not valid
func (t *Token) Validate() error {
	if t.UUID == "" {
		return fmt.Errorf("'uuid' is required")
	}
	if t.Value == "" {
		return fmt.Errorf("'token' is required")
	}
	if t.Platform != IOS && t.Platform != Android {
		return fmt.Errorf("'platform' is required and must be 1 (iOS) or 2 (Android)")
	}
	return nil
}

type TokenRepository interface {
	FindTokenWithValue(value string) (Token, error)
	RemoveTokenWithValue(value string) error
	SaveToken(t Token) error
}
<commit_msg>Add the language field to Token<commit_after>package push

import "fmt"

// Platform is an enum representing supported Push platforms
type Platform int

const (
	// IOS represents APNS platform
	IOS Platform = 1
	// Android represents GCM platform
	Android Platform = 2
)

// Token represents a token associated to a uuid (typically a user)
type Token struct {
	UUID     string   `json:"uuid"`
	Value    string   `json:"token"`
	Platform Platform `json:"platform"`
	Language string   `json:"language"`
}

// Validate returns an error if the Token is not valid
func (t *Token) Validate() error {
	if t.UUID == "" {
		return fmt.Errorf("'uuid' is required")
	}
	if t.Value == "" {
		return fmt.Errorf("'token' is required")
	}
	if t.Platform != IOS && t.Platform != Android {
		return fmt.Errorf("'platform' is required and must be 1 (iOS) or 2 (Android)")
	}
	if t.Language == "" {
		return fmt.Errorf("'language' is required")
	}
	return nil
}

// TokenRepository defines the behavior for interacting with tokens
type TokenRepository interface {
	FindTokenWithValue(value string) (Token, error)
	RemoveTokenWithValue(value string) error
	SaveToken(t Token) error
}
<|endoftext|>


<commit_before>package errors

type themisError struct {
	msg string
}

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

func New(msg string) *themisError {
	return &themisError{msg}
}
<commit_msg>Add custom error (themisError) type for gothemis<commit_after>package errors

type ThemisError struct {
	msg string
}

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

func New(msg string) *ThemisError {
	return &ThemisError{msg}
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "strings"
import "os"
import "log"
import "bufio"
import "github.com/jacobsa/go-serial/serial"
import "github.com/jessevdk/go-flags"

func main() {

	var opts struct {
		BaudRate uint   `short:"b" long:"baud" default:"9600" description:"Baud rate for transimission"`
		PortName string `short:"p" long:"port" required:"true" description:"Serial port to read"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(0)
		log.Fatal(err)
	}

	options := serial.OpenOptions{
		PortName:        opts.PortName,
		BaudRate:        opts.BaudRate,
		DataBits:        8,
		StopBits:        1,
		MinimumReadSize: 4,
	}

	// Open the port.
	port, err := serial.Open(options)
	if err != nil {
		log.Fatalf("serial.Open: %v", err)
	}

	// Make sure to close it later.
	defer port.Close()

	scanner := bufio.NewScanner(port)
	for scanner.Scan() {
		line := scanner.Text()
		fmt.Println(line)
		if strings.HasPrefix(line, "Total:") {
			os.Exit(0)
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Removed tester not used with eva<commit_after><|endoftext|>


<commit_before>package tuikit

import (
	"fmt"
	"sync"

	"time"

	termbox "github.com/nsf/termbox-go"
	"github.com/nsf/tulib"
	log "github.com/sgeb/go-sglog"
)

type Painter interface {
	PaintTo(buffer *tulib.Buffer, rect tulib.Rect) error
	SetPaintSubscriber(cb func())
}

type Responder interface {
	// HandleEvent should set Event.Handled to true if it was
	// handled so that the main loop knows to ignores it
	HandleEvent(*Event)
	SetCursorPainter(cb func(Point))
}

type Event struct {
	*termbox.Event
	Handled bool
}

var (
	rootPainter    Painter
	rootBuffer     tulib.Buffer
	firstResponder Responder

	// Event polling channel
	Events chan Event = make(chan Event, 20)

	// Controls event polling
	internalEvents chan termbox.Event = make(chan termbox.Event, 20)
	stopPolling    chan struct{}      = make(chan struct{}, 1)

	// Lock on screen drawing
	mutex sync.Mutex

	fpsCounter    *FpsCounter
	lastPaintTime time.Time
	framesSkipped uint64
)

func Init() error {
	err := termbox.Init()
	if err != nil {
		return fmt.Errorf("Could not init terminal: %v", err)
	}

	err = clearWithDefaultColors()
	if err != nil {
		return fmt.Errorf("Could not clear terminal: %v", err)
	}
	termbox.SetInputMode(termbox.InputAlt)
	hideCursor()

	internalEventProxying()
	StartEventPolling()

	fpsCounter = NewFpsCounter(time.Second)
	go func() {
		for fps := range fpsCounter.Fps {
			log.Debug.Printf("FPS: %v (%v frames skipped)", fps, framesSkipped)
			framesSkipped = 0
		}
	}()

	return nil
}

func internalEventProxying() {
	go func() {
		for {
			tbEvent := <-internalEvents
			ev := Event{Event: &tbEvent, Handled: false}

			switch {
			case ev.Type == termbox.EventResize:
				log.Debug.Println("Received Resize event, clearing screen " +
					"and setting new size")
				clearWithDefaultColors()
				Paint()
				ev.Handled = true
			case ev.Type == termbox.EventKey && firstResponder != nil:
				firstResponder.HandleEvent(&ev)
			}

			Events <- ev
		}
	}()
}

func StartEventPolling() {
	go func() {
		for {
			select {
			case <-stopPolling:
				return
			case internalEvents <- termbox.PollEvent():
			}
		}
	}()
}

func StopEventPolling() {
	stopPolling <- struct{}{}
}

func SetPainter(p Painter) {
	rootPainter = p
}

func SetFirstResponder(eh Responder) {
	if firstResponder != nil {
		firstResponder.SetCursorPainter(nil)
	}
	if eh != nil {
		eh.SetCursorPainter(func(pos Point) { setCursor(pos) })
	}
	firstResponder = eh
}

func Paint() error {
	if time.Now().Sub(lastPaintTime) < time.Second/10 {
		framesSkipped++
		return fmt.Errorf("Above 10 FPS, skipping frame")
	}

	err := rootPainter.PaintTo(&rootBuffer, rootBuffer.Rect)
	if err != nil {
		return err
	}

	if firstResponder == nil {
		hideCursor()
	}

	err = flush()
	if err != nil {
		return err
	}

	fpsCounter.Ticks <- struct{}{}
	lastPaintTime = time.Now()

	return nil
}

func Sync() error {
	mutex.Lock()
	err := termbox.Sync()
	mutex.Unlock()
	return err
}

func clearRect(buffer *tulib.Buffer, rect tulib.Rect) {
	buffer.Fill(rect, termbox.Cell{Ch: ' '})
}

func clearWithDefaultColors() error {
	return clear(termbox.ColorDefault, termbox.ColorDefault)
}

func clear(fg, bg termbox.Attribute) error {
	mutex.Lock()
	err := termbox.Clear(fg, bg)
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func updateRootBuffer() {
	mutex.Lock()
	rootBuffer = tulib.TermboxBuffer()
	mutex.Unlock()
}

func hideCursor() {
	// Probably a bug: Cursor must be set to valid position before hiding
	setCursor(PointZero)
	termbox.HideCursor()
}

func setCursor(p Point) {
	termbox.SetCursor(p.X, p.Y)
}

func flush() error {
	mutex.Lock()
	err := termbox.Flush()
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func Close() {
	mutex.Lock()
	termbox.Close()
	mutex.Unlock()
}
<commit_msg>Extract paint functionality to bypass FPS throttler<commit_after>package tuikit

import (
	"fmt"
	"sync"

	"time"

	termbox "github.com/nsf/termbox-go"
	"github.com/nsf/tulib"
	log "github.com/sgeb/go-sglog"
)

type Painter interface {
	PaintTo(buffer *tulib.Buffer, rect tulib.Rect) error
	SetPaintSubscriber(cb func())
}

type Responder interface {
	// HandleEvent should set Event.Handled to true if it was
	// handled so that the main loop knows to ignores it
	HandleEvent(*Event)
	SetCursorPainter(cb func(Point))
}

type Event struct {
	*termbox.Event
	Handled bool
}

var (
	rootPainter    Painter
	rootBuffer     tulib.Buffer
	firstResponder Responder

	// Event polling channel
	Events chan Event = make(chan Event, 20)

	// Controls event polling
	internalEvents chan termbox.Event = make(chan termbox.Event, 20)
	stopPolling    chan struct{}      = make(chan struct{}, 1)

	// Lock on screen drawing
	mutex sync.Mutex

	fpsCounter    *FpsCounter
	lastPaintTime time.Time
	framesSkipped uint64
)

func Init() error {
	err := termbox.Init()
	if err != nil {
		return fmt.Errorf("Could not init terminal: %v", err)
	}

	err = clearWithDefaultColors()
	if err != nil {
		return fmt.Errorf("Could not clear terminal: %v", err)
	}
	termbox.SetInputMode(termbox.InputAlt)
	hideCursor()

	internalEventProxying()
	StartEventPolling()

	fpsCounter = NewFpsCounter(time.Second)
	go func() {
		for fps := range fpsCounter.Fps {
			log.Debug.Printf("FPS: %v (%v frames skipped)", fps, framesSkipped)
			framesSkipped = 0
		}
	}()

	return nil
}

func internalEventProxying() {
	go func() {
		for {
			tbEvent := <-internalEvents
			ev := Event{Event: &tbEvent, Handled: false}

			switch {
			case ev.Type == termbox.EventResize:
				log.Debug.Println("Received Resize event, clearing screen " +
					"and setting new size")
				clearWithDefaultColors()
				paintForced()
				ev.Handled = true
			case ev.Type == termbox.EventKey && firstResponder != nil:
				firstResponder.HandleEvent(&ev)
			}

			Events <- ev
		}
	}()
}

func StartEventPolling() {
	go func() {
		for {
			select {
			case <-stopPolling:
				return
			case internalEvents <- termbox.PollEvent():
			}
		}
	}()
}

func StopEventPolling() {
	stopPolling <- struct{}{}
}

func SetPainter(p Painter) {
	rootPainter = p
}

func SetFirstResponder(eh Responder) {
	if firstResponder != nil {
		firstResponder.SetCursorPainter(nil)
	}
	if eh != nil {
		eh.SetCursorPainter(func(pos Point) { setCursor(pos) })
	}
	firstResponder = eh
}

func Paint() error {
	if time.Now().Sub(lastPaintTime) < time.Second/10 {
		framesSkipped++
		return fmt.Errorf("Above 10 FPS, skipping frame")
	}

	return paintForced()
}

func paintForced() error {
	err := rootPainter.PaintTo(&rootBuffer, rootBuffer.Rect)
	if err != nil {
		return err
	}

	if firstResponder == nil {
		hideCursor()
	}

	err = flush()
	if err != nil {
		return err
	}

	fpsCounter.Ticks <- struct{}{}
	lastPaintTime = time.Now()

	return nil
}

func Sync() error {
	mutex.Lock()
	err := termbox.Sync()
	mutex.Unlock()
	return err
}

func clearRect(buffer *tulib.Buffer, rect tulib.Rect) {
	buffer.Fill(rect, termbox.Cell{Ch: ' '})
}

func clearWithDefaultColors() error {
	return clear(termbox.ColorDefault, termbox.ColorDefault)
}

func clear(fg, bg termbox.Attribute) error {
	mutex.Lock()
	err := termbox.Clear(fg, bg)
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func updateRootBuffer() {
	mutex.Lock()
	rootBuffer = tulib.TermboxBuffer()
	mutex.Unlock()
}

func hideCursor() {
	// Probably a bug: Cursor must be set to valid position before hiding
	setCursor(PointZero)
	termbox.HideCursor()
}

func setCursor(p Point) {
	termbox.SetCursor(p.X, p.Y)
}

func flush() error {
	mutex.Lock()
	err := termbox.Flush()
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func Close() {
	mutex.Lock()
	termbox.Close()
	mutex.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"strings"

	"bitbucket.org/ckvist/twilio/twiml"
	"github.com/gorilla/mux"
)

type CallbackResponse struct {
	UUID    string `json:"uuid"`
	Message string `json:"message"`
	Error   string `json:"error"`
}

type SMSResponse struct {
	Sid         string
	DateCreated string
	DateUpdated string
	DateSent    string
	AccountSid  string
	To          string
	From        string
	Body        string
	NumSegments string
	Status      string
	Direction   string
	Price       string
	PriceUnit   string
	ApiVersion  string
	Uri         string
}

func SendSMS(phoneNumber, message, uuid string, dontSendAckRequest bool) {
	var cr SMSResponse

	if uuid != "" {
		log.Println("[", uuid, "]", "Sending SMS to", phoneNumber, "with message:", message)
	} else {
		log.Println("Sending SMS to", phoneNumber, "with message:", message)
	}

	// Generate an int in the range 100 <= n <= 999
	ackReply := rand.Intn(899) + 100

	u := url.Values{}
	u.Set("From", c.Config.Integrations.Twilio.CallFromNumber)
	u.Set("To", phoneNumber)
	if dontSendAckRequest {
		u.Set("Body", message)
	} else {
		u.Set("Body", fmt.Sprint(message, " - Reply with \"", ackReply, "\" to acknowledge"))

	}

	if uuid != "" {
		u.Set("StatusCallback", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/callback"))
	}

	body := *strings.NewReader(u.Encode())

	client := &http.Client{}
	req, _ := http.NewRequest("POST", fmt.Sprint(c.Config.Integrations.Twilio.APIBaseURL, c.Config.Integrations.Twilio.AccountSID, "/Messages.json"), &body)
	req.SetBasicAuth(c.Config.Integrations.Twilio.AccountSID, c.Config.Integrations.Twilio.AuthToken)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println("SendSMS() Request error:", err)
	}

	b, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*20))
	resp.Body.Close()

	err = json.Unmarshal(b, &cr)
	if err != nil {
		log.Fatalln("SendSMS() Error unmarshalling JSON:", err)
	}

	if uuid != "" {
		// We make a conversation key that's a combination of our recipient's phone number and the random 3-digit key
		// that we generated abovee
		conversationKey := fmt.Sprint(cr.To, "::", ackReply)

		NIP.Mu.Lock()
		defer NIP.Mu.Unlock()

		NIP.Conversations[conversationKey] = uuid
	}

}

func MakePhoneCall(phoneNumber, message, uuid string) {
	var cr map[string]interface{}

	log.Println("[", uuid, "] Calling", phoneNumber, "with message:", message)

	u := url.Values{}
	u.Set("From", c.Config.Integrations.Twilio.CallFromNumber)
	u.Set("To", phoneNumber)
	u.Set("Url", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/twiml/notify"))
	// u.Set("StatusCallback", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/callback"))
	// u.Add("StatusCallbackEvent", "ringing")
	// u.Add("StatusCallbackEvent", "answered")
	// u.Add("StatusCallbackEvent", "completed")
	u.Set("IfMachine", "Hangup")
	u.Set("Timeout", "20")
	body := *strings.NewReader(u.Encode())

	client := &http.Client{}
	req, _ := http.NewRequest("POST", fmt.Sprint(c.Config.Integrations.Twilio.APIBaseURL, c.Config.Integrations.Twilio.AccountSID, "/Calls.json"), &body)
	req.SetBasicAuth(c.Config.Integrations.Twilio.AccountSID, c.Config.Integrations.Twilio.AuthToken)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println("MakePhoneCall() Request error:", err)
	}

	b, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*20))
	resp.Body.Close()

	err = json.Unmarshal(b, &cr)
	if err != nil {
		log.Fatalln("MakePhoneCall() Error unmarshalling JSON:", err)
	}

}

func ReceiveSMSReply(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		log.Println("ReceiveSMSReply() r.ParseForm() error:", err)
	}

	recipient := r.FormValue("From")
	if recipient == "" {
		log.Println("ReceiveSMSReply() error: 'From' parameter was not provided in response")
		return
	}

	NIP.Mu.Lock()

	conversationKey := fmt.Sprint(recipient, "::", r.FormValue("Body"))

	if _, exists := NIP.Conversations[conversationKey]; exists {
		uuid := NIP.Conversations[conversationKey]

		log.Println("[", uuid, "]", "Recieved a SMS reply from", recipient, ":", r.FormValue("Body"))

		if _, exists := NIP.Stoppers[uuid]; !exists {
			log.Println("ReceiveSMSReply(): No active notifications for this UUID:", uuid)
			http.Error(w, "", http.StatusNotFound)
			NIP.Mu.Unlock()
			return
		}

		// Delete the conversation key from the in-progress store
		delete(NIP.Conversations, conversationKey)

		// Unlock our mutex so the notification engine can take it
		NIP.Mu.Unlock()

		log.Println("[", uuid, "] Attempting to stop notifications")

		// Attempt to stop the notification by sending the UUID to the notification engine
		stopChan <- uuid

		SendSMS(recipient, "Chicken Little has received your acknowledgment.  Thanks!", uuid, true)

	} else {
		SendSMS(recipient, "I'm sorry but I don't recognize that response.   Please acknowledge with the three-digit code from the notfication you received.", "", true)
	}

}

func ReceiveCallback(w http.ResponseWriter, r *http.Request) {
	var res CallbackResponse

	vars := mux.Vars(r)
	uuid := vars["uuid"]

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	// Stuff will happen

	res = CallbackResponse{
		Message: "Callback received",
		UUID:    uuid,
	}

	json.NewEncoder(w).Encode(res)

}

func ReceiveDigits(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	uuid := vars["uuid"]

	err := r.ParseForm()
	if err != nil {
		log.Println("ReceiveDigits() r.ParseForm() error:", err)
	}

	// Fetch some form values we'll need from Twilio's request
	digits := r.FormValue("Digits")
	callSid := r.FormValue("CallSid")

	// If digits has been set, user has answered the phone and pressed (any) key to acknowledge the message
	if digits != "" {

		if _, exists := NIP.Stoppers[uuid]; !exists {
			log.Println("ReceiveDigits(): No active notifications for this UUID:", uuid)
			http.Error(w, "", http.StatusNotFound)
			return
		}

		// We matched a valid notification-in-progress and the user pressed digits when prompted
		// so we'll do a POST to Twilio that points the call at a TwiML routine that confirms
		// their acknowledgement and sends them on their way.
		u := url.Values{}
		u.Set("Url", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/twiml/acknowledged"))

		body := *strings.NewReader(u.Encode())

		client := &http.Client{}
		req, _ := http.NewRequest("POST", fmt.Sprint(c.Config.Integrations.Twilio.APIBaseURL, c.Config.Integrations.Twilio.AccountSID, "/Calls/", callSid), &body)
		req.SetBasicAuth(c.Config.Integrations.Twilio.AccountSID, c.Config.Integrations.Twilio.AuthToken)
		req.Header.Add("Accept", "application/json")
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

		// Send the POST request
		_, err := client.Do(req)
		if err != nil {
			log.Println("ReceiveDigits() TwiML POST Request error:", err)
		}

		// Attempt to stop the notification by sending the UUID to the notification engine
		stopChan <- uuid
	}
}

func GenerateTwiML(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	uuid := vars["uuid"]
	action := vars["action"]

	resp := twiml.NewResponse()

	switch action {
	case "notify":
		// This is a request for a TwiML script for a standard message notification
		if _, exists := NIP.Stoppers[uuid]; !exists {
			http.Error(w, "No active notifications for this UUID", http.StatusNotFound)
			return
		}

		intro := twiml.Say{
			Voice: "woman",
			Text:  "This is Chicken Little with a message for you.",
		}

		gather := twiml.Gather{
			Action:    fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/digits"),
			Timeout:   15,
			NumDigits: 1,
		}

		theMessage := twiml.Say{
			Voice: "man",
			Text:  NIP.Messages[uuid],
		}

		pressAny := twiml.Say{
			Voice: "woman",
			Text:  "Press any key to acknowledge receipt of this message",
		}

		resp.Action(intro)
		resp.Gather(gather, theMessage, pressAny)

	case "acknowledged":
		// This is a request for the end-of-call wrap-up message
		resp.Action(twiml.Say{
			Voice: "woman",
			Text:  "Thank you. This message has been acknowledged. Goodbye!",
		})
	}

	resp.Send(w)
}
<commit_msg>Document the Twilio interaction<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"strings"

	"bitbucket.org/ckvist/twilio/twiml"
	"github.com/gorilla/mux"
)

type CallbackResponse struct {
	UUID    string `json:"uuid"`
	Message string `json:"message"`
	Error   string `json:"error"`
}

type SMSResponse struct {
	Sid         string
	DateCreated string
	DateUpdated string
	DateSent    string
	AccountSid  string
	To          string
	From        string
	Body        string
	NumSegments string
	Status      string
	Direction   string
	Price       string
	PriceUnit   string
	ApiVersion  string
	Uri         string
}

// Sends an SMS text message to a phone number using the Twilio API,
// optionally including a method for acknowledging receipt of the message.
func SendSMS(phoneNumber, message, uuid string, dontSendAckRequest bool) {
	var cr SMSResponse

	if uuid != "" {
		log.Println("[", uuid, "]", "Sending SMS to", phoneNumber, "with message:", message)
	} else {
		log.Println("Sending SMS to", phoneNumber, "with message:", message)
	}

	// Generate an int in the range 100 <= n <= 999
	ackReply := rand.Intn(899) + 100

	// Builds a form that will be posted to Twilio API
	u := url.Values{}
	u.Set("From", c.Config.Integrations.Twilio.CallFromNumber)
	u.Set("To", phoneNumber)

	// Sometimes we send texts that don't require ACKing.  This handles that.
	if dontSendAckRequest {
		u.Set("Body", message)
	} else {
		u.Set("Body", fmt.Sprint(message, " - Reply with \"", ackReply, "\" to acknowledge"))

	}

	// If we have a UUID, we can request status callbacks for this SMS
	if uuid != "" {
		u.Set("StatusCallback", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/callback"))
	}

	// Post the request to the Twilio API
	body := *strings.NewReader(u.Encode())
	client := &http.Client{}
	req, _ := http.NewRequest("POST", fmt.Sprint(c.Config.Integrations.Twilio.APIBaseURL, c.Config.Integrations.Twilio.AccountSID, "/Messages.json"), &body)
	req.SetBasicAuth(c.Config.Integrations.Twilio.AccountSID, c.Config.Integrations.Twilio.AuthToken)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println("SendSMS() Request error:", err)
	}

	// Get the response
	b, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*20))
	resp.Body.Close()

	err = json.Unmarshal(b, &cr)
	if err != nil {
		log.Fatalln("SendSMS() Error unmarshalling JSON:", err)
	}

	if uuid != "" {
		// We create conversation key that's a combination of our recipient's phone number and the random 3-digit key
		// that we generated above
		conversationKey := fmt.Sprint(cr.To, "::", ackReply)

		NIP.Mu.Lock()
		defer NIP.Mu.Unlock()

		NIP.Conversations[conversationKey] = uuid
	}

}

// Makes a phone call to a phone number using the Twilio API.  Sends Twilio a URL for
// retrieving the TwiML that defines the interaction in the call.
func MakePhoneCall(phoneNumber, message, uuid string) {
	var cr map[string]interface{}

	log.Println("[", uuid, "] Calling", phoneNumber, "with message:", message)

	// Build a form that we'll POST to the Twilio API to initiate a phone call
	u := url.Values{}
	u.Set("From", c.Config.Integrations.Twilio.CallFromNumber)
	u.Set("To", phoneNumber)
	u.Set("Url", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/twiml/notify"))
	// Optional status callbacks are enabled below...
	// u.Set("StatusCallback", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/callback"))
	// u.Add("StatusCallbackEvent", "ringing")
	// u.Add("StatusCallbackEvent", "answered")
	// u.Add("StatusCallbackEvent", "completed")
	u.Set("IfMachine", "Hangup")
	u.Set("Timeout", "20")
	body := *strings.NewReader(u.Encode())

	// Send our form to Twilio
	client := &http.Client{}
	req, _ := http.NewRequest("POST", fmt.Sprint(c.Config.Integrations.Twilio.APIBaseURL, c.Config.Integrations.Twilio.AccountSID, "/Calls.json"), &body)
	req.SetBasicAuth(c.Config.Integrations.Twilio.AccountSID, c.Config.Integrations.Twilio.AuthToken)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println("MakePhoneCall() Request error:", err)
	}

	b, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*20))
	resp.Body.Close()

	// We get the response back but don't currently do anything with it.   TO DO: implement error handling
	err = json.Unmarshal(b, &cr)
	if err != nil {
		log.Fatalln("MakePhoneCall() Error unmarshalling JSON:", err)
	}

}

// Receives the SMS reply callback from Twilio and deletes the notification if the
// response text matches the code sent with the original SMS notification
func ReceiveSMSReply(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		log.Println("ReceiveSMSReply() r.ParseForm() error:", err)
	}

	// We should have a "From" parameter being passed from Twilio
	recipient := r.FormValue("From")
	if recipient == "" {
		log.Println("ReceiveSMSReply() error: 'From' parameter was not provided in response")
		return
	}

	NIP.Mu.Lock()

	// Our conversation key is a combination of the recipient's phone number and the 3-digit code
	// that they sent in reply
	conversationKey := fmt.Sprint(recipient, "::", r.FormValue("Body"))

	// See if this SMS conversation is active.  If it is, look up the UUID with the conversation key.
	if _, exists := NIP.Conversations[conversationKey]; exists {
		uuid := NIP.Conversations[conversationKey]

		log.Println("[", uuid, "]", "Recieved a SMS reply from", recipient, ":", r.FormValue("Body"))

		if _, exists := NIP.Stoppers[uuid]; !exists {
			log.Println("ReceiveSMSReply(): No active notifications for this UUID:", uuid)
			http.Error(w, "", http.StatusNotFound)
			NIP.Mu.Unlock()
			return
		}

		// Delete the conversation key from the in-progress store
		delete(NIP.Conversations, conversationKey)

		// Unlock our mutex so the notification engine can take it
		NIP.Mu.Unlock()

		log.Println("[", uuid, "] Attempting to stop notifications")

		// Attempt to stop the notification by sending the UUID to the notification engine
		stopChan <- uuid

		SendSMS(recipient, "Chicken Little has received your acknowledgment.  Thanks!", uuid, true)

	} else {
		SendSMS(recipient, "I'm sorry but I don't recognize that response.   Please acknowledge with the three-digit code from the notfication you received.", "", true)
	}

}

// Receives call progress callbacks from the Twilio API.  Not currently used.
// May be used for Websocket interface in the future.
func ReceiveCallback(w http.ResponseWriter, r *http.Request) {
	var res CallbackResponse

	vars := mux.Vars(r)
	uuid := vars["uuid"]

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	// Stuff will happen

	res = CallbackResponse{
		Message: "Callback received",
		UUID:    uuid,
	}

	json.NewEncoder(w).Encode(res)

}

// Receives digits pressed during a phone call via callback by the Twilio API.
// Stops the notification if the user pressed any keys.
func ReceiveDigits(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	uuid := vars["uuid"]

	err := r.ParseForm()
	if err != nil {
		log.Println("ReceiveDigits() r.ParseForm() error:", err)
	}

	// Fetch some form values we'll need from Twilio's request
	digits := r.FormValue("Digits")
	callSid := r.FormValue("CallSid")

	// If digits has been set, user has answered the phone and pressed (any) key to acknowledge the message
	if digits != "" {

		if _, exists := NIP.Stoppers[uuid]; !exists {
			log.Println("ReceiveDigits(): No active notifications for this UUID:", uuid)
			http.Error(w, "", http.StatusNotFound)
			return
		}

		// We matched a valid notification-in-progress and the user pressed digits when prompted
		// so we'll do a POST to Twilio that points the call at a TwiML routine that confirms
		// their acknowledgement and sends them on their way.
		u := url.Values{}
		u.Set("Url", fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/twiml/acknowledged"))

		// Send our POST to Twilio
		body := *strings.NewReader(u.Encode())
		client := &http.Client{}
		req, _ := http.NewRequest("POST", fmt.Sprint(c.Config.Integrations.Twilio.APIBaseURL, c.Config.Integrations.Twilio.AccountSID, "/Calls/", callSid), &body)
		req.SetBasicAuth(c.Config.Integrations.Twilio.AccountSID, c.Config.Integrations.Twilio.AuthToken)
		req.Header.Add("Accept", "application/json")
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

		// Send the POST request
		_, err := client.Do(req)
		if err != nil {
			log.Println("ReceiveDigits() TwiML POST Request error:", err)
		}

		// Attempt to stop the notification by sending the UUID to the notification engine
		stopChan <- uuid
	}
}

// This Twilio callback generates TwiML that is used to describe the flow of the phone call.
func GenerateTwiML(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	uuid := vars["uuid"]
	action := vars["action"]

	resp := twiml.NewResponse()

	switch action {
	case "notify":
		// This is a request for a TwiML script for a standard message notification
		if _, exists := NIP.Stoppers[uuid]; !exists {
			http.Error(w, "No active notifications for this UUID", http.StatusNotFound)
			return
		}

		intro := twiml.Say{
			Voice: "woman",
			Text:  "This is Chicken Little with a message for you.",
		}

		gather := twiml.Gather{
			Action:    fmt.Sprint(c.Config.Service.CallbackURLBase, "/", uuid, "/digits"),
			Timeout:   15,
			NumDigits: 1,
		}

		theMessage := twiml.Say{
			Voice: "man",
			Text:  NIP.Messages[uuid],
		}

		pressAny := twiml.Say{
			Voice: "woman",
			Text:  "Press any key to acknowledge receipt of this message",
		}

		resp.Action(intro)
		resp.Gather(gather, theMessage, pressAny)

	case "acknowledged":
		// This is a request for the end-of-call wrap-up message
		resp.Action(twiml.Say{
			Voice: "woman",
			Text:  "Thank you. This message has been acknowledged. Goodbye!",
		})
	}

	// Reply to the callback with the TwiML content
	resp.Send(w)
}
<|endoftext|>


<commit_before>package randomsanity

// Best-effort "have we ever seen this array of bytes before?"

import (
	"appengine"
	"appengine/datastore"
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"net/http"
	"time"
)

func looksUnique(ctx appengine.Context, w http.ResponseWriter, b []byte, uID string, tag string) (bool, error) {
	// Test every 16-byte (128-bit) sequence in the input against our database

	// if we get a match, complain!
	match, i, err := unique(ctx, b[:], uID, tag)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return true, err
	}
	if match != nil {
		notify(ctx, uID, tag, b[i:i+16], "Non Unique")
		if len(match.UserID) > 0 && match.UserID != uID {
			notify(ctx, match.UserID, match.Tag, b[i:i+16], "Non Unique")
		}
		return false, nil
	}
	return true, nil
}

//
// Entities in the 'RBH' datastore;
// storing 16 "random we hope" bytes.
//
// First prefixBytes bytes are used as they key,
// the rest are stored as the value, collisions just
// result in multiple values under one key, oldest
// entries first.
//
// The simplest possible storage scheme would be
// 16-byte keys, but that is HUGELY inefficient.
//
// Why 128 bits? We want a false positive rate under
// 1-in-2^60. We're basically running a 'birthday attack'
// so comparing random 128-bit chunks we get
// a chance of collision of any pair of about 1-in-2^64
//

const prefixBytes = 4 // Use 4 for production, 1 for development/testing collisions

type RngUniqueBytesEntry struct {
	Trailing []byte `datastore:",noindex"`
	Time     int64  `datastore:",noindex"`
	UserID   string `datastore:",noindex"`
	Tag      string `datastore:",noindex"`
}
type RngUniqueBytes struct {
	Hits []RngUniqueBytesEntry `datastore:",noindex"`
}

type SecretBytes struct {
	Secret       []byte `datastore:",noindex"`
	CreationTime int64
}

func secretKey(ctx appengine.Context) ([]byte, error) {
	var result []byte

	// Create random secret if it doesn't already exist:
	var secrets []SecretBytes

	q := datastore.NewQuery("SecretBytes")
	if _, err := q.GetAll(ctx, &secrets); err != nil {
		return result, err
	}
	if len(secrets) == 0 {
		var b [16]byte
		if _, err := rand.Read(b[:]); err != nil {
			return result, err
		}
		result = b[:]
		secret := SecretBytes{result, time.Now().Unix()}
		k := datastore.NewIncompleteKey(ctx, "SecretBytes", nil)
		if _, err := datastore.Put(ctx, k, &secret); err != nil {
			return result, err
		}
	} else {
		result = secrets[0].Secret
	}
	return result, nil
}

func i64(b []byte) int64 {
	var result int64
	for i := uint(0); i < uint(len(b)) && i < 8; i++ {
		result = result | (int64(b[i]) << (i * 8))
	}
	return result
}

func dealWithMultiError(err error) error {
	// GetMulti returns either plain errors OR
	// an appengine.MultiError that is an array
	// of errors. We're OK if all the 'errors'
	// are ErrNoSuchEntity; otherwise,
	// we'll report the first error
	switch err.(type) {
	case nil:
		return nil
	case appengine.MultiError:
		m := err.(appengine.MultiError)
		for _, e := range m {
			if e == nil || e == datastore.ErrNoSuchEntity {
				continue
			}
			return e
		}
		return nil
	default:
		return err
	}
	return err
}

// Given secret and data, return 16-byte hash
func hash16(secret []byte, data []byte) []byte {
	b := bytes.Join([][]byte{secret, data}, []byte{})
	h := sha256.Sum224(b)
	return h[0:16]
}

func unique(ctx appengine.Context, b []byte, uID string, tag string) (*RngUniqueBytesEntry, int, error) {
	n := len(b) - 15 // Number of queries
	keys := make([]*datastore.Key, n)
	vals := make([]*RngUniqueBytes, n)

	// Input is first be run through AES-128 encryption, to prevent an attacker
	// from intentionally causing database entry collisions.
	secret, err := secretKey(ctx)
	if err != nil {
		return nil, 0, err
	}

	chunks := make([][]byte, n)
	for i := 0; i < n; i++ {
		chunks[i] = hash16(secret, b[i:i+16])

		keys[i] = datastore.NewKey(ctx, "RBH", "", 1+i64(chunks[i][0:prefixBytes]), nil)
		vals[i] = new(RngUniqueBytes)
	}
	err = datastore.GetMulti(ctx, keys, vals)
	err = dealWithMultiError(err)

	if err != nil {
		return nil, 0, err
	}
	for i, hit := range vals {
		for _, h := range hit.Hits {
			if bytes.Equal(h.Trailing, chunks[i][prefixBytes:]) {
				// Rewriting keeps this entry from getting evicted
				// and overwriting the userid prevents the
				// user from getting too many notifications
				write(ctx, chunks[i][:], time.Now().Unix(), "", h.Tag)
				return &h, i, nil // ... full match!
			}
		}
	}
	// If no matches, store the first and last 16 bytes. Any future
	// overlapping sequences will trigger a match.
	err = write(ctx, chunks[0][:], time.Now().Unix(), uID, tag)
	if err == nil && n > 1 {
		err = write(ctx, chunks[n-1][:], time.Now().Unix(), uID, tag)
	}
	if err != nil {
		return nil, 0, err
	}
	return nil, 0, nil
}

func write(ctx appengine.Context, b []byte, t int64, uID string, tag string) error {
	const maxEntriesPerKey = 100

	key := datastore.NewKey(ctx, "RBH", "", 1+i64(b[0:prefixBytes]), nil)

	err := datastore.RunInTransaction(ctx, func(ctx appengine.Context) error {
		hit := new(RngUniqueBytes)
		err := datastore.Get(ctx, key, hit)
		if err != nil && err != datastore.ErrNoSuchEntity {
			return err
		}
		// Find and remove old entry (if any):
		hits := hit.Hits[:0]
		for _, h := range hit.Hits {
			if !bytes.Equal(h.Trailing, b[prefixBytes:]) {
				hits = append(hits, h)
			}
		}
		// Append new:
		e := RngUniqueBytesEntry{Trailing: b[prefixBytes:], Time: t, UserID: uID, Tag: tag}
		hit.Hits = append(hits, e)
		// Throw out half the old if bucket overflows:
		if len(hit.Hits) > maxEntriesPerKey {
			hit.Hits = hit.Hits[len(hit.Hits)/2:]
		}
		_, err = datastore.Put(ctx, key, hit)
		return err
	}, nil)
	return err
}
<commit_msg>Fix comment<commit_after>package randomsanity

// Best-effort "have we ever seen this array of bytes before?"

import (
	"appengine"
	"appengine/datastore"
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"net/http"
	"time"
)

func looksUnique(ctx appengine.Context, w http.ResponseWriter, b []byte, uID string, tag string) (bool, error) {
	// Test every 16-byte (128-bit) sequence in the input against our database

	// if we get a match, complain!
	match, i, err := unique(ctx, b[:], uID, tag)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return true, err
	}
	if match != nil {
		notify(ctx, uID, tag, b[i:i+16], "Non Unique")
		if len(match.UserID) > 0 && match.UserID != uID {
			notify(ctx, match.UserID, match.Tag, b[i:i+16], "Non Unique")
		}
		return false, nil
	}
	return true, nil
}

//
// Entities in the 'RBH' datastore;
// storing 16 "random we hope" bytes.
//
// First prefixBytes bytes are used as they key,
// the rest are stored as the value, collisions just
// result in multiple values under one key, oldest
// entries first.
//
// The simplest possible storage scheme would be
// 16-byte keys, but that is HUGELY inefficient.
//
// Why 128 bits? We want a false positive rate under
// 1-in-2^60. We're basically running a 'birthday attack'
// so comparing random 128-bit chunks we get
// a chance of collision of any pair of about 1-in-2^64
//

const prefixBytes = 4 // Use 4 for production, 1 for development/testing collisions

type RngUniqueBytesEntry struct {
	Trailing []byte `datastore:",noindex"`
	Time     int64  `datastore:",noindex"`
	UserID   string `datastore:",noindex"`
	Tag      string `datastore:",noindex"`
}
type RngUniqueBytes struct {
	Hits []RngUniqueBytesEntry `datastore:",noindex"`
}

type SecretBytes struct {
	Secret       []byte `datastore:",noindex"`
	CreationTime int64
}

func secretKey(ctx appengine.Context) ([]byte, error) {
	var result []byte

	// Create random secret if it doesn't already exist:
	var secrets []SecretBytes

	q := datastore.NewQuery("SecretBytes")
	if _, err := q.GetAll(ctx, &secrets); err != nil {
		return result, err
	}
	if len(secrets) == 0 {
		var b [16]byte
		if _, err := rand.Read(b[:]); err != nil {
			return result, err
		}
		result = b[:]
		secret := SecretBytes{result, time.Now().Unix()}
		k := datastore.NewIncompleteKey(ctx, "SecretBytes", nil)
		if _, err := datastore.Put(ctx, k, &secret); err != nil {
			return result, err
		}
	} else {
		result = secrets[0].Secret
	}
	return result, nil
}

func i64(b []byte) int64 {
	var result int64
	for i := uint(0); i < uint(len(b)) && i < 8; i++ {
		result = result | (int64(b[i]) << (i * 8))
	}
	return result
}

func dealWithMultiError(err error) error {
	// GetMulti returns either plain errors OR
	// an appengine.MultiError that is an array
	// of errors. We're OK if all the 'errors'
	// are ErrNoSuchEntity; otherwise,
	// we'll report the first error
	switch err.(type) {
	case nil:
		return nil
	case appengine.MultiError:
		m := err.(appengine.MultiError)
		for _, e := range m {
			if e == nil || e == datastore.ErrNoSuchEntity {
				continue
			}
			return e
		}
		return nil
	default:
		return err
	}
	return err
}

// Given secret and data, return 16-byte hash
func hash16(secret []byte, data []byte) []byte {
	b := bytes.Join([][]byte{secret, data}, []byte{})
	h := sha256.Sum224(b)
	return h[0:16]
}

func unique(ctx appengine.Context, b []byte, uID string, tag string) (*RngUniqueBytesEntry, int, error) {
	n := len(b) - 15 // Number of queries
	keys := make([]*datastore.Key, n)
	vals := make([]*RngUniqueBytes, n)

	// Input is first hashed with a secret, to prevent an attacker
	// from intentionally causing database entry collisions.
	secret, err := secretKey(ctx)
	if err != nil {
		return nil, 0, err
	}

	chunks := make([][]byte, n)
	for i := 0; i < n; i++ {
		chunks[i] = hash16(secret, b[i:i+16])

		keys[i] = datastore.NewKey(ctx, "RBH", "", 1+i64(chunks[i][0:prefixBytes]), nil)
		vals[i] = new(RngUniqueBytes)
	}
	err = datastore.GetMulti(ctx, keys, vals)
	err = dealWithMultiError(err)

	if err != nil {
		return nil, 0, err
	}
	for i, hit := range vals {
		for _, h := range hit.Hits {
			if bytes.Equal(h.Trailing, chunks[i][prefixBytes:]) {
				// Rewriting keeps this entry from getting evicted
				// and overwriting the userid prevents the
				// user from getting too many notifications
				write(ctx, chunks[i][:], time.Now().Unix(), "", h.Tag)
				return &h, i, nil // ... full match!
			}
		}
	}
	// If no matches, store the first and last 16 bytes. Any future
	// overlapping sequences will trigger a match.
	err = write(ctx, chunks[0][:], time.Now().Unix(), uID, tag)
	if err == nil && n > 1 {
		err = write(ctx, chunks[n-1][:], time.Now().Unix(), uID, tag)
	}
	if err != nil {
		return nil, 0, err
	}
	return nil, 0, nil
}

func write(ctx appengine.Context, b []byte, t int64, uID string, tag string) error {
	const maxEntriesPerKey = 100

	key := datastore.NewKey(ctx, "RBH", "", 1+i64(b[0:prefixBytes]), nil)

	err := datastore.RunInTransaction(ctx, func(ctx appengine.Context) error {
		hit := new(RngUniqueBytes)
		err := datastore.Get(ctx, key, hit)
		if err != nil && err != datastore.ErrNoSuchEntity {
			return err
		}
		// Find and remove old entry (if any):
		hits := hit.Hits[:0]
		for _, h := range hit.Hits {
			if !bytes.Equal(h.Trailing, b[prefixBytes:]) {
				hits = append(hits, h)
			}
		}
		// Append new:
		e := RngUniqueBytesEntry{Trailing: b[prefixBytes:], Time: t, UserID: uID, Tag: tag}
		hit.Hits = append(hits, e)
		// Throw out half the old if bucket overflows:
		if len(hit.Hits) > maxEntriesPerKey {
			hit.Hits = hit.Hits[len(hit.Hits)/2:]
		}
		_, err = datastore.Put(ctx, key, hit)
		return err
	}, nil)
	return err
}
<|endoftext|>


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

package main

import (
	"archive/zip"
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/inconshreveable/go-update"
	"github.com/kardianos/osext"
	"github.com/kr/binarydist"
	"github.com/pivotal-golang/archiver/extractor"
	patch "github.com/sanderhahn/gozip/patchzip"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

func UnzipWrapper(src, dest string) error {
	e := extractor.NewDetectable()
	return e.Extract(src, dest)
}

func IsZip(path string) bool {
	r, err := zip.OpenReader(path)
	if err == nil {
		r.Close()
		return true
	}
	return false
}

func Zip(path string, dirs []string) (err error) {
	if IsZip(path) {
		return errors.New(path + " is already a zip file")
	}

	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return
	}
	defer f.Close()

	startoffset, err := f.Seek(0, os.SEEK_END)
	if err != nil {
		return
	}

	w := patch.NewWriterAt(f, startoffset)

	for _, dir := range dirs {
		err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			fh, err := patch.FileInfoHeader(info)
			if err != nil {
				return err
			}
			fh.Name = path

			p, err := w.CreateHeader(fh)
			if err != nil {
				return err
			}
			if !info.IsDir() {
				content, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				_, err = p.Write(content)
				if err != nil {
					return err
				}
			}
			return err
		})
	}
	err = w.Close()
	return
}

func Unzip(zippath string, destination string) (err error) {
	r, err := zip.OpenReader(zippath)
	if err != nil {
		return err
	}
	for _, f := range r.File {
		fullname := path.Join(destination, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
		} else {
			os.MkdirAll(filepath.Dir(fullname), 0755)
			perms := f.FileInfo().Mode().Perm()
			out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
			if err != nil {
				return err
			}
			rc, err := f.Open()
			if err != nil {
				return err
			}
			_, err = io.CopyN(out, rc, f.FileInfo().Size())
			if err != nil {
				return err
			}
			rc.Close()
			out.Close()

			mtime := f.FileInfo().ModTime()
			err = os.Chtimes(fullname, mtime, mtime)
			if err != nil {
				return err
			}
		}
	}
	return
}

func UnzipList(path string) (list []string, err error) {
	r, err := zip.OpenReader(path)
	if err != nil {
		return
	}
	for _, f := range r.File {
		list = append(list, f.Name)
	}
	return
}

// Update protocol:
//
//   GET hk.heroku.com/hk/linux-amd64.json
//
//   200 ok
//   {
//       "Version": "2",
//       "Sha256": "..." // base64
//   }
//
// then
//
//   GET hkpatch.s3.amazonaws.com/hk/1/2/linux-amd64
//
//   200 ok
//   [bsdiff data]
//
// or
//
//   GET hkdist.s3.amazonaws.com/hk/2/linux-amd64.gz
//
//   200 ok
//   [gzipped executable data]
//
//

const (
	plat = runtime.GOOS + "-" + runtime.GOARCH
)

const devValidTime = 7 * 24 * time.Hour

var ErrHashMismatch = errors.New("new file hash mismatch after patch")
var up = update.New()

// Updater is the configuration and runtime data for doing an update.
//
// Note that ApiURL, BinURL and DiffURL should have the same value if all files are available at the same location.
//
// Example:
//
//  updater := &selfupdate.Updater{
//  	CurrentVersion: version,
//  	ApiURL:         "http://updates.yourdomain.com/",
//  	BinURL:         "http://updates.yourdownmain.com/",
//  	DiffURL:        "http://updates.yourdomain.com/",
//  	Dir:            "update/",
//  	CmdName:        "myapp", // app name
//  }
//  if updater != nil {
//  	go updater.BackgroundRun()
//  }
type Updater struct {
	CurrentVersion string // Currently running version.
	ApiURL         string // Base URL for API requests (json files).
	CmdName        string // Command name is appended to the ApiURL like http://apiurl/CmdName/. This represents one binary.
	BinURL         string // Base URL for full binary downloads.
	DiffURL        string // Base URL for diff downloads.
	Dir            string // Directory to store selfupdate state.
	Info           struct {
		Version string
		Sha256  []byte
	}
}

func (u *Updater) getExecRelativeDir(dir string) string {
	filename, _ := osext.Executable()
	path := filepath.Join(filepath.Dir(filename), dir)
	return path
}

// BackgroundRun starts the update check and apply cycle.
func (u *Updater) BackgroundRun() error {
	os.MkdirAll(u.getExecRelativeDir(u.Dir), 0777)
	if u.wantUpdate() {
		if err := up.CanUpdate(); err != nil {
			log.Println(err)
			return err
		}
		//self, err := osext.Executable()
		//if err != nil {
		// fail update, couldn't figure out path to self
		//return
		//}
		// TODO(bgentry): logger isn't on Windows. Replace w/ proper error reports.
		if err := u.update(); err != nil {
			return err
		}
	}
	return nil
}

func (u *Updater) wantUpdate() bool {
	if strings.Contains(u.CurrentVersion, "dev") {
		return false
	} else {
		return true
	}
}

func (u *Updater) update() error {
	path, err := osext.Executable()
	if err != nil {
		return err
	}
	old, err := os.Open(path)
	if err != nil {
		return err
	}
	defer old.Close()

	err = u.fetchInfo()
	if err != nil {
		log.Println(err)
		return err
	}
	if u.Info.Version == u.CurrentVersion {
		return nil
	}
	bin, err := u.fetchAndVerifyPatch(old)
	if err != nil {
		if err == ErrHashMismatch {
			log.Println("update: hash mismatch from patched binary")
		} else {
			if u.DiffURL != "" {
				log.Println("update: patching binary,", err)
			}
		}

		bin, err = u.fetchAndVerifyFullBin()
		if err != nil {
			if err == ErrHashMismatch {
				log.Println("update: hash mismatch from full binary")
			} else {
				log.Println("update: fetching full binary,", err)
			}
			return err
		}
	}

	// close the old binary before installing because on windows
	// it can't be renamed if a handle to the file is still open
	old.Close()

	err, errRecover := up.FromStream(bytes.NewBuffer(bin))
	if errRecover != nil {
		log.Errorf("update and recovery errors: %q %q", err, errRecover)
		return fmt.Errorf("update and recovery errors: %q %q", err, errRecover)
	}
	if err != nil {
		return err
	}

	// remove config.ini so at restart the package will extract again
	//shutil.CopyFile(*configIni, *configIni+".bak", false)
	//os.Remove(*configIni)

	log.Println("Restarting because update!")

	restart(path)
	//addRebootTrayElement()

	// update done, we should decide if we need to restart ASAP (maybe a field in update json?)
	// BIG issue: the file has been renamed in the meantime

	return nil
}

func (u *Updater) fetchInfo() error {
	r, err := fetch(u.ApiURL + u.CmdName + "/" + plat + ".json")
	if err != nil {
		return err
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&u.Info)
	if err != nil {
		return err
	}
	if len(u.Info.Sha256) != sha256.Size {
		return errors.New("bad cmd hash in info")
	}
	return nil
}

func (u *Updater) fetchAndVerifyPatch(old io.Reader) ([]byte, error) {
	bin, err := u.fetchAndApplyPatch(old)
	if err != nil {
		return nil, err
	}
	if !verifySha(bin, u.Info.Sha256) {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) {
	r, err := fetch(u.DiffURL + u.CmdName + "/" + u.CurrentVersion + "/" + u.Info.Version + "/" + plat)
	if err != nil {
		return nil, err
	}
	defer r.Close()
	var buf bytes.Buffer
	err = binarydist.Patch(old, &buf, r)
	return buf.Bytes(), err
}

func (u *Updater) fetchAndVerifyFullBin() ([]byte, error) {
	bin, err := u.fetchBin()
	if err != nil {
		return nil, err
	}
	verified := verifySha(bin, u.Info.Sha256)
	if !verified {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchBin() ([]byte, error) {
	r, err := fetch(u.BinURL + u.CmdName + "/" + u.Info.Version + "/" + plat + ".gz")
	if err != nil {
		return nil, err
	}
	defer r.Close()
	buf := new(bytes.Buffer)
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(buf, gz); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// returns a random duration in [0,n).
func randDuration(n time.Duration) time.Duration {
	return time.Duration(rand.Int63n(int64(n)))
}

func fetch(url string) (io.ReadCloser, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		log.Errorf("bad http status from %s: %v", url, resp.Status)
		return nil, fmt.Errorf("bad http status from %s: %v", url, resp.Status)
	}
	return resp.Body, nil
}

func readTime(path string) time.Time {
	p, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return time.Time{}
	}
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	t, err := time.Parse(time.RFC3339, string(p))
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	return t
}

func verifySha(bin []byte, sha []byte) bool {
	h := sha256.New()
	h.Write(bin)
	return bytes.Equal(h.Sum(nil), sha)
}

func writeTime(path string, t time.Time) bool {
	return ioutil.WriteFile(path, []byte(t.Format(time.RFC3339)), 0644) == nil
}
<commit_msg>fix old dependencies and unused code<commit_after>//
//  update.go
//
//  Created by Martino Facchin
//  Code from github.com/sanbornm and github.com/sanderhahn
//  Copyright (c) 2015 Arduino LLC
//
//  Permission is hereby granted, free of charge, to any person
//  obtaining a copy of this software and associated documentation
//  files (the "Software"), to deal in the Software without
//  restriction, including without limitation the rights to use,
//  copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following
//  conditions:
//
//  The above copyright notice and this permission notice shall be
//  included in all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//  OTHER DEALINGS IN THE SOFTWARE.
//

package main

import (
	"archive/zip"
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/kardianos/osext"
	"github.com/kr/binarydist"
	"github.com/pivotal-golang/archiver/extractor"
	"gopkg.in/inconshreveable/go-update.v0"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

func UnzipWrapper(src, dest string) error {
	e := extractor.NewDetectable()
	return e.Extract(src, dest)
}

func IsZip(path string) bool {
	r, err := zip.OpenReader(path)
	if err == nil {
		r.Close()
		return true
	}
	return false
}

func Unzip(zippath string, destination string) (err error) {
	r, err := zip.OpenReader(zippath)
	if err != nil {
		return err
	}
	for _, f := range r.File {
		fullname := path.Join(destination, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
		} else {
			os.MkdirAll(filepath.Dir(fullname), 0755)
			perms := f.FileInfo().Mode().Perm()
			out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
			if err != nil {
				return err
			}
			rc, err := f.Open()
			if err != nil {
				return err
			}
			_, err = io.CopyN(out, rc, f.FileInfo().Size())
			if err != nil {
				return err
			}
			rc.Close()
			out.Close()

			mtime := f.FileInfo().ModTime()
			err = os.Chtimes(fullname, mtime, mtime)
			if err != nil {
				return err
			}
		}
	}
	return
}

func UnzipList(path string) (list []string, err error) {
	r, err := zip.OpenReader(path)
	if err != nil {
		return
	}
	for _, f := range r.File {
		list = append(list, f.Name)
	}
	return
}

// Update protocol:
//
//   GET hk.heroku.com/hk/linux-amd64.json
//
//   200 ok
//   {
//       "Version": "2",
//       "Sha256": "..." // base64
//   }
//
// then
//
//   GET hkpatch.s3.amazonaws.com/hk/1/2/linux-amd64
//
//   200 ok
//   [bsdiff data]
//
// or
//
//   GET hkdist.s3.amazonaws.com/hk/2/linux-amd64.gz
//
//   200 ok
//   [gzipped executable data]
//
//

const (
	plat = runtime.GOOS + "-" + runtime.GOARCH
)

const devValidTime = 7 * 24 * time.Hour

var ErrHashMismatch = errors.New("new file hash mismatch after patch")
var up = update.New()

// Updater is the configuration and runtime data for doing an update.
//
// Note that ApiURL, BinURL and DiffURL should have the same value if all files are available at the same location.
//
// Example:
//
//  updater := &selfupdate.Updater{
//  	CurrentVersion: version,
//  	ApiURL:         "http://updates.yourdomain.com/",
//  	BinURL:         "http://updates.yourdownmain.com/",
//  	DiffURL:        "http://updates.yourdomain.com/",
//  	Dir:            "update/",
//  	CmdName:        "myapp", // app name
//  }
//  if updater != nil {
//  	go updater.BackgroundRun()
//  }
type Updater struct {
	CurrentVersion string // Currently running version.
	ApiURL         string // Base URL for API requests (json files).
	CmdName        string // Command name is appended to the ApiURL like http://apiurl/CmdName/. This represents one binary.
	BinURL         string // Base URL for full binary downloads.
	DiffURL        string // Base URL for diff downloads.
	Dir            string // Directory to store selfupdate state.
	Info           struct {
		Version string
		Sha256  []byte
	}
}

func (u *Updater) getExecRelativeDir(dir string) string {
	filename, _ := osext.Executable()
	path := filepath.Join(filepath.Dir(filename), dir)
	return path
}

// BackgroundRun starts the update check and apply cycle.
func (u *Updater) BackgroundRun() error {
	os.MkdirAll(u.getExecRelativeDir(u.Dir), 0777)
	if u.wantUpdate() {
		if err := up.CanUpdate(); err != nil {
			log.Println(err)
			return err
		}
		//self, err := osext.Executable()
		//if err != nil {
		// fail update, couldn't figure out path to self
		//return
		//}
		// TODO(bgentry): logger isn't on Windows. Replace w/ proper error reports.
		if err := u.update(); err != nil {
			return err
		}
	}
	return nil
}

func (u *Updater) wantUpdate() bool {
	if strings.Contains(u.CurrentVersion, "dev") {
		return false
	} else {
		return true
	}
}

func (u *Updater) update() error {
	path, err := osext.Executable()
	if err != nil {
		return err
	}
	old, err := os.Open(path)
	if err != nil {
		return err
	}
	defer old.Close()

	err = u.fetchInfo()
	if err != nil {
		log.Println(err)
		return err
	}
	if u.Info.Version == u.CurrentVersion {
		return nil
	}
	bin, err := u.fetchAndVerifyPatch(old)
	if err != nil {
		if err == ErrHashMismatch {
			log.Println("update: hash mismatch from patched binary")
		} else {
			if u.DiffURL != "" {
				log.Println("update: patching binary,", err)
			}
		}

		bin, err = u.fetchAndVerifyFullBin()
		if err != nil {
			if err == ErrHashMismatch {
				log.Println("update: hash mismatch from full binary")
			} else {
				log.Println("update: fetching full binary,", err)
			}
			return err
		}
	}

	// close the old binary before installing because on windows
	// it can't be renamed if a handle to the file is still open
	old.Close()

	err, errRecover := up.FromStream(bytes.NewBuffer(bin))
	if errRecover != nil {
		log.Errorf("update and recovery errors: %q %q", err, errRecover)
		return fmt.Errorf("update and recovery errors: %q %q", err, errRecover)
	}
	if err != nil {
		return err
	}

	// remove config.ini so at restart the package will extract again
	//shutil.CopyFile(*configIni, *configIni+".bak", false)
	//os.Remove(*configIni)

	log.Println("Restarting because update!")

	restart(path)
	//addRebootTrayElement()

	// update done, we should decide if we need to restart ASAP (maybe a field in update json?)
	// BIG issue: the file has been renamed in the meantime

	return nil
}

func (u *Updater) fetchInfo() error {
	r, err := fetch(u.ApiURL + u.CmdName + "/" + plat + ".json")
	if err != nil {
		return err
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&u.Info)
	if err != nil {
		return err
	}
	if len(u.Info.Sha256) != sha256.Size {
		return errors.New("bad cmd hash in info")
	}
	return nil
}

func (u *Updater) fetchAndVerifyPatch(old io.Reader) ([]byte, error) {
	bin, err := u.fetchAndApplyPatch(old)
	if err != nil {
		return nil, err
	}
	if !verifySha(bin, u.Info.Sha256) {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) {
	r, err := fetch(u.DiffURL + u.CmdName + "/" + u.CurrentVersion + "/" + u.Info.Version + "/" + plat)
	if err != nil {
		return nil, err
	}
	defer r.Close()
	var buf bytes.Buffer
	err = binarydist.Patch(old, &buf, r)
	return buf.Bytes(), err
}

func (u *Updater) fetchAndVerifyFullBin() ([]byte, error) {
	bin, err := u.fetchBin()
	if err != nil {
		return nil, err
	}
	verified := verifySha(bin, u.Info.Sha256)
	if !verified {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchBin() ([]byte, error) {
	r, err := fetch(u.BinURL + u.CmdName + "/" + u.Info.Version + "/" + plat + ".gz")
	if err != nil {
		return nil, err
	}
	defer r.Close()
	buf := new(bytes.Buffer)
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(buf, gz); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// returns a random duration in [0,n).
func randDuration(n time.Duration) time.Duration {
	return time.Duration(rand.Int63n(int64(n)))
}

func fetch(url string) (io.ReadCloser, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		log.Errorf("bad http status from %s: %v", url, resp.Status)
		return nil, fmt.Errorf("bad http status from %s: %v", url, resp.Status)
	}
	return resp.Body, nil
}

func readTime(path string) time.Time {
	p, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return time.Time{}
	}
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	t, err := time.Parse(time.RFC3339, string(p))
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	return t
}

func verifySha(bin []byte, sha []byte) bool {
	h := sha256.New()
	h.Write(bin)
	return bytes.Equal(h.Sum(nil), sha)
}

func writeTime(path string, t time.Time) bool {
	return ioutil.WriteFile(path, []byte(t.Format(time.RFC3339)), 0644) == nil
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"syscall"
	"time"

	"github.com/dickeyxxx/golock"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Hidden:      true,
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = Channel
		}
		Update(channel)
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")
var autoupdateFile = filepath.Join(AppDir, "autoupdate")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// Update updates the CLI and plugins
func Update(channel string) {
	touchAutoupdateFile()
	golock.Lock(updateLockPath)
	defer golock.Unlock(updateLockPath)
	done := make(chan bool)
	go func() {
		updateCLI(channel)
		updatePlugins()
		done <- true
	}()
	select {
	case <-time.After(time.Second * 120):
		Errln("Timed out while updating")
	case <-done:
	}
}

func updatePlugins() {
	Err("updating plugins... ")
	b, _ := node.UpdatePackages()
	Errln("done")
	if len(b) > 0 {
		Err("rebuilding plugins cache... ")
		ClearPluginCache()
		WritePluginCache(GetPlugins())
		Errln("done")
	}
}

func updateCLI(channel string) {
	Err("checking for CLI update... ")
	manifest := getUpdateManifest(channel)
	if manifest.Version == Version && manifest.Channel == Channel {
		Errf("already on latest version %s\n", Version)
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out\n", Version, manifest.Version)
		return
	}
	Errf("updating from %s to %s (%s)... ", Version, manifest.Version, manifest.Channel)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", build.URL); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != build.Sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	if err := os.Rename(binPath, binPath+".old"); err != nil {
		panic(err)
	}
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
	Errln("done")
	golock.Unlock(updateLockPath)
	reexecBin()
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "soft" {
		return time.Since(f.ModTime()) > 1*time.Hour
	}
	return time.Since(f.ModTime()) > 168*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("https://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	if err != nil {
		return err
	}
	return out.Close()
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func reexecBin() {
	if runtime.GOOS == "windows" {
		cmd := exec.Command(binPath, os.Args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
		os.Exit(0)
	} else {
		if err := syscall.Exec(binPath, os.Args, os.Environ()); err != nil {
			panic(err)
		}
		os.Exit(99) // should never happen
	}
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if err := exec.Command(binPath, "update").Start(); err != nil {
		panic(err)
	}
}

// WarnIfUpdating prints to stderr if the CLI is updating
func WarnIfUpdating() {
	if exists, _ := fileExists(updateLockPath); exists {
		Errln("WARNING: CLI is updating")
	}
}
<commit_msg>reduce update frequency to 4 hours<commit_after>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"syscall"
	"time"

	"github.com/dickeyxxx/golock"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Hidden:      true,
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = Channel
		}
		Update(channel)
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")
var autoupdateFile = filepath.Join(AppDir, "autoupdate")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// Update updates the CLI and plugins
func Update(channel string) {
	touchAutoupdateFile()
	golock.Lock(updateLockPath)
	defer golock.Unlock(updateLockPath)
	done := make(chan bool)
	go func() {
		updateCLI(channel)
		updatePlugins()
		done <- true
	}()
	select {
	case <-time.After(time.Second * 120):
		Errln("Timed out while updating")
	case <-done:
	}
}

func updatePlugins() {
	Err("updating plugins... ")
	b, _ := node.UpdatePackages()
	Errln("done")
	if len(b) > 0 {
		Err("rebuilding plugins cache... ")
		ClearPluginCache()
		WritePluginCache(GetPlugins())
		Errln("done")
	}
}

func updateCLI(channel string) {
	Err("checking for CLI update... ")
	manifest := getUpdateManifest(channel)
	if manifest.Version == Version && manifest.Channel == Channel {
		Errf("already on latest version %s\n", Version)
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out\n", Version, manifest.Version)
		return
	}
	Errf("updating from %s to %s (%s)... ", Version, manifest.Version, manifest.Channel)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", build.URL); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != build.Sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	if err := os.Rename(binPath, binPath+".old"); err != nil {
		panic(err)
	}
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
	Errln("done")
	golock.Unlock(updateLockPath)
	reexecBin()
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "soft" {
		return time.Since(f.ModTime()) > 4*time.Hour
	}
	return time.Since(f.ModTime()) > 168*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("https://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	if err != nil {
		return err
	}
	return out.Close()
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func reexecBin() {
	if runtime.GOOS == "windows" {
		cmd := exec.Command(binPath, os.Args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
		os.Exit(0)
	} else {
		if err := syscall.Exec(binPath, os.Args, os.Environ()); err != nil {
			panic(err)
		}
		os.Exit(99) // should never happen
	}
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if err := exec.Command(binPath, "update").Start(); err != nil {
		panic(err)
	}
}

// WarnIfUpdating prints to stderr if the CLI is updating
func WarnIfUpdating() {
	if exists, _ := fileExists(updateLockPath); exists {
		Errln("WARNING: CLI is updating")
	}
}
<|endoftext|>