<commit_before>package val

import (
	"fmt"
	"strconv"

	"github.com/c2stack/c2g/c2"
)

func Conv(f Format, val interface{}) (Value, error) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			err = c2.NewErr(fmt.Sprintf("Could not convert %v to type  %s", val, f))
		}
	}()
	if val == nil {
		return nil, nil
	}
	switch f {
	case FmtBool:
		if x, err := toBool(val); err != nil {
			return nil, err
		} else {
			return Bool(x), nil
		}
	case FmtBoolList:
		if x, err := toBoolList(val); err != nil {
			return nil, err
		} else {
			return BoolList(x), nil
		}
	case FmtInt32:
		if x, err := toInt32(val); err != nil {
			return nil, err
		} else {
			return Int32(x), nil
		}
	case FmtInt32List:
		if x, err := toInt32List(val); err != nil {
			return nil, err
		} else {
			return Int32List(x), nil
		}
	case FmtDecimal64:
		if x, err := toDecimal64(val); err != nil {
			return nil, err
		} else {
			return Decimal64(x), nil
		}
	case FmtDecimal64List:
		if x, err := toDecimal64List(val); err != nil {
			return nil, err
		} else {
			return Decimal64List(x), nil
		}
	case FmtAny:
		return Any{Thing: val}, nil
	case FmtString:
		if x, err := toString(val); err != nil {
			return nil, err
		} else {
			return String(x), nil
		}
	case FmtStringList:
		if x, err := toStringList(val); err != nil {
			return nil, err
		} else {
			return StringList(x), nil
		}
	}

	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to %s value", val, f.String()))
}

func toDecimal64(val interface{}) (float64, error) {
	switch x := val.(type) {
	case int:
		return float64(x), nil
	case int64:
		return float64(x), nil
	case float32:
		return float64(x), nil
	case float64:
		return x, nil
	case string:
		return strconv.ParseFloat(x, 64)
	}
	return 0, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to float64", val))
}

func toDecimal64List(val interface{}) ([]float64, error) {
	switch x := val.(type) {
	case []float64:
		return x, nil
	case []interface{}:
		l := make([]float64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toDecimal64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]float64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toDecimal64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		// TODO: Use reflection on general array type
		if i, notSingle := toDecimal64(val); notSingle == nil {
			return []float64{i}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []int", val))
}

func toInt32List(val interface{}) ([]int, error) {
	switch x := val.(type) {
	case []int:
		return x, nil
	case []interface{}:
		l := make([]int, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt32(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []float64:
		l := make([]int, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt32(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]int, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt32(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		// TODO: Use reflection on general array type

		if i, notSingle := toInt32(val); notSingle == nil {
			return []int{i}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []int", val))
}

func toInt32(val interface{}) (int, error) {
	switch x := val.(type) {
	case int:
		return x, nil
	case int64:
		return int(x), nil
	case string:
		return strconv.Atoi(x)
	case float64:
		return int(x), nil
	case float32:
		return int(x), nil
	}
	return 0, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to int", val))
}

func toBoolList(val interface{}) ([]bool, error) {
	switch x := val.(type) {
	case []bool:
		return x, nil
	case []interface{}:
		l := make([]bool, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toBool(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]bool, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toBool(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		if b, canBool := toBool(val); canBool == nil {
			return []bool{b}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to boolean array", val))
}

func toBool(val interface{}) (bool, error) {
	switch x := val.(type) {
	case bool:
		return x, nil
	case string:
		switch x {
		case "1", "true", "yes":
			return true, nil
		case "0", "false", "np":
			return false, nil
		}
	}
	return false, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to boolean value", val))
}

func toString(val interface{}) (string, error) {
	switch x := val.(type) {
	case float64:
		// wrong format, truncating decimals as most likely mistake but
		// will not please everyone.  Get input in correct format by placing
		// quotes around data.
		return strconv.FormatFloat(x, 'f', 0, 64), nil
	}
	return fmt.Sprintf("%v", val), nil
}

func toStringList(val interface{}) ([]string, error) {
	switch x := val.(type) {
	case []string:
		return x, nil
	case []float64:
		l := make([]string, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toString(x[i]); err != nil {
				return nil, err
			}
		}
		return l, err
	case []interface{}:
		l := make([]string, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toString(x[i]); err != nil {
				return nil, err
			}
		}
		return l, err
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []string", val))
}
<commit_msg>add int64 coersion<commit_after>package val

import (
	"fmt"
	"strconv"

	"github.com/c2stack/c2g/c2"
)

func Conv(f Format, val interface{}) (Value, error) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			err = c2.NewErr(fmt.Sprintf("Could not convert %v to type  %s", val, f))
		}
	}()
	if val == nil {
		return nil, nil
	}
	switch f {
	case FmtBool:
		if x, err := toBool(val); err != nil {
			return nil, err
		} else {
			return Bool(x), nil
		}
	case FmtBoolList:
		if x, err := toBoolList(val); err != nil {
			return nil, err
		} else {
			return BoolList(x), nil
		}
	case FmtInt32:
		if x, err := toInt32(val); err != nil {
			return nil, err
		} else {
			return Int32(x), nil
		}
	case FmtInt32List:
		if x, err := toInt32List(val); err != nil {
			return nil, err
		} else {
			return Int32List(x), nil
		}
	case FmtInt64:
		if x, err := toInt64(val); err != nil {
			return nil, err
		} else {
			return Int64(x), nil
		}
	case FmtInt64List:
		if x, err := toInt64List(val); err != nil {
			return nil, err
		} else {
			return Int64List(x), nil
		}
	case FmtDecimal64:
		if x, err := toDecimal64(val); err != nil {
			return nil, err
		} else {
			return Decimal64(x), nil
		}
	case FmtDecimal64List:
		if x, err := toDecimal64List(val); err != nil {
			return nil, err
		} else {
			return Decimal64List(x), nil
		}
	case FmtAny:
		return Any{Thing: val}, nil
	case FmtString:
		if x, err := toString(val); err != nil {
			return nil, err
		} else {
			return String(x), nil
		}
	case FmtStringList:
		if x, err := toStringList(val); err != nil {
			return nil, err
		} else {
			return StringList(x), nil
		}
	}

	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to %s value", val, f.String()))
}

func toDecimal64(val interface{}) (float64, error) {
	switch x := val.(type) {
	case int:
		return float64(x), nil
	case int64:
		return float64(x), nil
	case float32:
		return float64(x), nil
	case float64:
		return x, nil
	case string:
		return strconv.ParseFloat(x, 64)
	}
	return 0, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to float64", val))
}

func toDecimal64List(val interface{}) ([]float64, error) {
	switch x := val.(type) {
	case []float64:
		return x, nil
	case []interface{}:
		l := make([]float64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toDecimal64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]float64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toDecimal64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		// TODO: Use reflection on general array type
		if i, notSingle := toDecimal64(val); notSingle == nil {
			return []float64{i}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []int", val))
}

func toInt32List(val interface{}) ([]int, error) {
	switch x := val.(type) {
	case []int:
		return x, nil
	case []interface{}:
		l := make([]int, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt32(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []float64:
		l := make([]int, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt32(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]int, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt32(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		// TODO: Use reflection on general array type

		if i, notSingle := toInt32(val); notSingle == nil {
			return []int{i}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []int", val))
}

func toInt32(val interface{}) (int, error) {
	switch x := val.(type) {
	case int:
		return x, nil
	case int64:
		return int(x), nil
	case string:
		return strconv.Atoi(x)
	case float64:
		return int(x), nil
	case float32:
		return int(x), nil
	}
	return 0, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to int", val))
}

func toInt64List(val interface{}) ([]int64, error) {
	switch x := val.(type) {
	case []int:
		l := make([]int64, len(x))
		for i := 0; i < len(x); i++ {
			l[i] = int64(x[i])
		}
		return l, nil
	case []int64:
		return x, nil
	case []interface{}:
		l := make([]int64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []float64:
		l := make([]int64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]int64, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toInt64(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		// TODO: Use reflection on general array type

		if i, notSingle := toInt64(val); notSingle == nil {
			return []int64{i}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []int", val))
}

func toInt64(val interface{}) (int64, error) {
	switch x := val.(type) {
	case int:
		return int64(x), nil
	case int64:
		return x, nil
	case string:
		return strconv.ParseInt(x, 10, 64)
	case float64:
		return int64(x), nil
	case float32:
		return int64(x), nil
	}
	return 0, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to int", val))
}

func toBoolList(val interface{}) ([]bool, error) {
	switch x := val.(type) {
	case []bool:
		return x, nil
	case []interface{}:
		l := make([]bool, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toBool(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	case []string:
		l := make([]bool, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toBool(x[i]); err != nil {
				return nil, err
			}
		}
		return l, nil
	default:
		if b, canBool := toBool(val); canBool == nil {
			return []bool{b}, nil
		}
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to boolean array", val))
}

func toBool(val interface{}) (bool, error) {
	switch x := val.(type) {
	case bool:
		return x, nil
	case string:
		switch x {
		case "1", "true", "yes":
			return true, nil
		case "0", "false", "np":
			return false, nil
		}
	}
	return false, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to boolean value", val))
}

func toString(val interface{}) (string, error) {
	switch x := val.(type) {
	case float64:
		// wrong format, truncating decimals as most likely mistake but
		// will not please everyone.  Get input in correct format by placing
		// quotes around data.
		return strconv.FormatFloat(x, 'f', 0, 64), nil
	}
	return fmt.Sprintf("%v", val), nil
}

func toStringList(val interface{}) ([]string, error) {
	switch x := val.(type) {
	case []string:
		return x, nil
	case []float64:
		l := make([]string, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toString(x[i]); err != nil {
				return nil, err
			}
		}
		return l, err
	case []interface{}:
		l := make([]string, len(x))
		var err error
		for i := 0; i < len(x); i++ {
			if l[i], err = toString(x[i]); err != nil {
				return nil, err
			}
		}
		return l, err
	}
	return nil, c2.NewErr(fmt.Sprintf("cannot coerse '%v' to []string", val))
}
<|endoftext|>


<commit_before>package dijkstra

import "testing"
import "reflect"

func TestDijkstra(t *testing.T) {
	graph := [][][2]int{
		{ // 0
			{1, 7},
			{2, 9},
			{5, 14},
		},
		{ // 1
			{0, 7},
			{2, 10},
			{3, 15},
		},
		{ // 2
			{0, 9},
			{1, 10},
			{5, 2},
			{3, 11},
		},
		{ // 3
			{1, 15},
			{4, 6},
			{2, 11},
		},
		{ // 4
			{5, 9},
			{3, 6},
		},
		{ // 5
			{0, 14},
			{2, 2},
			{4, 9},
		},
	}

	result := Dijkstra(graph, 0)
	expected := []int{0, 7, 9, 20, 20, 11}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected: %v\nGot: %v", expected, result)
	}
}
<commit_msg>one more test<commit_after>package dijkstra

import "testing"
import "reflect"

func TestDijkstra(t *testing.T) {
	graph := [][][2]int{
		{ // 0
			{1, 7},
			{2, 9},
			{5, 14},
		},
		{ // 1
			{0, 7},
			{2, 10},
			{3, 15},
		},
		{ // 2
			{0, 9},
			{1, 10},
			{5, 2},
			{3, 11},
		},
		{ // 3
			{1, 15},
			{4, 6},
			{2, 11},
		},
		{ // 4
			{5, 9},
			{3, 6},
		},
		{ // 5
			{0, 14},
			{2, 2},
			{4, 9},
		},
	}

	result := Dijkstra(graph, 0)
	expected := []int{0, 7, 9, 20, 20, 11}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected: %v\nGot: %v", expected, result)
	}

	result = Dijkstra(graph, 1)
	expected = []int{7, 0, 10, 15, 21, 12}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected: %v\nGot: %v", expected, result)
	}

}
<|endoftext|>


<commit_before>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gflock_test

import (
	"testing"
	"time"

	"github.com/gogf/gf/g/container/garray"
	"github.com/gogf/gf/g/os/gfile"
	"github.com/gogf/gf/g/os/gflock"
	"github.com/gogf/gf/g/test/gtest"
)

func Test_GFlock_Base(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "test"
		lock := gflock.New(fileName)
		gtest.Assert(lock.Path(), gfile.TempDir()+gfile.Separator+"gflock"+gfile.Separator+fileName)
		gtest.Assert(lock.IsLocked(), false)
		lock.Lock()
		gtest.Assert(lock.IsLocked(), true)
		lock.Unlock()
		gtest.Assert(lock.IsLocked(), false)
	})

	gtest.Case(t, func() {
		fileName := "test"
		lock := gflock.New(fileName)
		gtest.Assert(lock.Path(), gfile.TempDir()+gfile.Separator+"gflock"+gfile.Separator+fileName)
		gtest.Assert(lock.IsRLocked(), false)
		lock.RLock()
		gtest.Assert(lock.IsRLocked(), true)
		lock.RUnlock()
		gtest.Assert(lock.IsRLocked(), false)
	})
}

func Test_GFlock_Lock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)

		go func() {
			lock.Lock()
			array.Append(1)
			time.Sleep(200 * time.Millisecond)
			lock.Unlock()
		}()

		go func() {
			time.Sleep(50 * time.Millisecond)
			lock2.Lock()
			array.Append(1)
			lock2.Unlock()
		}()

		time.Sleep(50 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 2)
	})
}

func Test_GFlock_RLock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testRLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)

		go func() {
			lock.RLock()
			array.Append(1)
			time.Sleep(200 * time.Millisecond)
			lock.RUnlock()
		}()

		go func() {
			time.Sleep(50 * time.Millisecond)
			lock2.RLock()
			array.Append(1)
			lock2.RUnlock()
		}()

		time.Sleep(50 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 2)
	})
}

func Test_GFlock_TryLock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testTryLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)

		go func() {
			lock.TryLock()
			array.Append(1)
			time.Sleep(200 * time.Millisecond)
			lock.Unlock()
		}()

		go func() {
			time.Sleep(100 * time.Millisecond)
			if lock2.TryLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()

		go func() {
			time.Sleep(300 * time.Millisecond)
			if lock2.TryLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(200 * time.Millisecond)
		gtest.Assert(array.Len(), 2)
	})
}

func Test_GFlock_TryRLock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testTryRLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)
		go func() {
			lock.TryRLock()
			array.Append(1)
			time.Sleep(300 * time.Millisecond)
			lock.Unlock()
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			if lock2.TryRLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			if lock2.TryRLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			if lock2.TryRLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(300 * time.Millisecond)
		gtest.Assert(array.Len(), 4)
	})
}
<commit_msg>Improve gflock unit testing.<commit_after>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gflock_test

import (
	"testing"
	"time"

	"github.com/gogf/gf/g/container/garray"
	"github.com/gogf/gf/g/os/gfile"
	"github.com/gogf/gf/g/os/gflock"
	"github.com/gogf/gf/g/test/gtest"
)

func Test_GFlock_Base(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "test"
		lock := gflock.New(fileName)
		gtest.Assert(lock.Path(), gfile.TempDir()+gfile.Separator+"gflock"+gfile.Separator+fileName)
		gtest.Assert(lock.IsLocked(), false)
		lock.Lock()
		gtest.Assert(lock.IsLocked(), true)
		lock.Unlock()
		gtest.Assert(lock.IsLocked(), false)
	})

	gtest.Case(t, func() {
		fileName := "test"
		lock := gflock.New(fileName)
		gtest.Assert(lock.Path(), gfile.TempDir()+gfile.Separator+"gflock"+gfile.Separator+fileName)
		gtest.Assert(lock.IsRLocked(), false)
		lock.RLock()
		gtest.Assert(lock.IsRLocked(), true)
		lock.RUnlock()
		gtest.Assert(lock.IsRLocked(), false)
	})
}

func Test_GFlock_Lock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)

		go func() {
			lock.Lock()
			array.Append(1)
			time.Sleep(200 * time.Millisecond)
			lock.Unlock()
		}()

		go func() {
			time.Sleep(50 * time.Millisecond)
			lock2.Lock()
			array.Append(1)
			lock2.Unlock()
		}()

		time.Sleep(50 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 2)
	})
}

func Test_GFlock_RLock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testRLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)

		go func() {
			lock.RLock()
			array.Append(1)
			time.Sleep(400 * time.Millisecond)
			lock.RUnlock()
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			lock2.RLock()
			array.Append(1)
			lock2.RUnlock()
		}()

		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(200 * time.Millisecond)
		gtest.Assert(array.Len(), 2)
	})
}

func Test_GFlock_TryLock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testTryLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)

		go func() {
			lock.TryLock()
			array.Append(1)
			time.Sleep(200 * time.Millisecond)
			lock.Unlock()
		}()

		go func() {
			time.Sleep(100 * time.Millisecond)
			if lock2.TryLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()

		go func() {
			time.Sleep(300 * time.Millisecond)
			if lock2.TryLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(200 * time.Millisecond)
		gtest.Assert(array.Len(), 2)
	})
}

func Test_GFlock_TryRLock(t *testing.T) {
	gtest.Case(t, func() {
		fileName := "testTryRLock"
		array := garray.New()
		lock := gflock.New(fileName)
		lock2 := gflock.New(fileName)
		go func() {
			lock.TryRLock()
			array.Append(1)
			time.Sleep(300 * time.Millisecond)
			lock.Unlock()
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			if lock2.TryRLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			if lock2.TryRLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()

		go func() {
			time.Sleep(200 * time.Millisecond)
			if lock2.TryRLock() {
				array.Append(1)
				lock2.Unlock()
			}
		}()
		time.Sleep(100 * time.Millisecond)
		gtest.Assert(array.Len(), 1)
		time.Sleep(300 * time.Millisecond)
		gtest.Assert(array.Len(), 4)
	})
}
<|endoftext|>


<commit_before><commit_msg>Initial commit of the "serve" command<commit_after><|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// 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 store

import (
	"github.com/jinzhu/gorm"
)

type DB interface {
	Open() error
	Close()
	Conn() *gorm.DB
}
<commit_msg>store/db: split DB into smaller interfaces<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// 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 store

import (
	"github.com/jinzhu/gorm"
)

// DB opener interface
type Opener interface {
	// open DB access, return error or nil
	Open() error
	// close DB access
	Close()
}

// connectable DB interface
type DBConn interface {
	// return connection to gorm.DB
	Conn() *gorm.DB
}

// DB wrapper
type DB interface {
	DBConn
	// start transaction, return transactional interface
	Begin() DBTransaction
}

// DB transaction interface
type DBTransaction interface {
	// implements connectable (we need to acces and manipulate the
	// DBe)
	DBConn
	// and commit operation
	Commit()
}
<|endoftext|>


<commit_before>package show

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/ejcx/passgo/pc"
	"github.com/ejcx/passgo/pio"
)

type searchType int

var (
	lastPrefix      = "└──"
	regPrefix       = "├──"
	innerPrefix     = "|  "
	innerLastPrefix = "   "
)

const (
	// All indicates SearchSites should return all sites from the vault.
	All searchType = iota
	// One indicates SearchSites should return only one site from the vault.
	// It is used when printing a site.
	One
	// Search indicates that SearchSites should return all sites found that
	// match that contain the searchFor string
	Search
)

func init() {
	/* Windows doesn't work with ambiguous width characters */
	if runtime.GOOS == "windows" {
		lastPrefix = "+--"
		regPrefix = "+--"
	}
}

func handleErrors(allErrors []error) {
	errorStr := "Error"
	if len(allErrors) == 0 {
		return
	} else if len(allErrors) > 1 {
		errorStr = "Errors"
	}
	log.Printf("%d %s encountered:\n", len(allErrors), errorStr)
	for n, err := range allErrors {
		log.Printf("Error %d: %s", n, err.Error())
	}
}

// Find will search the vault for all occurences of frag in the site name.
func Find(frag string) {
	allSites, allErrors := SearchAll(Search, frag)
	showResults(allSites)
	handleErrors(allErrors)
}

// Site will print out the password of the site that matches path
func Site(path string, copyPassword bool) {
	allSites, allErrors := SearchAll(One, path)
	if len(allSites) == 0 {
		fmt.Printf("Site with path %s not found", path)
		return
	}
	masterPrivKey := pc.GetMasterKey()
	showPassword(allSites, masterPrivKey, copyPassword)
	handleErrors(allErrors)
}

// ListAll will print out all contents of the vault.
func ListAll() {
	allSites, allErrors := SearchAll(All, "")
	showResults(allSites)
	handleErrors(allErrors)
}

func showPassword(allSites map[string][]pio.SiteInfo, masterPrivKey [32]byte, copyPassword bool) {
	for _, siteList := range allSites {
		for _, site := range siteList {
			var unsealed []byte
			var err error
			if site.IsFile {
				fileDir, err := pio.GetEncryptedFilesDir()
				if err != nil {
					log.Fatalf("Could not get encrypted file dir when searching vault: %s", err.Error())
				}
				filePath := filepath.Join(fileDir, site.FileName)
				f, err := os.OpenFile(filePath, os.O_RDONLY, 0600)
				if err != nil {
					log.Fatalf("Could not open encrypted file: %s", err.Error())
				}
				defer f.Close()

				fileSealed, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatalf("Could not read encrypted file: %s", err.Error())
				}
				unsealed, err = pc.OpenAsym(fileSealed, &site.PubKey, &masterPrivKey)
				if err != nil {
					log.Fatalf("Could not decrypt file bytes: %s", err.Error())
				}

			} else {
				unsealed, err = pc.OpenAsym(site.PassSealed, &site.PubKey, &masterPrivKey)
				if err != nil {
					log.Println("Could not decrypt site password.")
					continue
				}
			}
			if copyPassword {
				pio.ToClipboard(string(unsealed))
			} else {
				fmt.Println(string(unsealed))
			}
		}
	}
}

func showResults(allSites map[string][]pio.SiteInfo) {
	fmt.Println(".")
	counter := 1
	for group, siteList := range allSites {
		siteCounter := 1
		for _, site := range siteList {
			preGroup := regPrefix
			preName := innerPrefix + regPrefix
			if counter == len(allSites) {
				preGroup = lastPrefix
				sitePrefix := innerLastPrefix
				if group == "" {
					sitePrefix = ""
				}
				preName = sitePrefix + regPrefix
				if siteCounter == len(siteList) {
					preName = sitePrefix + lastPrefix
				}
			} else {
				if siteCounter == len(siteList) {
					preName = innerPrefix + lastPrefix
				}
			}

			if siteCounter == 1 {
				if group != "" {
					fmt.Println(preGroup + group)
				}
			}
			fmt.Printf("%s%s\n", preName, site.Name)
			siteCounter++
		}
		counter++
	}
}

// SearchAll will perform a search of searchType with optionally used searchFor. It
// will return all sites as a map of group names to pio.SiteInfo types. That way, callers
// of this function do not need to sort the sites by group themselves.
func SearchAll(st searchType, searchFor string) (allSites map[string][]pio.SiteInfo, allErrors []error) {
	allSites = map[string][]pio.SiteInfo{}
	siteFile, err := pio.GetSitesFile()
	if err != nil {
		log.Fatalf("Could not get site file: %s", err.Error())
	}

	siteFileContents, err := ioutil.ReadFile(siteFile)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatalf("Could not open site file. Run passgo init.: %s", err.Error())
		}
		log.Fatalf("Could not read site file contents: %s", err.Error())
	}

	var sites pio.SiteFile
	err = json.Unmarshal(siteFileContents, &sites)
	if err != nil {
		log.Fatalf("Could not unmarshal site file contents: %s", err.Error())
	}

	for _, s := range sites {
		slashIndex := strings.Index(string(s.Name), "/")
		group := ""
		if slashIndex > 0 {
			group = string(s.Name[:slashIndex])
		}
		name := s.Name[slashIndex+1:]
		pass := s.PassSealed
		pubKey := s.PubKey
		isFile := s.IsFile
		filename := s.FileName
		si := pio.SiteInfo{
			Name:       name,
			PassSealed: pass,
			PubKey:     pubKey,
			IsFile:     isFile,
			FileName:   filename,
		}
		if st == One {
			if name == searchFor || fmt.Sprintf("%s/%s", group, name) == searchFor {
				return map[string][]pio.SiteInfo{
					group: []pio.SiteInfo{
						si,
					},
				}, allErrors
			}
		} else if st == All {
			if allSites[group] == nil {
				allSites[group] = []pio.SiteInfo{}
			}
			allSites[group] = append(allSites[group], si)
		} else if st == Search {
			if strings.Contains(group, searchFor) || strings.Contains(name, searchFor) {
				if allSites[group] == nil {
					allSites[group] = []pio.SiteInfo{}
				}
				allSites[group] = append(allSites[group], si)
			}
		}
	}
	return
}
<commit_msg>Insert site asdf/asdf<commit_after>package show

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/ejcx/passgo/pc"
	"github.com/ejcx/passgo/pio"
	"github.com/xlab/treeprint"
)

type searchType int

var (
	lastPrefix      = "└──"
	regPrefix       = "├──"
	innerPrefix     = "|  "
	innerLastPrefix = "   "
)

const (
	// All indicates SearchSites should return all sites from the vault.
	All searchType = iota
	// One indicates SearchSites should return only one site from the vault.
	// It is used when printing a site.
	One
	// Search indicates that SearchSites should return all sites found that
	// match that contain the searchFor string
	Search
)

func init() {
	/* Windows doesn't work with ambiguous width characters */
	if runtime.GOOS == "windows" {
		lastPrefix = "+--"
		regPrefix = "+--"
	}
}

func handleErrors(allErrors []error) {
	errorStr := "Error"
	if len(allErrors) == 0 {
		return
	} else if len(allErrors) > 1 {
		errorStr = "Errors"
	}
	log.Printf("%d %s encountered:\n", len(allErrors), errorStr)
	for n, err := range allErrors {
		log.Printf("Error %d: %s", n, err.Error())
	}
}

// Find will search the vault for all occurences of frag in the site name.
func Find(frag string) {
	allSites, allErrors := SearchAll(Search, frag)
	showResults(allSites)
	handleErrors(allErrors)
}

// Site will print out the password of the site that matches path
func Site(path string, copyPassword bool) {
	allSites, allErrors := SearchAll(One, path)
	if len(allSites) == 0 {
		fmt.Printf("Site with path %s not found", path)
		return
	}
	masterPrivKey := pc.GetMasterKey()
	showPassword(allSites, masterPrivKey, copyPassword)
	handleErrors(allErrors)
}

// ListAll will print out all contents of the vault.
func ListAll() {
	allSites, allErrors := SearchAll(All, "")
	showResults(allSites)
	handleErrors(allErrors)
}

func showPassword(allSites map[string][]pio.SiteInfo, masterPrivKey [32]byte, copyPassword bool) {
	for _, siteList := range allSites {
		for _, site := range siteList {
			var unsealed []byte
			var err error
			if site.IsFile {
				fileDir, err := pio.GetEncryptedFilesDir()
				if err != nil {
					log.Fatalf("Could not get encrypted file dir when searching vault: %s", err.Error())
				}
				filePath := filepath.Join(fileDir, site.FileName)
				f, err := os.OpenFile(filePath, os.O_RDONLY, 0600)
				if err != nil {
					log.Fatalf("Could not open encrypted file: %s", err.Error())
				}
				defer f.Close()

				fileSealed, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatalf("Could not read encrypted file: %s", err.Error())
				}
				unsealed, err = pc.OpenAsym(fileSealed, &site.PubKey, &masterPrivKey)
				if err != nil {
					log.Fatalf("Could not decrypt file bytes: %s", err.Error())
				}

			} else {
				unsealed, err = pc.OpenAsym(site.PassSealed, &site.PubKey, &masterPrivKey)
				if err != nil {
					log.Println("Could not decrypt site password.")
					continue
				}
			}
			if copyPassword {
				pio.ToClipboard(string(unsealed))
			} else {
				fmt.Println(string(unsealed))
			}
		}
	}
}

func showResults(allSites map[string][]pio.SiteInfo) {
	tree := treeprint.New()
	for group, siteList := range allSites {
		branch := tree.AddBranch(group)
		for _, site := range siteList {
			branch.AddNode(site)
		}
	}
	fmt.Println(tree.String())
	// fmt.Println(".")
	// counter := 1
	// for group, siteList := range allSites {
	// 	siteCounter := 1
	// 	for _, site := range siteList {
	// 		preGroup := regPrefix
	// 		preName := innerPrefix + regPrefix
	// 		if counter == len(allSites) {
	// 			preGroup = lastPrefix
	// 			sitePrefix := innerLastPrefix
	// 			if group == "" {
	// 				sitePrefix = ""
	// 			}
	// 			preName = sitePrefix + regPrefix
	// 			if siteCounter == len(siteList) {
	// 				preName = sitePrefix + lastPrefix
	// 			}
	// 		} else {
	// 			if siteCounter == len(siteList) {
	// 				preName = innerPrefix + lastPrefix
	// 			}
	// 		}

	// 		if siteCounter == 1 {
	// 			if group != "" {
	// 				fmt.Println(preGroup + group)
	// 			}
	// 		}
	// 		fmt.Printf("%s%s\n", preName, site.Name)
	// 		siteCounter++
	// 	}
	// 	counter++
	// }
}

// SearchAll will perform a search of searchType with optionally used searchFor. It
// will return all sites as a map of group names to pio.SiteInfo types. That way, callers
// of this function do not need to sort the sites by group themselves.
func SearchAll(st searchType, searchFor string) (allSites map[string][]pio.SiteInfo, allErrors []error) {
	allSites = map[string][]pio.SiteInfo{}
	siteFile, err := pio.GetSitesFile()
	if err != nil {
		log.Fatalf("Could not get site file: %s", err.Error())
	}

	siteFileContents, err := ioutil.ReadFile(siteFile)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatalf("Could not open site file. Run passgo init.: %s", err.Error())
		}
		log.Fatalf("Could not read site file contents: %s", err.Error())
	}

	var sites pio.SiteFile
	err = json.Unmarshal(siteFileContents, &sites)
	if err != nil {
		log.Fatalf("Could not unmarshal site file contents: %s", err.Error())
	}

	for _, s := range sites {
		slashIndex := strings.Index(string(s.Name), "/")
		group := ""
		if slashIndex > 0 {
			group = string(s.Name[:slashIndex])
		}
		name := s.Name[slashIndex+1:]
		pass := s.PassSealed
		pubKey := s.PubKey
		isFile := s.IsFile
		filename := s.FileName
		si := pio.SiteInfo{
			Name:       name,
			PassSealed: pass,
			PubKey:     pubKey,
			IsFile:     isFile,
			FileName:   filename,
		}
		if st == One {
			if name == searchFor || fmt.Sprintf("%s/%s", group, name) == searchFor {
				return map[string][]pio.SiteInfo{
					group: []pio.SiteInfo{
						si,
					},
				}, allErrors
			}
		} else if st == All {
			if allSites[group] == nil {
				allSites[group] = []pio.SiteInfo{}
			}
			allSites[group] = append(allSites[group], si)
		} else if st == Search {
			if strings.Contains(group, searchFor) || strings.Contains(name, searchFor) {
				if allSites[group] == nil {
					allSites[group] = []pio.SiteInfo{}
				}
				allSites[group] = append(allSites[group], si)
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package geo

import (
	"fmt"
	"github.com/kylelemons/go-gypsy/yaml"
	"os"
	"path"
)

// Provides a set of configuration variables that describe how to interact with a SQL database.
type SQLConf struct {
	driver  string
	openStr string
	table   string
	latCol  string
	lngCol  string
}

const (
	DEFAULT_PGSQL_OPEN_STR = "user=postgres dbname=points sslmode=disable"
	DEFAULT_MYSQL_OPEN_STR = "points/root/"
	DEFAULT_TEST_OPEN_STR  = ""
)

// Returns a SQLConf based on the $DB environment variable
// Returns a PostgreSQL configuration as a default
func sqlConfFromEnv() *SQLConf {
	var dbEnv = os.Getenv("DB")

	switch dbEnv {
	case "mysql":
		return &SQLConf{driver: "mymysql", openStr: DEFAULT_MYSQL_OPEN_STR, table: "points", latCol: "lat", lngCol: "lng"}
	case "mock":
		return &SQLConf{driver: "testdb", openStr: DEFAULT_TEST_OPEN_STR, table: "points", latCol: "lat", lngCol: "lng"}
	default:
		return &SQLConf{driver: "postgres", openStr: DEFAULT_PGSQL_OPEN_STR, table: "points", latCol: "lat", lngCol: "lng"}
	}
}

// Attempts to read config/geo.yml, and creates a SQLConf as described therein.
// Returns the DefaultSQLConf if no config/geo.yml is found, or an error if one arises during the process of parsing the configuration file.
func GetSQLConf() (*SQLConf, error) {
	DefaultSQLConf := sqlConfFromEnv()

	// TODO This should be redesigned so that the user specifies where the config file is
	//      We can still handle the issue where it doesn't exist, but that way it's not hardcoded.
	configPath := path.Join("config/geo.yml")
	_, err := os.Stat(configPath)
	if err != nil && os.IsNotExist(err) {
		return DefaultSQLConf, nil
	} else {

		// Defaults to development environment, you can override by changing the $GO_ENV variable:
		// `$ export GO_ENV=environment` (where environment can be "production", "test", "staging", etc.
		// TODO Potentially find a better solution to handling environments
		// https://github.com/adeven/goenv ?
		goEnv := os.Getenv("GO_ENV")
		if goEnv == "" {
			goEnv = "development"
		}

		config, readYamlErr := yaml.ReadFile(configPath)
		if readYamlErr == nil {

			// TODO Refactor this into a more generic method of retrieving info

			// Get driver
			driver, driveError := config.Get(fmt.Sprintf("%s.driver", goEnv))
			if driveError != nil {
				return nil, driveError
			}

			// Get openStr
			openStr, openStrError := config.Get(fmt.Sprintf("%s.openStr", goEnv))
			if openStrError != nil {
				return nil, openStrError
			}

			// Get table
			table, tableError := config.Get(fmt.Sprintf("%s.table", goEnv))
			if tableError != nil {
				return nil, tableError
			}

			// Get latCol
			latCol, latColError := config.Get(fmt.Sprintf("%s.latCol", goEnv))
			if latColError != nil {
				return nil, latColError
			}

			// Get lngCol
			lngCol, lngColError := config.Get(fmt.Sprintf("%s.lngCol", goEnv))
			if lngColError != nil {
				return nil, lngColError
			}

			sqlConf := &SQLConf{driver: driver, openStr: openStr, table: table, latCol: latCol, lngCol: lngCol}
			return sqlConf, nil

		}

		return nil, readYamlErr
	}

	return nil, err
}
<commit_msg>Moving yaml parsing logic into seperate method such that we can provide a seperate code path for getting configuration from any file the user wishes<commit_after>package geo

import (
	"fmt"
	"github.com/kylelemons/go-gypsy/yaml"
	"os"
	"path"
)

// Provides a set of configuration variables that describe how to interact with a SQL database.
type SQLConf struct {
	driver  string
	openStr string
	table   string
	latCol  string
	lngCol  string
}

const (
	DEFAULT_PGSQL_OPEN_STR = "user=postgres dbname=points sslmode=disable"
	DEFAULT_MYSQL_OPEN_STR = "points/root/"
	DEFAULT_TEST_OPEN_STR  = ""
)

// Returns a SQLConf based on the $DB environment variable
// Returns a PostgreSQL configuration as a default
func sqlConfFromEnv() *SQLConf {
	var dbEnv = os.Getenv("DB")

	switch dbEnv {
	case "mysql":
		return &SQLConf{driver: "mymysql", openStr: DEFAULT_MYSQL_OPEN_STR, table: "points", latCol: "lat", lngCol: "lng"}
	case "mock":
		return &SQLConf{driver: "testdb", openStr: DEFAULT_TEST_OPEN_STR, table: "points", latCol: "lat", lngCol: "lng"}
	default:
		return &SQLConf{driver: "postgres", openStr: DEFAULT_PGSQL_OPEN_STR, table: "points", latCol: "lat", lngCol: "lng"}
	}
}

// Attempts to read config/geo.yml, and creates a SQLConf as described therein.
// Returns the DefaultSQLConf if no config/geo.yml is found, or an error
// if one arises during the process of parsing the configuration file.
func GetSQLConf() (*SQLConf, error) {
	DefaultSQLConf := sqlConfFromEnv()

	// TODO This should be redesigned so that the user specifies where the config file is
	//      We can still handle the issue where it doesn't exist,
	//      but that way it's not hardcoded.
	configPath := path.Join("config/geo.yml")
	_, err := os.Stat(configPath)
	if err != nil && os.IsNotExist(err) {
		return DefaultSQLConf, nil
	} else {

		// Defaults to development environment, you can override
		// by changing the $GO_ENV variable:
		// `$ export GO_ENV=environment` (where environment
		// can be "production", "test", "staging", etc.)
		// TODO: Potentially find a better solution to handling environments
		// Perhaps: https://github.com/adeven/goenv ?
		goEnv := os.Getenv("GO_ENV")
		if goEnv == "" {
			goEnv = "development"
		}

		config, readYamlErr := yaml.ReadFile(configPath)
		if readYamlErr != nil {
			return nil, readYamlErr
		}

		return confFromYamlFile(config, goEnv)
	}

	return nil, err
}

func confFromYamlFile(config *yaml.File, goEnv string) (*SQLConf, error) {

	// TODO Refactor this into a more generic method of retrieving info

	// Get driver
	driver, driveError := config.Get(fmt.Sprintf("%s.driver", goEnv))
	if driveError != nil {
		return nil, driveError
	}

	// Get openStr
	openStr, openStrError := config.Get(fmt.Sprintf("%s.openStr", goEnv))
	if openStrError != nil {
		return nil, openStrError
	}

	// Get table
	table, tableError := config.Get(fmt.Sprintf("%s.table", goEnv))
	if tableError != nil {
		return nil, tableError
	}

	// Get latCol
	latCol, latColError := config.Get(fmt.Sprintf("%s.latCol", goEnv))
	if latColError != nil {
		return nil, latColError
	}

	// Get lngCol
	lngCol, lngColError := config.Get(fmt.Sprintf("%s.lngCol", goEnv))
	if lngColError != nil {
		return nil, lngColError
	}

	sqlConf := &SQLConf{driver: driver, openStr: openStr, table: table, latCol: latCol, lngCol: lngCol}
	return sqlConf, nil

}
<|endoftext|>


<commit_before>package group

import "github.com/visit1985/atlasgo/common/request"

type GetIpWhitelistOutput []struct {
	CidrBlock string `json:"cidrBlock"`
	Comment   string `json:"comment"`
	GroupID   string `json:"groupId"`
	IPAddress string `json:"ipAddress,omitempty"`
	Links     []struct {
		Href string `json:"href"`
		Rel  string `json:"rel"`
	} `json:"links"`
}

func (g *Group) GetIpWhitelist() (*GetIpWhitelistOutput, error) {
	req, out := g.GetIpWhitelistRequest()
	return out, req.Send()
}

func (g *Group) GetIpWhitelistRequest() (req *request.Request, out *GetIpWhitelistOutput) {
	op := &request.Operation{
		Name:		"GetIpWhitelist",
		HTTPMethod:	"GET",
		HTTPPath:	"/groups/" + g.GroupID + "/whitelist",
	}

	out = &GetIpWhitelistOutput{}

	handlers := &request.Handlers {
		ReponseHandler: request.ListReponseHandler,
	}

	req = g.NewRequest(op, nil, out, handlers)
	return req, out
}
<commit_msg>fixed error handling<commit_after>package group

import "github.com/visit1985/atlasgo/common/request"

type GetIpWhitelistOutput []struct {
	CidrBlock string `json:"cidrBlock"`
	Comment   string `json:"comment"`
	GroupID   string `json:"groupId"`
	IPAddress string `json:"ipAddress,omitempty"`
}

func (g *Group) GetIpWhitelist() (*GetIpWhitelistOutput, error) {
	req, out := g.GetIpWhitelistRequest()

	// check for client errors before sending request
	if g.Error != nil {
		return out, g.Error
	}

	if req.Error != nil {
		return out, req.Error
	}

	return out, req.Send()
}

func (g *Group) GetIpWhitelistRequest() (req *request.Request, out *GetIpWhitelistOutput) {
	op := &request.Operation{
		Name:		"GetIpWhitelist",
		HTTPMethod:	"GET",
		HTTPPath:	"/groups/" + g.GroupID + "/whitelist",
	}

	out = &GetIpWhitelistOutput{}

	handlers := &request.Handlers {
		ReponseHandler: request.ListReponseHandler,
	}

	// TODO: add paginator
	req = g.NewRequest(op, nil, out, handlers)
	return req, out
}
<|endoftext|>


<commit_before>package game

import (
	"github.com/MichaelDiBernardo/srl/lib/math"
)

type Tile struct {
	Feature *Feature
	Actor   *Obj
	Pos     math.Point
}

type Map [][]*Tile

type Level struct {
	Map    Map
	fac    ObjFactory
	actors []*Obj
}

// Create a level that uses the given factory to create game objects, and which
// generated by the given generator function.
func NewLevel(width, height int, fac ObjFactory, gen func(*Level) *Level) *Level {
	newmap := Map{}
	for y := 0; y < height; y++ {
		row := make([]*Tile, width, width)
		newmap = append(newmap, row)
	}
	level := &Level{
		Map:    newmap,
		fac:    fac,
		actors: make([]*Obj, 0),
	}
	return gen(level)
}

func (l *Level) Width() int {
	return len(l.Map[0])
}

func (l *Level) Height() int {
	return len(l.Map)
}

func (l *Level) At(p math.Point) *Tile {
	return l.Map[p.Y][p.X]
}

func (l *Level) HasPoint(p math.Point) bool {
	return p.X >= 0 && p.Y >= 0 && p.X < l.Width() && p.Y < l.Height()
}

// Place `o` on the tile at `p`. Returns false if this is impossible (e.g.
// trying to put something on a solid square.)
// This will remove `o` from any tile on any map it was previously on.
func (l *Level) Place(o *Obj, p math.Point) bool {
	tile := l.At(p)

	if tile.Feature.Solid || tile.Actor != nil {
		return false
	}

	// If this actor has been placed before, we need to clear the tile they
	// were on previously. If they haven't, we need to add them to the actor
	// list so we know who they are.
	if o.Tile != nil {
		o.Tile.Actor = nil
	} else {
		l.actors = append(l.actors, o)
	}

	o.Level = l
	o.Tile = tile

	tile.Actor = o

	return true
}

func (l *Level) Evolve() {
	for _, actor := range l.actors {
		actor.AI.Act(l)
	}
}
<commit_msg>Give level bounds.<commit_after>package game

import (
	"github.com/MichaelDiBernardo/srl/lib/math"
)

type Tile struct {
	Feature *Feature
	Actor   *Obj
	Pos     math.Point
}

type Map [][]*Tile

type Level struct {
	Map    Map
	fac    ObjFactory
	actors []*Obj
    bounds math.Rectangle
}

// Create a level that uses the given factory to create game objects, and which
// generated by the given generator function.
func NewLevel(width, height int, fac ObjFactory, gen func(*Level) *Level) *Level {
	newmap := Map{}
	for y := 0; y < height; y++ {
		row := make([]*Tile, width, width)
		newmap = append(newmap, row)
	}
	level := &Level{
		Map:    newmap,
		fac:    fac,
		actors: make([]*Obj, 0),
        bounds: math.Rect(math.Origin, math.Pt(width, height)),
	}
	return gen(level)
}

func (l *Level) Width() int {
	return len(l.Map[0])
}

func (l *Level) Height() int {
	return len(l.Map)
}

func (l *Level) At(p math.Point) *Tile {
	return l.Map[p.Y][p.X]
}

func (l *Level) HasPoint(p math.Point) bool {
	return l.bounds.HasPoint(p)
}

// Place `o` on the tile at `p`. Returns false if this is impossible (e.g.
// trying to put something on a solid square.)
// This will remove `o` from any tile on any map it was previously on.
func (l *Level) Place(o *Obj, p math.Point) bool {
	tile := l.At(p)

	if tile.Feature.Solid || tile.Actor != nil {
		return false
	}

	// If this actor has been placed before, we need to clear the tile they
	// were on previously. If they haven't, we need to add them to the actor
	// list so we know who they are.
	if o.Tile != nil {
		o.Tile.Actor = nil
	} else {
		l.actors = append(l.actors, o)
	}

	o.Level = l
	o.Tile = tile

	tile.Actor = o

	return true
}

func (l *Level) Evolve() {
	for _, actor := range l.actors {
		actor.AI.Act(l)
	}
}
<|endoftext|>


<commit_before>package dhcp6

import (
	"encoding/binary"
	"errors"
	"time"
)

var (
	// ErrInvalidIANAID is returned when an input IAID value is not
	// exactly 4 bytes in length.
	ErrInvalidIANAIAID = errors.New("IAID must be exactly 4 bytes")

	// errInvalidIANA is returned when a byte slice does not contain
	// enough bytes to parse a valid IANA value.
	errInvalidIANA = errors.New("not enough bytes for valid IA_NA")
)

// IANA represents an Identity Association for Non-temporary Addresses, as
// defined in IETF RFC 3315, Section 10.  DHCP clients and servers use
// identity assocations (IAs) to identify, group, and manage a set of
// related IPv6 addresses.  Each IA must be associated with exactly one
// network interface.  Multiple IAs may be present in a DHCP request.
type IANA struct {
	// The raw byte slice containing the IANA, with options stripped
	iana []byte

	// Options map which is marshaled to binary when Bytes is called
	options Options
}

// NewIANA creates a new IANA from an IAID, T1 and T2 durations, and
// an optional Options map.  The IAID must be exactly 4 bytes in length.
// If an Options map is not specified, a new one will be allocated.
func NewIANA(iaid []byte, t1 time.Duration, t2 time.Duration, options Options) (*IANA, error) {
	// IANA is always 4 bytes
	if len(iaid) != 4 {
		return nil, ErrInvalidIANAIAID
	}

	iana := make([]byte, 12)
	copy(iana[0:4], iaid)

	// Convert durations to uint32 binary form
	binary.BigEndian.PutUint32(iana[4:8], uint32(t1/time.Second))
	binary.BigEndian.PutUint32(iana[8:12], uint32(t2/time.Second))

	// If no options set, make empty map
	if options == nil {
		options = make(Options)
	}

	return &IANA{
		iana:    iana,
		options: options,
	}, nil
}

// Bytes returns the underlying byte slice for an IANA, as well as a
// byte slice for all options which have been applied to the Options
// map for this IANA.
func (i *IANA) Bytes() []byte {
	// Enumerate optslice and check byte count
	opts := i.options.enumerate()
	c := opts.count()

	// Allocate correct number of bytes and write options
	buf := make([]byte, c, c)
	opts.write(buf)

	// Return IANA with options
	return append(i.iana, buf...)
}

// IAID returns an identity association identifier, which is a value generated
// by a client, chosen to be unique among other IAs for that client.  An IAID
// must always produce the same value across restarts of a client.
func (i *IANA) IAID() []byte {
	// Too short to contain IAID
	if len(i.iana) < 4 {
		return nil
	}

	return i.iana[0:4]
}

// T1 returns a duration which indicates how long a DHCP client will wait to
// contact the server, to extend the lifetimes of the addresses assigned to
// this IANA by this server.
func (i *IANA) T1() time.Duration {
	// Too short to contain T1
	if len(i.iana) < 8 {
		return 0
	}

	return time.Duration(binary.BigEndian.Uint32(i.iana[4:8])) * time.Second
}

// T2 returns a duration which indicates how long a DHCP client will wait to
// contact any server, to extend the lifetimes of the addresses assigned to
// this IANA by any server.
func (i *IANA) T2() time.Duration {
	// Too short to contain T2
	if len(i.iana) < 12 {
		return 0
	}

	return time.Duration(binary.BigEndian.Uint32(i.iana[8:12])) * time.Second
}

// Options parses the Options map associated with this IANA.  The Options
// may contain additional information regarding this IANA.  Options can be
// added, removed, or modified directly via this map.
func (i IANA) Options() Options {
	return i.options
}

// parseIANA attempts to parse an input byte slice as a IANA.
func parseIANA(b []byte) (*IANA, error) {
	if len(b) < 12 {
		return nil, errInvalidIANA
	}

	return &IANA{
		iana:    b[:12],
		options: parseOptions(b[12:]),
	}, nil
}
<commit_msg>iana: improve documentation for types<commit_after>package dhcp6

import (
	"encoding/binary"
	"errors"
	"time"
)

var (
	// ErrInvalidIANAID is returned when an input IAID value is not
	// exactly 4 bytes in length.
	ErrInvalidIANAIAID = errors.New("IAID must be exactly 4 bytes")

	// errInvalidIANA is returned when a byte slice does not contain
	// enough bytes to parse a valid IANA value.
	errInvalidIANA = errors.New("not enough bytes for valid IA_NA")
)

// IANA represents an Identity Association for Non-temporary Addresses, as
// defined in IETF RFC 3315, Section 10.
//
// DHCP clients and servers use identity assocations (IAs) to identify, group,
// and manage a set of related IPv6 addresses.  Each IA must be associated
// with exactly one network interface.
//
// Multiple IAs may be present in a single DHCP request.
type IANA struct {
	// The raw byte slice containing the IANA, with options stripped
	iana []byte

	// Options map which is marshaled to binary when Bytes is called
	options Options
}

// NewIANA creates a new IANA from an IAID, T1 and T2 durations, and an
// optional Options map.
//
// The IAID must be exactly 4 bytes in length, and the same value must
// always be produced across restarts of a client. If an Options map is not
// specified, a new one will be allocated.
func NewIANA(iaid []byte, t1 time.Duration, t2 time.Duration, options Options) (*IANA, error) {
	// IANA is always 4 bytes
	if len(iaid) != 4 {
		return nil, ErrInvalidIANAIAID
	}

	iana := make([]byte, 12)
	copy(iana[0:4], iaid)

	// Convert durations to uint32 binary form
	binary.BigEndian.PutUint32(iana[4:8], uint32(t1/time.Second))
	binary.BigEndian.PutUint32(iana[8:12], uint32(t2/time.Second))

	// If no options set, make empty map
	if options == nil {
		options = make(Options)
	}

	return &IANA{
		iana:    iana,
		options: options,
	}, nil
}

// Bytes implements Byteser, and returns the underlying byte slice for an IANA,
// appended with a byte slice of all options which have been applied to the
// Options map for this IANA.
func (i *IANA) Bytes() []byte {
	// Enumerate optslice and check byte count
	opts := i.options.enumerate()
	c := opts.count()

	// Allocate correct number of bytes and write options
	buf := make([]byte, c, c)
	opts.write(buf)

	// Return IANA with options
	return append(i.iana, buf...)
}

// IAID returns an identity association identifier, which is a value generated
// by a client, chosen to be unique among other IAs for that client.
func (i *IANA) IAID() []byte {
	// Too short to contain IAID
	if len(i.iana) < 4 {
		return nil
	}

	return i.iana[0:4]
}

// T1 returns a duration which indicates how long a DHCP client will wait to
// contact the server, to extend the lifetimes of the addresses assigned to
// this IANA by this server.
func (i *IANA) T1() time.Duration {
	// Too short to contain T1
	if len(i.iana) < 8 {
		return 0
	}

	return time.Duration(binary.BigEndian.Uint32(i.iana[4:8])) * time.Second
}

// T2 returns a duration which indicates how long a DHCP client will wait to
// contact any server, to extend the lifetimes of the addresses assigned to
// this IANA by any server.
func (i *IANA) T2() time.Duration {
	// Too short to contain T2
	if len(i.iana) < 12 {
		return 0
	}

	return time.Duration(binary.BigEndian.Uint32(i.iana[8:12])) * time.Second
}

// Options parses the Options map associated with this IANA.  The Options
// may contain additional information regarding this IANA.  Options can be
// added, removed, or modified directly via this map.
func (i IANA) Options() Options {
	return i.options
}

// parseIANA attempts to parse an input byte slice as a IANA.
func parseIANA(b []byte) (*IANA, error) {
	if len(b) < 12 {
		return nil, errInvalidIANA
	}

	return &IANA{
		iana:    b[:12],
		options: parseOptions(b[12:]),
	}, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 VMware, Inc. All Rights Reserved.

package ipmi

import (
	"bytes"
	"encoding/binary"
	"hash/adler32"
	"io"
	"log"
	"net"
	"sync"
)

type rmcpHeader struct {
	Version            uint8
	Reserved           uint8
	RMCPSequenceNumber uint8
	Class              uint8
}

type asfHeader struct {
	IANAEnterpriseNumber uint32
	MessageType          uint8
	MessageTag           uint8
	Reserved             uint8
	DataLength           uint8
}

type asfMessage struct {
	rmcpHeader
	asfHeader
	Data []byte
}

type ipmiSession struct {
	AuthType  uint8
	Sequence  uint32
	SessionID uint32
}

type ipmiHeader struct {
	MsgLen     uint8
	RsAddr     uint8
	NetFnRsLUN uint8
	Checksum   uint8
	RqAddr     uint8
	RqSeq      uint8
	Command
}

// Message encapsulates an IPMI message
type Message struct {
	rmcpHeader
	ipmiSession
	AuthCode [16]byte
	ipmiHeader
	Data      []byte
	RequestID string
}

// NetworkFunction identifies the functional class of an IPMI message
type NetworkFunction uint8

// Network Function Codes (section 5.1)
var (
	NetworkFunctionChassis = NetworkFunction(0x00)
	NetworkFunctionApp     = NetworkFunction(0x06)
)

// Command fields on an IPMI message
type Command uint8

// Command Number Assignments (table G-1)
var (
	CommandGetAuthenticationCapabilities = Command(0x38)
	CommandGetSessionChallenge           = Command(0x39)
	CommandActivateSession               = Command(0x3a)
	CommandSetSessionPrivilegeLevel      = Command(0x3b)
	CommandCloseSession                  = Command(0x3c)
	CommandChassisControl                = Command(0x02)
	CommandSetSystemBootOptions          = Command(0x08)
)

// CompletionCode is the first byte in the data field of all IPMI responses
type CompletionCode uint8

// Code returns the CompletionCode as uint8
func (c CompletionCode) Code() uint8 {
	return uint8(c)
}

// Completion Codes (section 5.2)
var (
	CommandCompleted = CompletionCode(0x00)
	InvalidCommand   = CompletionCode(0xc1)
	UnspecifiedError = CompletionCode(0xff)
)

// Request handler
type Request func(*Message) Response

// Response to an IPMI request must include at least a CompletionCode
type Response interface {
	Code() uint8
}

// Simulator for IPMI
type Simulator struct {
	wg       sync.WaitGroup
	addr     net.UDPAddr
	conn     *net.UDPConn
	handlers map[NetworkFunction]map[Command]Request
	ids      map[uint32]string
}

// NewSimulator constructs a Simulator with the given addr
func NewSimulator(addr net.UDPAddr) *Simulator {
	s := &Simulator{
		addr: addr,
		ids:  map[uint32]string{},
		handlers: map[NetworkFunction]map[Command]Request{
			NetworkFunctionChassis: map[Command]Request{},
		},
	}

	// Built-in handlers for session management
	s.handlers[NetworkFunctionApp] = map[Command]Request{
		CommandGetAuthenticationCapabilities: s.authenticationCapabilities,
		CommandGetSessionChallenge:           s.sessionChallenge,
		CommandActivateSession:               s.sessionActivate,
		CommandSetSessionPrivilegeLevel:      s.sessionPrivilege,
		CommandCloseSession:                  s.sessionClose,
	}

	return s
}

// SetHandler sets the command handler for the given netfn and command
func (s *Simulator) SetHandler(netfn NetworkFunction, command Command, handler Request) {
	s.handlers[netfn][command] = handler
}

// NewConnection to this Simulator instance
func (s *Simulator) NewConnection() *Connection {
	addr := s.LocalAddr()
	return &Connection{
		Hostname:  addr.IP.String(),
		Port:      addr.Port,
		Interface: "lan",
	}
}

// LocalAddr returns the address the server is bound to.
func (s *Simulator) LocalAddr() *net.UDPAddr {
	if s.conn != nil {
		return s.conn.LocalAddr().(*net.UDPAddr)
	}
	return nil
}

// NetFn returns the NetworkFunction portion of the NetFn/RsLUN field
func (m *Message) NetFn() NetworkFunction {
	return NetworkFunction(m.NetFnRsLUN >> 2)
}

// Run the Simulator.
func (s *Simulator) Run() error {
	var err error
	s.conn, err = net.ListenUDP("udp4", &s.addr)
	if err != nil {
		return err
	}

	s.wg.Add(1)

	go func() {
		_ = s.serve()
		s.wg.Done()
	}()

	return nil
}

// Stop the Simulator.
func (s *Simulator) Stop() {
	_ = s.conn.Close()
	s.wg.Wait()
}

func (s *Simulator) authenticationCapabilities(*Message) Response {
	const (
		authNone = (1 << iota)
		authMD2
		authMD5
		authReserved
		authPassword
		authOEM
	)

	return struct {
		CompletionCode
		ChannelNumber             uint8
		AuthenticationTypeSupport uint8
		Status                    uint8
		Reserved                  uint8
		OEMID                     uint16
		OEMAux                    uint8
	}{
		CompletionCode:            CommandCompleted,
		ChannelNumber:             0x01,
		AuthenticationTypeSupport: authNone | authMD5 | authPassword,
	}
}

func (s *Simulator) sessionChallenge(m *Message) Response {
	// Convert username to a uint32 and use as the SessionID.
	// The SessionID will be propagated such that all requests
	// for this session include the ID, which can be used to
	// dispatch requests.
	username := bytes.TrimRight(m.Data[1:], "\000")
	hash := adler32.New()
	hash.Sum(username)
	id := hash.Sum32()
	s.ids[id] = string(username)

	return struct {
		CompletionCode
		TemporarySessionID uint32
		Challenge          [15]byte
	}{
		CompletionCode:     CommandCompleted,
		TemporarySessionID: id,
	}
}

func (s *Simulator) sessionActivate(m *Message) Response {
	return struct {
		CompletionCode
		AuthType   uint8
		SessionID  uint32
		InboundSeq uint32
		MaxPriv    uint8
	}{
		CompletionCode: CommandCompleted,
		AuthType:       m.AuthType,
		SessionID:      m.SessionID,
		InboundSeq:     m.Sequence,
		MaxPriv:        0x04, // Admin
	}
}

func (s *Simulator) sessionPrivilege(m *Message) Response {
	return struct {
		CompletionCode
		NewPrivilegeLevel uint8
	}{
		CompletionCode:    CommandCompleted,
		NewPrivilegeLevel: m.Data[0],
	}
}

func (s *Simulator) sessionClose(*Message) Response {
	return CommandCompleted
}

func (s *Simulator) write(writer io.Writer, data interface{}) {
	err := binary.Write(writer, binary.BigEndian, data)
	if err != nil {
		// shouldn't happen to a bytes.Buffer
		panic(err)
	}
}

func (s *Simulator) read(reader io.Reader, data interface{}) {
	err := binary.Read(reader, binary.BigEndian, data)
	if err != nil {
		// in this case, client gets no response or InvalidCommand
		log.Printf("binary.Read error: %s", err)
	}
}

func (s *Simulator) ipmiCommand(m *Message) []byte {
	response := Response(InvalidCommand)

	if commands, ok := s.handlers[m.NetFn()]; ok {
		if handler, ok := commands[m.Command]; ok {
			m.RequestID = s.ids[m.SessionID]
			response = handler(m)
		}
	}

	buf := new(bytes.Buffer)
	s.write(buf, &m.rmcpHeader)
	s.write(buf, &m.ipmiSession)
	if m.AuthType != 0 {
		s.write(buf, m.AuthCode)
	}
	s.write(buf, &m.ipmiHeader)
	s.write(buf, response)

	return buf.Bytes()
}

func (s *Simulator) asfCommand(m *asfMessage) []byte {
	if m.MessageType != 0x80 {
		log.Panicf("ASF message type not supported: %d", m.MessageType)
	}

	response := struct {
		IANAEnterpriseNumber  uint32
		OEM                   uint32
		SupportedEntities     uint8
		SupportedInteractions uint8
		Reserved              [6]uint8
	}{
		IANAEnterpriseNumber: m.IANAEnterpriseNumber,
		SupportedEntities:    0x81, // IPMI
	}

	buf := new(bytes.Buffer)
	s.write(buf, &m.rmcpHeader)
	s.write(buf, &m.asfHeader)
	s.write(buf, &response)

	return buf.Bytes()
}

func (s *Simulator) serve() error {
	buf := make([]byte, 1024)
	ipmiHeaderSize := binary.Size(ipmiHeader{})

	for {
		var header rmcpHeader
		var response []byte
		var err error

		n, addr, err := s.conn.ReadFrom(buf)
		if err != nil {
			return err // conn closed
		}

		reader := bytes.NewReader(buf[:n])

		s.read(reader, &header)

		switch header.Class {
		case 6:
			m := &asfMessage{
				rmcpHeader: header,
			}

			s.read(reader, &m.asfHeader)

			response = s.asfCommand(m)
		case 7:
			m := &Message{
				rmcpHeader: header,
			}

			s.read(reader, &m.ipmiSession)
			if m.AuthType != 0 {
				s.read(reader, &m.AuthCode)
			}
			s.read(reader, &m.ipmiHeader)

			dataLen := int(m.MsgLen) - ipmiHeaderSize
			m.Data = make([]byte, dataLen)
			_, _ = reader.Read(m.Data)

			response = s.ipmiCommand(m)
		default:
			log.Printf("Unsupported Class: %d", header.Class)
			continue
		}

		_, err = s.conn.WriteTo(response, addr)
		if err != nil {
			return err // conn closed
		}
	}
}
<commit_msg>IPMI Proxy forwards request to jessup.Control<commit_after>// Copyright (c) 2014 VMware, Inc. All Rights Reserved.

package ipmi

import (
	"bytes"
	"encoding/binary"
	"hash/adler32"
	"io"
	"log"
	"net"
	"sync"
)

type rmcpHeader struct {
	Version            uint8
	Reserved           uint8
	RMCPSequenceNumber uint8
	Class              uint8
}

type asfHeader struct {
	IANAEnterpriseNumber uint32
	MessageType          uint8
	MessageTag           uint8
	Reserved             uint8
	DataLength           uint8
}

type asfMessage struct {
	rmcpHeader
	asfHeader
	Data []byte
}

type ipmiSession struct {
	AuthType  uint8
	Sequence  uint32
	SessionID uint32
}

type ipmiHeader struct {
	MsgLen     uint8
	RsAddr     uint8
	NetFnRsLUN uint8
	Checksum   uint8
	RqAddr     uint8
	RqSeq      uint8
	Command
}

// Message encapsulates an IPMI message
type Message struct {
	rmcpHeader
	ipmiSession
	AuthCode [16]byte
	ipmiHeader
	Data      []byte
	RequestID string
}

// NetworkFunction identifies the functional class of an IPMI message
type NetworkFunction uint8

// Network Function Codes (section 5.1)
var (
	NetworkFunctionChassis = NetworkFunction(0x00)
	NetworkFunctionApp     = NetworkFunction(0x06)
)

// Command fields on an IPMI message
type Command uint8

// Command Number Assignments (table G-1)
var (
	CommandGetAuthenticationCapabilities = Command(0x38)
	CommandGetSessionChallenge           = Command(0x39)
	CommandActivateSession               = Command(0x3a)
	CommandSetSessionPrivilegeLevel      = Command(0x3b)
	CommandCloseSession                  = Command(0x3c)
	CommandChassisControl                = Command(0x02)
	CommandSetSystemBootOptions          = Command(0x08)
)

// CompletionCode is the first byte in the data field of all IPMI responses
type CompletionCode uint8

// Code returns the CompletionCode as uint8
func (c CompletionCode) Code() uint8 {
	return uint8(c)
}

// Completion Codes (section 5.2)
var (
	CommandCompleted       = CompletionCode(0x00)
	InvalidCommand         = CompletionCode(0xc1)
	DestinationUnavailable = CompletionCode(0xd3)
	UnspecifiedError       = CompletionCode(0xff)
)

// Request handler
type Request func(*Message) Response

// Response to an IPMI request must include at least a CompletionCode
type Response interface {
	Code() uint8
}

// Simulator for IPMI
type Simulator struct {
	wg       sync.WaitGroup
	addr     net.UDPAddr
	conn     *net.UDPConn
	handlers map[NetworkFunction]map[Command]Request
	ids      map[uint32]string
}

// NewSimulator constructs a Simulator with the given addr
func NewSimulator(addr net.UDPAddr) *Simulator {
	s := &Simulator{
		addr: addr,
		ids:  map[uint32]string{},
		handlers: map[NetworkFunction]map[Command]Request{
			NetworkFunctionChassis: map[Command]Request{},
		},
	}

	// Built-in handlers for session management
	s.handlers[NetworkFunctionApp] = map[Command]Request{
		CommandGetAuthenticationCapabilities: s.authenticationCapabilities,
		CommandGetSessionChallenge:           s.sessionChallenge,
		CommandActivateSession:               s.sessionActivate,
		CommandSetSessionPrivilegeLevel:      s.sessionPrivilege,
		CommandCloseSession:                  s.sessionClose,
	}

	return s
}

// SetHandler sets the command handler for the given netfn and command
func (s *Simulator) SetHandler(netfn NetworkFunction, command Command, handler Request) {
	s.handlers[netfn][command] = handler
}

// NewConnection to this Simulator instance
func (s *Simulator) NewConnection() *Connection {
	addr := s.LocalAddr()
	return &Connection{
		Hostname:  addr.IP.String(),
		Port:      addr.Port,
		Interface: "lan",
	}
}

// LocalAddr returns the address the server is bound to.
func (s *Simulator) LocalAddr() *net.UDPAddr {
	if s.conn != nil {
		return s.conn.LocalAddr().(*net.UDPAddr)
	}
	return nil
}

// NetFn returns the NetworkFunction portion of the NetFn/RsLUN field
func (m *Message) NetFn() NetworkFunction {
	return NetworkFunction(m.NetFnRsLUN >> 2)
}

// Run the Simulator.
func (s *Simulator) Run() error {
	var err error
	s.conn, err = net.ListenUDP("udp4", &s.addr)
	if err != nil {
		return err
	}

	s.wg.Add(1)

	go func() {
		_ = s.serve()
		s.wg.Done()
	}()

	return nil
}

// Stop the Simulator.
func (s *Simulator) Stop() {
	_ = s.conn.Close()
	s.wg.Wait()
}

func (s *Simulator) authenticationCapabilities(*Message) Response {
	const (
		authNone = (1 << iota)
		authMD2
		authMD5
		authReserved
		authPassword
		authOEM
	)

	return struct {
		CompletionCode
		ChannelNumber             uint8
		AuthenticationTypeSupport uint8
		Status                    uint8
		Reserved                  uint8
		OEMID                     uint16
		OEMAux                    uint8
	}{
		CompletionCode:            CommandCompleted,
		ChannelNumber:             0x01,
		AuthenticationTypeSupport: authNone | authMD5 | authPassword,
	}
}

func (s *Simulator) sessionChallenge(m *Message) Response {
	// Convert username to a uint32 and use as the SessionID.
	// The SessionID will be propagated such that all requests
	// for this session include the ID, which can be used to
	// dispatch requests.
	username := bytes.TrimRight(m.Data[1:], "\000")
	hash := adler32.New()
	hash.Sum(username)
	id := hash.Sum32()
	s.ids[id] = string(username)

	return struct {
		CompletionCode
		TemporarySessionID uint32
		Challenge          [15]byte
	}{
		CompletionCode:     CommandCompleted,
		TemporarySessionID: id,
	}
}

func (s *Simulator) sessionActivate(m *Message) Response {
	return struct {
		CompletionCode
		AuthType   uint8
		SessionID  uint32
		InboundSeq uint32
		MaxPriv    uint8
	}{
		CompletionCode: CommandCompleted,
		AuthType:       m.AuthType,
		SessionID:      m.SessionID,
		InboundSeq:     m.Sequence,
		MaxPriv:        0x04, // Admin
	}
}

func (s *Simulator) sessionPrivilege(m *Message) Response {
	return struct {
		CompletionCode
		NewPrivilegeLevel uint8
	}{
		CompletionCode:    CommandCompleted,
		NewPrivilegeLevel: m.Data[0],
	}
}

func (s *Simulator) sessionClose(*Message) Response {
	return CommandCompleted
}

func (s *Simulator) write(writer io.Writer, data interface{}) {
	err := binary.Write(writer, binary.BigEndian, data)
	if err != nil {
		// shouldn't happen to a bytes.Buffer
		panic(err)
	}
}

func (s *Simulator) read(reader io.Reader, data interface{}) {
	err := binary.Read(reader, binary.BigEndian, data)
	if err != nil {
		// in this case, client gets no response or InvalidCommand
		log.Printf("binary.Read error: %s", err)
	}
}

func (s *Simulator) ipmiCommand(m *Message) []byte {
	response := Response(InvalidCommand)

	if commands, ok := s.handlers[m.NetFn()]; ok {
		if handler, ok := commands[m.Command]; ok {
			m.RequestID = s.ids[m.SessionID]
			response = handler(m)
		}
	}

	buf := new(bytes.Buffer)
	s.write(buf, &m.rmcpHeader)
	s.write(buf, &m.ipmiSession)
	if m.AuthType != 0 {
		s.write(buf, m.AuthCode)
	}
	s.write(buf, &m.ipmiHeader)
	s.write(buf, response)

	return buf.Bytes()
}

func (s *Simulator) asfCommand(m *asfMessage) []byte {
	if m.MessageType != 0x80 {
		log.Panicf("ASF message type not supported: %d", m.MessageType)
	}

	response := struct {
		IANAEnterpriseNumber  uint32
		OEM                   uint32
		SupportedEntities     uint8
		SupportedInteractions uint8
		Reserved              [6]uint8
	}{
		IANAEnterpriseNumber: m.IANAEnterpriseNumber,
		SupportedEntities:    0x81, // IPMI
	}

	buf := new(bytes.Buffer)
	s.write(buf, &m.rmcpHeader)
	s.write(buf, &m.asfHeader)
	s.write(buf, &response)

	return buf.Bytes()
}

func (s *Simulator) serve() error {
	buf := make([]byte, 1024)
	ipmiHeaderSize := binary.Size(ipmiHeader{})

	for {
		var header rmcpHeader
		var response []byte
		var err error

		n, addr, err := s.conn.ReadFrom(buf)
		if err != nil {
			return err // conn closed
		}

		reader := bytes.NewReader(buf[:n])

		s.read(reader, &header)

		switch header.Class {
		case 6:
			m := &asfMessage{
				rmcpHeader: header,
			}

			s.read(reader, &m.asfHeader)

			response = s.asfCommand(m)
		case 7:
			m := &Message{
				rmcpHeader: header,
			}

			s.read(reader, &m.ipmiSession)
			if m.AuthType != 0 {
				s.read(reader, &m.AuthCode)
			}
			s.read(reader, &m.ipmiHeader)

			dataLen := int(m.MsgLen) - ipmiHeaderSize
			m.Data = make([]byte, dataLen)
			_, _ = reader.Read(m.Data)

			response = s.ipmiCommand(m)
		default:
			log.Printf("Unsupported Class: %d", header.Class)
			continue
		}

		_, err = s.conn.WriteTo(response, addr)
		if err != nil {
			return err // conn closed
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
)

var iconDirectory string

func iconDir() string {
	if iconDirectory != "" {
		return iconDirectory
	}
	u, err := user.Current()
	if err != nil {
		log.Fatal("failed to get home directory: ", err)
	}
	dir := filepath.Join(u.HomeDir, ".config")
	if runtime.GOOS == "windows" {
		dir = os.Getenv("APPDATA")
	}
	iconDirectory = filepath.Join(dir, "gomon")
	return iconDirectory
}

func icon(name string) string {
	f := filepath.Join(iconDir(), name+".png")
	if _, err := FileExists(f); err == nil {
		return f
	}
	return ""

}

func download(target, path string) {
	r, err := http.Get(target)
	if err != nil {
		log.Fatal("failed to download file: ", err)
	}
	defer r.Body.Close()
	out, err := os.Create(path)
	if err != nil {
		log.Fatal("failed to create file: ", err)
	}
	defer out.Close()
	io.Copy(out, r.Body)
	fmt.Printf("Downloaded %s into %s\n", target, path)
}

func installGrowlIcons() {
	dir := iconDir()
	_, err := os.Stat(dir)
	if err != nil {
		if os.Mkdir(dir, 0700) != nil {
			log.Fatal("failed to create directory: ", err)
		}
	}
	download(
		"https://raw.github.com/c9s/gomon/gh-pages/icons/success.png",
		filepath.Join(iconDir(), "success.png"))
	download(
		"https://raw.github.com/c9s/gomon/gh-pages/icons/failed.png",
		filepath.Join(iconDir(), "failed.png"))
}

<commit_msg>extract getConfigDir method out<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
)

var iconDirectory string

func getConfigDir() string {
	u, err := user.Current()
	if err != nil {
		log.Fatal("failed to get home directory: ", err)
	}
	return filepath.Join(u.HomeDir, ".gomon")
}

func getIconDir() string {
	if iconDirectory != "" {
		return iconDirectory
	}
	var dir string
	if runtime.GOOS == "windows" {
		dir = os.Getenv("APPDATA")
	} else {
		dir = getConfigDir()
	}
	iconDirectory = filepath.Join(dir, "gomon")
	return iconDirectory
}

func icon(name string) string {
	f := filepath.Join(getIconDir(), name+".png")
	if _, err := FileExists(f); err == nil {
		return f
	}
	return ""

}

func download(target, path string) {
	r, err := http.Get(target)
	if err != nil {
		log.Fatal("failed to download file: ", err)
	}
	defer r.Body.Close()
	out, err := os.Create(path)
	if err != nil {
		log.Fatal("failed to create file: ", err)
	}
	defer out.Close()
	io.Copy(out, r.Body)
	fmt.Printf("Downloaded %s into %s\n", target, path)
}

func installGrowlIcons() {
	dir := getIconDir()
	_, err := os.Stat(dir)
	if err != nil {
		if os.Mkdir(dir, 0700) != nil {
			log.Fatal("failed to create directory: ", err)
		}
	}
	download(
		"https://raw.github.com/c9s/gomon/gh-pages/icons/success.png",
		filepath.Join(getIconDir(), "success.png"))
	download(
		"https://raw.github.com/c9s/gomon/gh-pages/icons/failed.png",
		filepath.Join(getIconDir(), "failed.png"))
}
<|endoftext|>


<commit_before>package apps

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	. "github.com/pivotal-cf-experimental/cf-test-helpers/cf"
	. "github.com/pivotal-cf-experimental/cf-test-helpers/generator"
	"fmt"
	"time"
)

var _ = Describe("Riak CS Service", func() {
	BeforeEach(func() {
		AppName = RandomName()

		Expect(Cf("push", AppName, "-m", "256M", "-p", sinatraPath, "-no-start")).To(ExitWithTimeout(0, 60*time.Second))
	})

	AfterEach(func() {
		Expect(Cf("delete", AppName, "-f")).To(ExitWithTimeout(0, 20*time.Second))
	})

	It("Allows users to create, bind, write to, read from, unbind, and destroy the service instance", func() {
		ServiceName := "riak-cs"
		PlanName := "bucket"
		ServiceInstanceName := RandomName()

		Expect(Cf("create-service", ServiceName, PlanName, ServiceInstanceName)).To(ExitWithTimeout(0, 60*time.Second))
		Expect(Cf("bind-service", AppName, ServiceInstanceName)).To(ExitWithTimeout(0, 60*time.Second))
		Expect(Cf("start", AppName)).To(ExitWithTimeout(0, 120*time.Second))

		uri := AppUri(AppName) + "/service/blobstore/" + ServiceInstanceName + "/mykey"
		delete_uri := AppUri(AppName) + "/service/blobstore/" + ServiceInstanceName

		fmt.Println("Posting to url: ", uri)
		Eventually(Curling("-d", "myvalue", uri), 10.0, 1.0).Should(Say("myvalue"))
		fmt.Println("\n")

		fmt.Println("Curling url: ", uri)
		Eventually(Curling(uri), 10.0, 1.0).Should(Say("myvalue"))
		fmt.Println("\n")

		fmt.Println("Sending delete to: ", delete_uri)
		Eventually(Curling("-X", "DELETE", delete_uri), 10.0, 1.0).Should(Say(""))
		fmt.Println("\n")

		Expect(Cf("unbind-service", AppName, ServiceInstanceName)).To(ExitWithTimeout(0, 20*time.Second))
		Expect(Cf("delete-service", "-f", ServiceInstanceName)).To(ExitWithTimeout(0, 20*time.Second))
	})
})
<commit_msg>increase timeout for starting app in integration test<commit_after>package apps

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	. "github.com/pivotal-cf-experimental/cf-test-helpers/cf"
	. "github.com/pivotal-cf-experimental/cf-test-helpers/generator"
	"fmt"
	"time"
)

var _ = Describe("Riak CS Service", func() {
	BeforeEach(func() {
		AppName = RandomName()

		Expect(Cf("push", AppName, "-m", "256M", "-p", sinatraPath, "-no-start")).To(ExitWithTimeout(0, 60*time.Second))
	})

	AfterEach(func() {
		Expect(Cf("delete", AppName, "-f")).To(ExitWithTimeout(0, 20*time.Second))
	})

	It("Allows users to create, bind, write to, read from, unbind, and destroy the service instance", func() {
		ServiceName := "riak-cs"
		PlanName := "bucket"
		ServiceInstanceName := RandomName()

		Expect(Cf("create-service", ServiceName, PlanName, ServiceInstanceName)).To(ExitWithTimeout(0, 60*time.Second))
		Expect(Cf("bind-service", AppName, ServiceInstanceName)).To(ExitWithTimeout(0, 60*time.Second))
		Expect(Cf("start", AppName)).To(ExitWithTimeout(0, 5*60*time.Second))

		uri := AppUri(AppName) + "/service/blobstore/" + ServiceInstanceName + "/mykey"
		delete_uri := AppUri(AppName) + "/service/blobstore/" + ServiceInstanceName

		fmt.Println("Posting to url: ", uri)
		Eventually(Curling("-d", "myvalue", uri), 10.0, 1.0).Should(Say("myvalue"))
		fmt.Println("\n")

		fmt.Println("Curling url: ", uri)
		Eventually(Curling(uri), 10.0, 1.0).Should(Say("myvalue"))
		fmt.Println("\n")

		fmt.Println("Sending delete to: ", delete_uri)
		Eventually(Curling("-X", "DELETE", delete_uri), 10.0, 1.0).Should(Say(""))
		fmt.Println("\n")

		Expect(Cf("unbind-service", AppName, ServiceInstanceName)).To(ExitWithTimeout(0, 20*time.Second))
		Expect(Cf("delete-service", "-f", ServiceInstanceName)).To(ExitWithTimeout(0, 20*time.Second))
	})
})
<|endoftext|>


<commit_before>package conngater

import (
	"net"
	"sync"

	"github.com/libp2p/go-libp2p-core/connmgr"
	"github.com/libp2p/go-libp2p-core/control"
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"

	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr/net"

	"github.com/ipfs/go-datastore"
	"github.com/ipfs/go-datastore/namespace"
	"github.com/ipfs/go-datastore/query"
	logging "github.com/ipfs/go-log"
)

type BasicConnectionGater struct {
	sync.RWMutex

	blockedPeers   map[peer.ID]struct{}
	blockedAddrs   map[string]struct{}
	blockedSubnets map[string]*net.IPNet

	ds datastore.Datastore
}

var log = logging.Logger("net/conngater")

const (
	ns        = "/libp2p/net/conngater"
	keyPeer   = "/peer/"
	keyAddr   = "/addr/"
	keySubnet = "/subnet/"
)

// NewBasicConnectionGater creates a new connection gater.
// The ds argument is an (optional, can be nil) datastore to persist the connection gater
// filters
func NewBasicConnectionGater(ds datastore.Datastore) *BasicConnectionGater {
	cg := &BasicConnectionGater{
		blockedPeers:   make(map[peer.ID]struct{}),
		blockedAddrs:   make(map[string]struct{}),
		blockedSubnets: make(map[string]*net.IPNet),
	}

	if ds != nil {
		cg.ds = namespace.Wrap(ds, datastore.NewKey(ns))
		cg.loadRules()
	}

	return cg
}

func (cg *BasicConnectionGater) loadRules() {
	// load blocked peers
	res, err := cg.ds.Query(query.Query{Prefix: keyPeer})
	if err != nil {
		log.Errorf("error querying datastore for blocked peers: %s", err)
		return
	}

	for r := range res.Next() {
		if r.Error != nil {
			log.Errorf("query result error: %s", r.Error)
			return
		}

		p := peer.ID(r.Entry.Value)
		cg.blockedPeers[p] = struct{}{}
	}

	// load blocked addrs
	res, err = cg.ds.Query(query.Query{Prefix: keyAddr})
	if err != nil {
		log.Errorf("error querying datastore for blocked addrs: %s", err)
		return
	}

	for r := range res.Next() {
		if r.Error != nil {
			log.Errorf("query result error: %s", r.Error)
			return
		}

		ip := net.IP(r.Entry.Value)
		cg.blockedAddrs[ip.String()] = struct{}{}
	}

	// load blocked subnets
	res, err = cg.ds.Query(query.Query{Prefix: keySubnet})
	if err != nil {
		log.Errorf("error querying datastore for blocked subnets: %s", err)
		return
	}

	for r := range res.Next() {
		if r.Error != nil {
			log.Errorf("query result error: %s", r.Error)
			return
		}

		ipnetStr := string(r.Entry.Value)
		_, ipnet, err := net.ParseCIDR(ipnetStr)
		if err != nil {
			log.Errorf("error parsing CIDR subnet: %s", err)
			return
		}
		cg.blockedSubnets[ipnetStr] = ipnet
	}
}

// BlockPeer adds a peer to the set of blocked peers
func (cg *BasicConnectionGater) BlockPeer(p peer.ID) {
	cg.Lock()
	defer cg.Unlock()

	cg.blockedPeers[p] = struct{}{}

	if cg.ds != nil {
		err := cg.ds.Put(datastore.NewKey(keyPeer+p.String()), []byte(p))
		if err != nil {
			log.Errorf("error writing blocked peer to datastore: %s", err)
		}
	}
}

// UnblockPeer removes a peer from the set of blocked peers
func (cg *BasicConnectionGater) UnblockPeer(p peer.ID) {
	cg.Lock()
	defer cg.Unlock()

	delete(cg.blockedPeers, p)

	if cg.ds != nil {
		err := cg.ds.Delete(datastore.NewKey(keyPeer + p.String()))
		if err != nil {
			log.Errorf("error deleting blocked peer from datastore: %s", err)
		}
	}
}

// ListBlockedPeers return a list of blocked peers
func (cg *BasicConnectionGater) ListBlockedPeers() []peer.ID {
	cg.RLock()
	defer cg.RUnlock()

	result := make([]peer.ID, 0, len(cg.blockedPeers))
	for p := range cg.blockedPeers {
		result = append(result, p)
	}

	return result
}

// BlockAddr adds an IP address to the set of blocked addresses
func (cg *BasicConnectionGater) BlockAddr(ip net.IP) {
	cg.Lock()
	defer cg.Unlock()

	cg.blockedAddrs[ip.String()] = struct{}{}

	if cg.ds != nil {
		err := cg.ds.Put(datastore.NewKey(keyAddr+ip.String()), []byte(ip))
		if err != nil {
			log.Errorf("error writing blocked addr to datastore: %s", err)
		}
	}
}

// UnblockAddr removes an IP address from the set of blocked addresses
func (cg *BasicConnectionGater) UnblockAddr(ip net.IP) {
	cg.Lock()
	defer cg.Unlock()

	delete(cg.blockedAddrs, ip.String())

	if cg.ds != nil {
		err := cg.ds.Delete(datastore.NewKey(keyAddr + ip.String()))
		if err != nil {
			log.Errorf("error deleting blocked addr from datastore: %s", err)
		}
	}
}

// ListBlockedAddrs return a list of blocked IP addresses
func (cg *BasicConnectionGater) ListBlockedAddrs() []net.IP {
	cg.RLock()
	defer cg.RUnlock()

	result := make([]net.IP, 0, len(cg.blockedAddrs))
	for ipStr := range cg.blockedAddrs {
		ip := net.ParseIP(ipStr)
		result = append(result, ip)
	}

	return result
}

// BlockSubnet adds an IP subnet to the set of blocked addresses
func (cg *BasicConnectionGater) BlockSubnet(ipnet *net.IPNet) {
	cg.Lock()
	defer cg.Unlock()

	cg.blockedSubnets[ipnet.String()] = ipnet

	if cg.ds != nil {
		err := cg.ds.Put(datastore.NewKey(keySubnet+ipnet.String()), []byte(ipnet.String()))
		if err != nil {
			log.Errorf("error writing blocked addr to datastore: %s", err)
		}
	}
}

// UnblockSubnet removes an IP address from the set of blocked addresses
func (cg *BasicConnectionGater) UnblockSubnet(ipnet *net.IPNet) {
	cg.Lock()
	defer cg.Unlock()

	delete(cg.blockedSubnets, ipnet.String())

	if cg.ds != nil {
		err := cg.ds.Delete(datastore.NewKey(keySubnet + ipnet.String()))
		if err != nil {
			log.Errorf("error deleting blocked subnet from datastore: %s", err)
		}
	}
}

// ListBlockedSubnets return a list of blocked IP subnets
func (cg *BasicConnectionGater) ListBlockedSubnets() []*net.IPNet {
	cg.RLock()
	defer cg.RUnlock()

	result := make([]*net.IPNet, 0, len(cg.blockedSubnets))
	for _, ipnet := range cg.blockedSubnets {
		result = append(result, ipnet)
	}

	return result
}

// ConnectionGater interface
var _ connmgr.ConnectionGater = (*BasicConnectionGater)(nil)

func (cg *BasicConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
	cg.RLock()
	defer cg.RUnlock()

	_, block := cg.blockedPeers[p]
	return !block
}

func (cg *BasicConnectionGater) InterceptAddrDial(p peer.ID, a ma.Multiaddr) (allow bool) {
	// we have already filrted blocked peersin InterceptPeerDial, so we just check the IP
	cg.RLock()
	defer cg.RUnlock()

	ip, err := manet.ToIP(a)
	if err != nil {
		log.Warnf("error converting multiaddr to IP addr: %s", err)
		return true
	}

	_, block := cg.blockedAddrs[ip.String()]
	if block {
		return false
	}

	for _, ipnet := range cg.blockedSubnets {
		if ipnet.Contains(ip) {
			return false
		}
	}

	return true
}

func (cg *BasicConnectionGater) InterceptAccept(cma network.ConnMultiaddrs) (allow bool) {
	cg.RLock()
	defer cg.RUnlock()

	a := cma.RemoteMultiaddr()

	ip, err := manet.ToIP(a)
	if err != nil {
		log.Warnf("error converting multiaddr to IP addr: %s", err)
		return true
	}

	_, block := cg.blockedAddrs[ip.String()]
	if block {
		return false
	}

	for _, ipnet := range cg.blockedSubnets {
		if ipnet.Contains(ip) {
			return false
		}
	}

	return true
}

func (cg *BasicConnectionGater) InterceptSecured(dir network.Direction, p peer.ID, cma network.ConnMultiaddrs) (allow bool) {
	if dir == network.DirOutbound {
		// we have already filtered those in InterceptPeerDial/InterceptAddrDial
		return true
	}

	// we have already filtered addrs in InterceptAccept, so we just check the peer ID
	cg.RLock()
	defer cg.RUnlock()

	_, block := cg.blockedPeers[p]
	return !block
}

func (cg *BasicConnectionGater) InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason) {
	return true, 0
}
<commit_msg>propagate errors from datastore ops<commit_after>package conngater

import (
	"net"
	"sync"

	"github.com/libp2p/go-libp2p-core/connmgr"
	"github.com/libp2p/go-libp2p-core/control"
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"

	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr/net"

	"github.com/ipfs/go-datastore"
	"github.com/ipfs/go-datastore/namespace"
	"github.com/ipfs/go-datastore/query"
	logging "github.com/ipfs/go-log"
)

type BasicConnectionGater struct {
	sync.RWMutex

	blockedPeers   map[peer.ID]struct{}
	blockedAddrs   map[string]struct{}
	blockedSubnets map[string]*net.IPNet

	ds datastore.Datastore
}

var log = logging.Logger("net/conngater")

const (
	ns        = "/libp2p/net/conngater"
	keyPeer   = "/peer/"
	keyAddr   = "/addr/"
	keySubnet = "/subnet/"
)

// NewBasicConnectionGater creates a new connection gater.
// The ds argument is an (optional, can be nil) datastore to persist the connection gater
// filters
func NewBasicConnectionGater(ds datastore.Datastore) (*BasicConnectionGater, error) {
	cg := &BasicConnectionGater{
		blockedPeers:   make(map[peer.ID]struct{}),
		blockedAddrs:   make(map[string]struct{}),
		blockedSubnets: make(map[string]*net.IPNet),
	}

	if ds != nil {
		cg.ds = namespace.Wrap(ds, datastore.NewKey(ns))
		err := cg.loadRules()
		if err != nil {
			return nil, err
		}
	}

	return cg, nil
}

func (cg *BasicConnectionGater) loadRules() error {
	// load blocked peers
	res, err := cg.ds.Query(query.Query{Prefix: keyPeer})
	if err != nil {
		log.Errorf("error querying datastore for blocked peers: %s", err)
		return err
	}

	for r := range res.Next() {
		if r.Error != nil {
			log.Errorf("query result error: %s", r.Error)
			return err
		}

		p := peer.ID(r.Entry.Value)
		cg.blockedPeers[p] = struct{}{}
	}

	// load blocked addrs
	res, err = cg.ds.Query(query.Query{Prefix: keyAddr})
	if err != nil {
		log.Errorf("error querying datastore for blocked addrs: %s", err)
		return err
	}

	for r := range res.Next() {
		if r.Error != nil {
			log.Errorf("query result error: %s", r.Error)
			return err
		}

		ip := net.IP(r.Entry.Value)
		cg.blockedAddrs[ip.String()] = struct{}{}
	}

	// load blocked subnets
	res, err = cg.ds.Query(query.Query{Prefix: keySubnet})
	if err != nil {
		log.Errorf("error querying datastore for blocked subnets: %s", err)
		return err
	}

	for r := range res.Next() {
		if r.Error != nil {
			log.Errorf("query result error: %s", r.Error)
			return err
		}

		ipnetStr := string(r.Entry.Value)
		_, ipnet, err := net.ParseCIDR(ipnetStr)
		if err != nil {
			log.Errorf("error parsing CIDR subnet: %s", err)
			return err
		}
		cg.blockedSubnets[ipnetStr] = ipnet
	}

	return nil
}

// BlockPeer adds a peer to the set of blocked peers
func (cg *BasicConnectionGater) BlockPeer(p peer.ID) error {
	if cg.ds != nil {
		err := cg.ds.Put(datastore.NewKey(keyPeer+p.String()), []byte(p))
		if err != nil {
			log.Errorf("error writing blocked peer to datastore: %s", err)
			return err
		}
	}

	cg.Lock()
	defer cg.Unlock()
	cg.blockedPeers[p] = struct{}{}

	return nil
}

// UnblockPeer removes a peer from the set of blocked peers
func (cg *BasicConnectionGater) UnblockPeer(p peer.ID) error {
	if cg.ds != nil {
		err := cg.ds.Delete(datastore.NewKey(keyPeer + p.String()))
		if err != nil {
			log.Errorf("error deleting blocked peer from datastore: %s", err)
			return err
		}
	}

	cg.Lock()
	defer cg.Unlock()

	delete(cg.blockedPeers, p)

	return nil
}

// ListBlockedPeers return a list of blocked peers
func (cg *BasicConnectionGater) ListBlockedPeers() []peer.ID {
	cg.RLock()
	defer cg.RUnlock()

	result := make([]peer.ID, 0, len(cg.blockedPeers))
	for p := range cg.blockedPeers {
		result = append(result, p)
	}

	return result
}

// BlockAddr adds an IP address to the set of blocked addresses
func (cg *BasicConnectionGater) BlockAddr(ip net.IP) error {
	if cg.ds != nil {
		err := cg.ds.Put(datastore.NewKey(keyAddr+ip.String()), []byte(ip))
		if err != nil {
			log.Errorf("error writing blocked addr to datastore: %s", err)
			return err
		}
	}

	cg.Lock()
	defer cg.Unlock()

	cg.blockedAddrs[ip.String()] = struct{}{}

	return nil
}

// UnblockAddr removes an IP address from the set of blocked addresses
func (cg *BasicConnectionGater) UnblockAddr(ip net.IP) error {
	if cg.ds != nil {
		err := cg.ds.Delete(datastore.NewKey(keyAddr + ip.String()))
		if err != nil {
			log.Errorf("error deleting blocked addr from datastore: %s", err)
			return err
		}
	}

	cg.Lock()
	defer cg.Unlock()

	delete(cg.blockedAddrs, ip.String())

	return nil
}

// ListBlockedAddrs return a list of blocked IP addresses
func (cg *BasicConnectionGater) ListBlockedAddrs() []net.IP {
	cg.RLock()
	defer cg.RUnlock()

	result := make([]net.IP, 0, len(cg.blockedAddrs))
	for ipStr := range cg.blockedAddrs {
		ip := net.ParseIP(ipStr)
		result = append(result, ip)
	}

	return result
}

// BlockSubnet adds an IP subnet to the set of blocked addresses
func (cg *BasicConnectionGater) BlockSubnet(ipnet *net.IPNet) error {
	if cg.ds != nil {
		err := cg.ds.Put(datastore.NewKey(keySubnet+ipnet.String()), []byte(ipnet.String()))
		if err != nil {
			log.Errorf("error writing blocked addr to datastore: %s", err)
			return err
		}
	}

	cg.Lock()
	defer cg.Unlock()

	cg.blockedSubnets[ipnet.String()] = ipnet

	return nil
}

// UnblockSubnet removes an IP address from the set of blocked addresses
func (cg *BasicConnectionGater) UnblockSubnet(ipnet *net.IPNet) error {
	if cg.ds != nil {
		err := cg.ds.Delete(datastore.NewKey(keySubnet + ipnet.String()))
		if err != nil {
			log.Errorf("error deleting blocked subnet from datastore: %s", err)
			return err
		}
	}

	cg.Lock()
	defer cg.Unlock()

	delete(cg.blockedSubnets, ipnet.String())

	return nil
}

// ListBlockedSubnets return a list of blocked IP subnets
func (cg *BasicConnectionGater) ListBlockedSubnets() []*net.IPNet {
	cg.RLock()
	defer cg.RUnlock()

	result := make([]*net.IPNet, 0, len(cg.blockedSubnets))
	for _, ipnet := range cg.blockedSubnets {
		result = append(result, ipnet)
	}

	return result
}

// ConnectionGater interface
var _ connmgr.ConnectionGater = (*BasicConnectionGater)(nil)

func (cg *BasicConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
	cg.RLock()
	defer cg.RUnlock()

	_, block := cg.blockedPeers[p]
	return !block
}

func (cg *BasicConnectionGater) InterceptAddrDial(p peer.ID, a ma.Multiaddr) (allow bool) {
	// we have already filrted blocked peersin InterceptPeerDial, so we just check the IP
	cg.RLock()
	defer cg.RUnlock()

	ip, err := manet.ToIP(a)
	if err != nil {
		log.Warnf("error converting multiaddr to IP addr: %s", err)
		return true
	}

	_, block := cg.blockedAddrs[ip.String()]
	if block {
		return false
	}

	for _, ipnet := range cg.blockedSubnets {
		if ipnet.Contains(ip) {
			return false
		}
	}

	return true
}

func (cg *BasicConnectionGater) InterceptAccept(cma network.ConnMultiaddrs) (allow bool) {
	cg.RLock()
	defer cg.RUnlock()

	a := cma.RemoteMultiaddr()

	ip, err := manet.ToIP(a)
	if err != nil {
		log.Warnf("error converting multiaddr to IP addr: %s", err)
		return true
	}

	_, block := cg.blockedAddrs[ip.String()]
	if block {
		return false
	}

	for _, ipnet := range cg.blockedSubnets {
		if ipnet.Contains(ip) {
			return false
		}
	}

	return true
}

func (cg *BasicConnectionGater) InterceptSecured(dir network.Direction, p peer.ID, cma network.ConnMultiaddrs) (allow bool) {
	if dir == network.DirOutbound {
		// we have already filtered those in InterceptPeerDial/InterceptAddrDial
		return true
	}

	// we have already filtered addrs in InterceptAccept, so we just check the peer ID
	cg.RLock()
	defer cg.RUnlock()

	_, block := cg.blockedPeers[p]
	return !block
}

func (cg *BasicConnectionGater) InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason) {
	return true, 0
}
<|endoftext|>