<commit_before>package validate

import (
	"math"
	"net/url"
	"reflect"
	"strconv"
	"testing"

	"github.com/casualjim/go-swagger"
	"github.com/casualjim/go-swagger/errors"
	"github.com/casualjim/go-swagger/spec"
	"github.com/casualjim/go-swagger/strfmt"
	"github.com/stretchr/testify/assert"
)

type email struct {
	Address string
}

type paramFactory func(string) *spec.Parameter

var paramFactories = []paramFactory{
	spec.QueryParam,
	spec.HeaderParam,
	spec.PathParam,
	spec.FormDataParam,
}

func np(param *spec.Parameter) *paramBinder {
	return newParamBinder(*param, new(spec.Swagger), strfmt.Default)
}

var stringItems = new(spec.Items)

func init() {
	stringItems.Type = "string"
}

func testCollectionFormat(t *testing.T, param *spec.Parameter, valid bool) {
	binder := &paramBinder{
		parameter: param,
	}
	_, _, err := binder.readValue(url.Values(nil), reflect.ValueOf(nil))
	if valid {
		assert.NoError(t, err)
	} else {
		assert.Error(t, err)
		assert.Equal(t, errors.InvalidCollectionFormat(param.Name, param.In, param.CollectionFormat), err)
	}
}

func requiredError(param *spec.Parameter) *errors.Validation {
	return errors.Required(param.Name, param.In)
}

func validateRequiredTest(t *testing.T, param *spec.Parameter, value reflect.Value) {
	binder := np(param)
	err := binder.bindValue([]string{}, value)
	assert.Error(t, err)
	assert.EqualError(t, requiredError(param), err.Error())
	err = binder.bindValue([]string{""}, value)
	assert.Error(t, err)
	assert.EqualError(t, requiredError(param), err.Error())
}

func TestRequiredValidation(t *testing.T) {
	strParam := spec.QueryParam("name").Typed("string", "").AsRequired()
	validateRequiredTest(t, strParam, reflect.ValueOf(""))

	intParam := spec.QueryParam("id").Typed("integer", "int32").AsRequired()
	validateRequiredTest(t, intParam, reflect.ValueOf(int32(0)))
	longParam := spec.QueryParam("id").Typed("integer", "int64").AsRequired()
	validateRequiredTest(t, longParam, reflect.ValueOf(int64(0)))

	floatParam := spec.QueryParam("score").Typed("number", "float").AsRequired()
	validateRequiredTest(t, floatParam, reflect.ValueOf(float32(0)))
	doubleParam := spec.QueryParam("score").Typed("number", "double").AsRequired()
	validateRequiredTest(t, doubleParam, reflect.ValueOf(float64(0)))

	dateTimeParam := spec.QueryParam("registered").Typed("string", "date-time").AsRequired()
	validateRequiredTest(t, dateTimeParam, reflect.ValueOf(swagger.DateTime{}))
	dateParam := spec.QueryParam("registered").Typed("string", "date").AsRequired()
	validateRequiredTest(t, dateParam, reflect.ValueOf(swagger.DateTime{}))

	sliceParam := spec.QueryParam("tags").CollectionOf(stringItems, "").AsRequired()
	validateRequiredTest(t, sliceParam, reflect.ValueOf([]string{}))
}

func TestInvalidCollectionFormat(t *testing.T) {
	validCf1 := spec.QueryParam("validFmt").CollectionOf(stringItems, "multi")
	validCf2 := spec.FormDataParam("validFmt2").CollectionOf(stringItems, "multi")
	invalidCf1 := spec.HeaderParam("invalidHdr").CollectionOf(stringItems, "multi")
	invalidCf2 := spec.PathParam("invalidPath").CollectionOf(stringItems, "multi")

	testCollectionFormat(t, validCf1, true)
	testCollectionFormat(t, validCf2, true)
	testCollectionFormat(t, invalidCf1, false)
	testCollectionFormat(t, invalidCf2, false)
}

func invalidTypeError(param *spec.Parameter, data interface{}) *errors.Validation {
	tpe := param.Type
	if param.Format != "" {
		tpe = param.Format
	}
	return errors.InvalidType(param.Name, param.In, tpe, data)
}

func TestTypeValidation(t *testing.T) {
	for _, newParam := range paramFactories {
		intParam := newParam("badInt").Typed("integer", "int32")
		value := reflect.ValueOf(int32(0))
		binder := np(intParam)
		err := binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(intParam, "yada"), err)
		// fails for overflow
		val := int64(math.MaxInt32)
		str := strconv.FormatInt(val, 10) + "0"
		v := int32(0)
		value = reflect.ValueOf(&v).Elem()
		binder = np(intParam)
		err = binder.bindValue([]string{str}, value)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(intParam, str), err)

		longParam := newParam("badLong").Typed("integer", "int64")
		value = reflect.ValueOf(int64(0))
		binder = np(longParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(longParam, "yada"), err)
		// fails for overflow
		str2 := strconv.FormatInt(math.MaxInt64, 10) + "0"
		v2 := int64(0)
		vv2 := reflect.ValueOf(&v2).Elem()
		binder = np(longParam)
		err = binder.bindValue([]string{str2}, vv2)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(longParam, str2), err)

		floatParam := newParam("badFloat").Typed("number", "float")
		value = reflect.ValueOf(float64(0))
		binder = np(floatParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(floatParam, "yada"), err)
		// fails for overflow
		str3 := strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
		v3 := reflect.TypeOf(float32(0))
		value = reflect.New(v3).Elem()
		binder = np(floatParam)
		err = binder.bindValue([]string{str3}, value)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(floatParam, str3), err)

		doubleParam := newParam("badDouble").Typed("number", "double")
		value = reflect.ValueOf(float64(0))
		binder = np(doubleParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(doubleParam, "yada"), err)
		// fails for overflow
		str4 := "9" + strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
		v4 := reflect.TypeOf(float64(0))
		value = reflect.New(v4).Elem()
		binder = np(doubleParam)
		err = binder.bindValue([]string{str4}, value)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(doubleParam, str4), err)

		dateParam := newParam("badDate").Typed("string", "date")
		value = reflect.ValueOf(swagger.Date{})
		binder = np(dateParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(dateParam, "yada"), err)

		dateTimeParam := newParam("badDateTime").Typed("string", "date-time")
		value = reflect.ValueOf(swagger.DateTime{})
		binder = np(dateTimeParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(dateTimeParam, "yada"), err)

		byteParam := newParam("badByte").Typed("string", "byte")
		values := url.Values(map[string][]string{})
		values.Add("badByte", "yaüda")
		v5 := []byte{}
		value = reflect.ValueOf(&v5).Elem()
		binder = np(byteParam)
		err = binder.bindValue([]string{"yaüda"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(byteParam, "yaüda"), err)
	}
}

func TestTypeDetectionInvalidItems(t *testing.T) {
	withoutItems := spec.QueryParam("without").CollectionOf(nil, "")
	binder := &paramBinder{
		name:      "without",
		parameter: withoutItems,
	}
	assert.Nil(t, binder.Type())

	items := new(spec.Items)
	items.Type = "array"
	withInvalidItems := spec.QueryParam("invalidItems").CollectionOf(items, "")
	binder = &paramBinder{
		name:      "invalidItems",
		parameter: withInvalidItems,
	}
	assert.Nil(t, binder.Type())

	noType := spec.QueryParam("invalidType")
	noType.Type = "invalid"
	binder = &paramBinder{
		name:      "invalidType",
		parameter: noType,
	}
	assert.Nil(t, binder.Type())
}

// type emailStrFmt struct {
// 	name      string
// 	tpe       reflect.Type
// 	validator FormatValidator
// }
//
// func (e *emailStrFmt) Name() string {
// 	return e.name
// }
//
// func (e *emailStrFmt) Type() reflect.Type {
// 	return e.tpe
// }
//
// func (e *emailStrFmt) Matches(str string) bool {
// 	return e.validator(str)
// }
//
// func TestTypeDetectionValid(t *testing.T) {
// 	// emlFmt := &emailStrFmt{
// 	// 	name: "email",
// 	// 	tpe:  reflect.TypeOf(email{}),
// 	// }
// 	// formats := []StringFormat{emlFmt}
//
// 	expected := map[string]reflect.Type{
// 		"name":         reflect.TypeOf(""),
// 		"id":           reflect.TypeOf(int64(0)),
// 		"age":          reflect.TypeOf(int32(0)),
// 		"score":        reflect.TypeOf(float32(0)),
// 		"factor":       reflect.TypeOf(float64(0)),
// 		"friend":       reflect.TypeOf(map[string]interface{}{}),
// 		"X-Request-Id": reflect.TypeOf(int64(0)),
// 		"tags":         reflect.TypeOf([]string{}),
// 		"confirmed":    reflect.TypeOf(true),
// 		"planned":      reflect.TypeOf(swagger.Date{}),
// 		"delivered":    reflect.TypeOf(swagger.DateTime{}),
// 		"email":        reflect.TypeOf(email{}),
// 		"picture":      reflect.TypeOf([]byte{}),
// 		"file":         reflect.TypeOf(&swagger.File{}).Elem(),
// 	}
//
// 	params := parametersForAllTypes("")
// 	emailParam := spec.QueryParam("email").Typed("string", "email")
// 	params["email"] = *emailParam
//
// 	fileParam := spec.FileParam("file")
// 	params["file"] = *fileParam
//
// 	for _, v := range params {
// 		binder := &paramBinder{
// 			formats:   formats,
// 			name:      v.Name,
// 			parameter: &v,
// 		}
// 		assert.Equal(t, expected[v.Name], binder.Type(), "name: %s", v.Name)
// 	}
// }
<commit_msg>fix remaining missing types<commit_after>package validate

import (
	"math"
	"net/url"
	"reflect"
	"strconv"
	"testing"

	"github.com/casualjim/go-swagger/errors"
	"github.com/casualjim/go-swagger/spec"
	"github.com/casualjim/go-swagger/strfmt"
	"github.com/stretchr/testify/assert"
)

type email struct {
	Address string
}

type paramFactory func(string) *spec.Parameter

var paramFactories = []paramFactory{
	spec.QueryParam,
	spec.HeaderParam,
	spec.PathParam,
	spec.FormDataParam,
}

func np(param *spec.Parameter) *paramBinder {
	return newParamBinder(*param, new(spec.Swagger), strfmt.Default)
}

var stringItems = new(spec.Items)

func init() {
	stringItems.Type = "string"
}

func testCollectionFormat(t *testing.T, param *spec.Parameter, valid bool) {
	binder := &paramBinder{
		parameter: param,
	}
	_, _, err := binder.readValue(url.Values(nil), reflect.ValueOf(nil))
	if valid {
		assert.NoError(t, err)
	} else {
		assert.Error(t, err)
		assert.Equal(t, errors.InvalidCollectionFormat(param.Name, param.In, param.CollectionFormat), err)
	}
}

func requiredError(param *spec.Parameter) *errors.Validation {
	return errors.Required(param.Name, param.In)
}

func validateRequiredTest(t *testing.T, param *spec.Parameter, value reflect.Value) {
	binder := np(param)
	err := binder.bindValue([]string{}, value)
	assert.Error(t, err)
	assert.EqualError(t, requiredError(param), err.Error())
	err = binder.bindValue([]string{""}, value)
	assert.Error(t, err)
	assert.EqualError(t, requiredError(param), err.Error())
}

func TestRequiredValidation(t *testing.T) {
	strParam := spec.QueryParam("name").Typed("string", "").AsRequired()
	validateRequiredTest(t, strParam, reflect.ValueOf(""))

	intParam := spec.QueryParam("id").Typed("integer", "int32").AsRequired()
	validateRequiredTest(t, intParam, reflect.ValueOf(int32(0)))
	longParam := spec.QueryParam("id").Typed("integer", "int64").AsRequired()
	validateRequiredTest(t, longParam, reflect.ValueOf(int64(0)))

	floatParam := spec.QueryParam("score").Typed("number", "float").AsRequired()
	validateRequiredTest(t, floatParam, reflect.ValueOf(float32(0)))
	doubleParam := spec.QueryParam("score").Typed("number", "double").AsRequired()
	validateRequiredTest(t, doubleParam, reflect.ValueOf(float64(0)))

	dateTimeParam := spec.QueryParam("registered").Typed("string", "date-time").AsRequired()
	validateRequiredTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{}))

	dateParam := spec.QueryParam("registered").Typed("string", "date").AsRequired()
	validateRequiredTest(t, dateParam, reflect.ValueOf(strfmt.DateTime{}))

	sliceParam := spec.QueryParam("tags").CollectionOf(stringItems, "").AsRequired()
	validateRequiredTest(t, sliceParam, reflect.ValueOf([]string{}))
}

func TestInvalidCollectionFormat(t *testing.T) {
	validCf1 := spec.QueryParam("validFmt").CollectionOf(stringItems, "multi")
	validCf2 := spec.FormDataParam("validFmt2").CollectionOf(stringItems, "multi")
	invalidCf1 := spec.HeaderParam("invalidHdr").CollectionOf(stringItems, "multi")
	invalidCf2 := spec.PathParam("invalidPath").CollectionOf(stringItems, "multi")

	testCollectionFormat(t, validCf1, true)
	testCollectionFormat(t, validCf2, true)
	testCollectionFormat(t, invalidCf1, false)
	testCollectionFormat(t, invalidCf2, false)
}

func invalidTypeError(param *spec.Parameter, data interface{}) *errors.Validation {
	tpe := param.Type
	if param.Format != "" {
		tpe = param.Format
	}
	return errors.InvalidType(param.Name, param.In, tpe, data)
}

func TestTypeValidation(t *testing.T) {
	for _, newParam := range paramFactories {
		intParam := newParam("badInt").Typed("integer", "int32")
		value := reflect.ValueOf(int32(0))
		binder := np(intParam)
		err := binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(intParam, "yada"), err)
		// fails for overflow
		val := int64(math.MaxInt32)
		str := strconv.FormatInt(val, 10) + "0"
		v := int32(0)
		value = reflect.ValueOf(&v).Elem()
		binder = np(intParam)
		err = binder.bindValue([]string{str}, value)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(intParam, str), err)

		longParam := newParam("badLong").Typed("integer", "int64")
		value = reflect.ValueOf(int64(0))
		binder = np(longParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(longParam, "yada"), err)
		// fails for overflow
		str2 := strconv.FormatInt(math.MaxInt64, 10) + "0"
		v2 := int64(0)
		vv2 := reflect.ValueOf(&v2).Elem()
		binder = np(longParam)
		err = binder.bindValue([]string{str2}, vv2)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(longParam, str2), err)

		floatParam := newParam("badFloat").Typed("number", "float")
		value = reflect.ValueOf(float64(0))
		binder = np(floatParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(floatParam, "yada"), err)
		// fails for overflow
		str3 := strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
		v3 := reflect.TypeOf(float32(0))
		value = reflect.New(v3).Elem()
		binder = np(floatParam)
		err = binder.bindValue([]string{str3}, value)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(floatParam, str3), err)

		doubleParam := newParam("badDouble").Typed("number", "double")
		value = reflect.ValueOf(float64(0))
		binder = np(doubleParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(doubleParam, "yada"), err)
		// fails for overflow
		str4 := "9" + strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
		v4 := reflect.TypeOf(float64(0))
		value = reflect.New(v4).Elem()
		binder = np(doubleParam)
		err = binder.bindValue([]string{str4}, value)
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(doubleParam, str4), err)

		dateParam := newParam("badDate").Typed("string", "date")
		value = reflect.ValueOf(strfmt.Date{})
		binder = np(dateParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(dateParam, "yada"), err)

		dateTimeParam := newParam("badDateTime").Typed("string", "date-time")
		value = reflect.ValueOf(strfmt.DateTime{})
		binder = np(dateTimeParam)
		err = binder.bindValue([]string{"yada"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(dateTimeParam, "yada"), err)

		byteParam := newParam("badByte").Typed("string", "byte")
		values := url.Values(map[string][]string{})
		values.Add("badByte", "yaüda")
		v5 := []byte{}
		value = reflect.ValueOf(&v5).Elem()
		binder = np(byteParam)
		err = binder.bindValue([]string{"yaüda"}, value)
		// fails for invalid string
		assert.Error(t, err)
		assert.Equal(t, invalidTypeError(byteParam, "yaüda"), err)
	}
}

func TestTypeDetectionInvalidItems(t *testing.T) {
	withoutItems := spec.QueryParam("without").CollectionOf(nil, "")
	binder := &paramBinder{
		name:      "without",
		parameter: withoutItems,
	}
	assert.Nil(t, binder.Type())

	items := new(spec.Items)
	items.Type = "array"
	withInvalidItems := spec.QueryParam("invalidItems").CollectionOf(items, "")
	binder = &paramBinder{
		name:      "invalidItems",
		parameter: withInvalidItems,
	}
	assert.Nil(t, binder.Type())

	noType := spec.QueryParam("invalidType")
	noType.Type = "invalid"
	binder = &paramBinder{
		name:      "invalidType",
		parameter: noType,
	}
	assert.Nil(t, binder.Type())
}

// type emailStrFmt struct {
// 	name      string
// 	tpe       reflect.Type
// 	validator FormatValidator
// }
//
// func (e *emailStrFmt) Name() string {
// 	return e.name
// }
//
// func (e *emailStrFmt) Type() reflect.Type {
// 	return e.tpe
// }
//
// func (e *emailStrFmt) Matches(str string) bool {
// 	return e.validator(str)
// }
//
// func TestTypeDetectionValid(t *testing.T) {
// 	// emlFmt := &emailStrFmt{
// 	// 	name: "email",
// 	// 	tpe:  reflect.TypeOf(email{}),
// 	// }
// 	// formats := []StringFormat{emlFmt}
//
// 	expected := map[string]reflect.Type{
// 		"name":         reflect.TypeOf(""),
// 		"id":           reflect.TypeOf(int64(0)),
// 		"age":          reflect.TypeOf(int32(0)),
// 		"score":        reflect.TypeOf(float32(0)),
// 		"factor":       reflect.TypeOf(float64(0)),
// 		"friend":       reflect.TypeOf(map[string]interface{}{}),
// 		"X-Request-Id": reflect.TypeOf(int64(0)),
// 		"tags":         reflect.TypeOf([]string{}),
// 		"confirmed":    reflect.TypeOf(true),
// 		"planned":      reflect.TypeOf(swagger.Date{}),
// 		"delivered":    reflect.TypeOf(swagger.DateTime{}),
// 		"email":        reflect.TypeOf(email{}),
// 		"picture":      reflect.TypeOf([]byte{}),
// 		"file":         reflect.TypeOf(&swagger.File{}).Elem(),
// 	}
//
// 	params := parametersForAllTypes("")
// 	emailParam := spec.QueryParam("email").Typed("string", "email")
// 	params["email"] = *emailParam
//
// 	fileParam := spec.FileParam("file")
// 	params["file"] = *fileParam
//
// 	for _, v := range params {
// 		binder := &paramBinder{
// 			formats:   formats,
// 			name:      v.Name,
// 			parameter: &v,
// 		}
// 		assert.Equal(t, expected[v.Name], binder.Type(), "name: %s", v.Name)
// 	}
// }
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"strconv"

	"github.com/codeskyblue/go-sh"
	"github.com/gorilla/mux"
	"github.com/gorilla/rpc"
	"github.com/gorilla/rpc/json"
)

const frontendHeaderACL = `acl is{{.ACL}} hdr_beg(host) {{index .HaproxyURLs #}}`
const frontendPathACL = `acl is{{.ACL}} path_beg -i {{index .HaproxyURLs #}}`

const frontendUse = `use_backend {{.Backend}} if is{{.ACL}}
`

// const frontendTmpl = `
// acl is{{.ACL}} hdr_beg(host) {{.HaproxyURL}}
// use_backend {{.Backend}} if is{{.ACL}}
// `
const backendTmpl = `
backend {{.Backend}}
  server {{.Backend}} {{.Hostmachine}}:{{.Port}} check inter 10000
`

const defaultBackendTmpl = `
  default_backend {{.Backend}}
`

var confPath = "/usr/local/etc/haproxy/conf"
var haproxyPath = "/usr/local/etc/haproxy"

// Service to be added
type Service struct {
	// ACL to be used
	ACL string
	// URL by which service will be called
	HaproxyURLs []string
	// Backend name
	Backend string
	// storefront-services-1.myntra.com
	Hostmachine string
	// Port on Hostmachine where service runs
	Port string
}

// Services ...
type Services struct {
	Services []Service
}

// Haproxy ...
type Haproxy int

// Result ...
type Result int

// Add a frontend and backend
func (h *Haproxy) Add(r *http.Request, services *Services, result *Result) error {
	var defaultBackend interface{}
	isDefaultBackedDefined := false
	for _, service := range services.Services {
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".backend").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontend").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontendtop").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontendbottom").Run()

		log.Printf("Add service %s:%s", service.HaproxyURLs, service.Port)
		data := struct {
			ACL         string
			HaproxyURLs []string
			Backend     string
			Hostmachine string
			Port        string
		}{
			strings.Title(service.ACL),
			service.HaproxyURLs,
			service.Backend,
			service.Hostmachine,
			service.Port,
		}

		frontendACLs := `
  `

		frontendType := ".frontend"

		for haproxyURLId, haproxyURL := range service.HaproxyURLs {
			if haproxyURL == "default_backend" {
				defaultBackend = data
				isDefaultBackedDefined = true
			} else if strings.EqualFold(haproxyURL, "top") || strings.EqualFold(haproxyURL, "bottom") {
				frontendType += haproxyURL
			} else if string(haproxyURL[0]) == "/" {
				frontendACLs += `  `
				frontendACLs += strings.Replace(frontendPathACL, "#", strconv.Itoa(haproxyURLId), -1)
				frontendACLs += `
`
			} else {
				frontendACLs += `  `
				frontendACLs += strings.Replace(frontendHeaderACL, "#", strconv.Itoa(haproxyURLId), -1)
				frontendACLs += `
`
			}
		}

		frontendTmpl := frontendACLs + frontendUse

		log.Println(frontendTmpl)

		// Generate frontend entry
		tmpl := template.Must(template.New("frontend").Parse(frontendTmpl))
		f, err := os.OpenFile(confPath+"/"+service.ACL+frontendType, os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			*result = 0
			return err
		}
		// fill in the template
		err = tmpl.Execute(f, data)
		if err != nil {
			*result = 0
			return err
		}

		// Generate backend entry
		tmpl = template.Must(template.New("backend").Parse(backendTmpl))
		f, err = os.OpenFile(confPath+"/"+service.ACL+".backend", os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			*result = 0
			return err
		}
		err = tmpl.Execute(f, data)
		if err != nil {
			*result = 0
			return err
		}
	}

	// Generate default_backend if needed
	if isDefaultBackedDefined {
		tmpl := template.Must(template.New("default_backend").Parse(defaultBackendTmpl))
		f, err := os.OpenFile(confPath+"/"+".default_backend", os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			*result = 0
			return err
		}
		err = tmpl.Execute(f, defaultBackend)
		if err != nil {
			*result = 0
			return err
		}
	}

	//join all the configs
	if err := h.generateCfg(); err != nil {
		*result = 0
		return err
	}

	if err := h.ReloadHaproxy(); err != nil {
		*result = 0
		return err
	}

	*result = 1
	return nil

}

// Remove a frontend and backend
func (h *Haproxy) Remove(r *http.Request, services *Services, result *Result) error {
	for _, service := range services.Services {
		log.Printf("Remove service %s:%s", service.HaproxyURLs, service.Port)
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".backend").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontend").Run()
	}

	if err := h.generateCfg(); err != nil {
		*result = 0
		return err
	}

	if err := h.ReloadHaproxy(); err != nil {
		*result = 0
		return err
	}

	*result = 1
	return nil

}

func (h *Haproxy) generateCfg() error {
	if _, err := os.Stat(haproxyPath + "/haproxy.cfg"); !os.IsNotExist(err) {
		currentTime := string(time.Now().Format("20060102150405"))
		err := os.Rename(haproxyPath+"/haproxy.cfg", haproxyPath+"/haproxy.cfg.BAK."+currentTime)
		if err != nil {
			return err
		}
	}

	var haproxyCfg []byte

	var partFunc = func(part string) {
		// walk all files in the directory
		filepath.Walk(confPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() && strings.HasSuffix(info.Name(), part) {
				b, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				haproxyCfg = append(haproxyCfg, b...)
			}
			return nil

		})
	}

	//append the configs in the following order
	parts := []string{".globalcfg", ".defaultcfg", ".frontendcfg", ".frontendtop", ".frontend", ".frontendbottom", ".default_backend", ".backend"}
	for i := range parts {
		partFunc(parts[i])
	}

	//write the file
	err := ioutil.WriteFile(haproxyPath+"/haproxy.cfg", haproxyCfg, 0777)
	if err != nil {
		return err
	}

	return nil

}

// StartHaproxy ...
func (h *Haproxy) StartHaproxy() error {
	// restart haproxy container
	session := sh.NewSession()
	err := session.Command("haproxy", "-p", "/var/run/haproxy.pid", "-f", "/usr/local/etc/haproxy/haproxy.cfg").Run()
	if err != nil {
		return err
	}
	return nil
}

// ReloadHaproxy ...
func (h *Haproxy) ReloadHaproxy() error {
	session := sh.NewSession()
	err := session.Command("/usr/bin/reload.sh").Run()
	if err != nil {
		return err
	}
	return nil
}

// Generate regenerates haproxy config from existing configs
func (h *Haproxy) Generate(r *http.Request, service *Service, result *Result) error {
	if err := h.generateCfg(); err != nil {
		*result = 0
		return err
	}

	*result = 1
	return nil

}

func main() {

	//if running without docker
	if os.Getenv("CONF_PATH") != "" {
		confPath = os.Getenv("CONF_PATH")
	}

	if os.Getenv("HAPROXY_PATH") != "" {
		haproxyPath = os.Getenv("HAPROXY_PATH")
	}

	s := rpc.NewServer()
	s.RegisterCodec(json.NewCodec(), "application/json")
	haproxy := new(Haproxy)
	// generate default haproxy.cfg
	log.Println("Generating default haproxy.cfg")
	haproxy.generateCfg()
	haproxy.StartHaproxy()
	s.RegisterService(haproxy, "")
	r := mux.NewRouter()
	r.Handle("/haproxy", s)
	http.ListenAndServe(":34015", r)

}
<commit_msg>better format of haproxy configs<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"strconv"

	"github.com/codeskyblue/go-sh"
	"github.com/gorilla/mux"
	"github.com/gorilla/rpc"
	"github.com/gorilla/rpc/json"
)

const frontendHeaderACL = `
  acl is{{.ACL}} hdr_beg(host) {{index .HaproxyURLs #}}`
const frontendPathACL = `
  acl is{{.ACL}} path_beg -i {{index .HaproxyURLs #}}`

const frontendUse = `
  use_backend {{.Backend}} if is{{.ACL}}`

// const frontendTmpl = `
// acl is{{.ACL}} hdr_beg(host) {{.HaproxyURL}}
// use_backend {{.Backend}} if is{{.ACL}}
// `
const backendTmpl = `
backend {{.Backend}}
  server {{.Backend}} {{.Hostmachine}}:{{.Port}} check inter 10000`

const defaultBackendTmpl = `
  default_backend {{.Backend}}`

var confPath = "/usr/local/etc/haproxy/conf"
var haproxyPath = "/usr/local/etc/haproxy"

// Service to be added
type Service struct {
	// ACL to be used
	ACL string
	// URL by which service will be called
	HaproxyURLs []string
	// Backend name
	Backend string
	// storefront-services-1.myntra.com
	Hostmachine string
	// Port on Hostmachine where service runs
	Port string
}

// Services ...
type Services struct {
	Services []Service
}

// Haproxy ...
type Haproxy int

// Result ...
type Result int

// Add a frontend and backend
func (h *Haproxy) Add(r *http.Request, services *Services, result *Result) error {
	var defaultBackend interface{}
	isDefaultBackedDefined := false
	for _, service := range services.Services {
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".backend").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontend").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontendtop").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontendbottom").Run()

		log.Printf("Add service %s:%s", service.HaproxyURLs, service.Port)
		data := struct {
			ACL         string
			HaproxyURLs []string
			Backend     string
			Hostmachine string
			Port        string
		}{
			strings.Title(service.ACL),
			service.HaproxyURLs,
			service.Backend,
			service.Hostmachine,
			service.Port,
		}

		frontendACLs := `
  `

		frontendType := ".frontend"

		for haproxyURLId, haproxyURL := range service.HaproxyURLs {
			if haproxyURL == "default_backend" {
				defaultBackend = data
				isDefaultBackedDefined = true
			} else if strings.EqualFold(haproxyURL, "top") || strings.EqualFold(haproxyURL, "bottom") {
				frontendType += haproxyURL
			} else if string(haproxyURL[0]) == "/" {
				frontendACLs += `  `
				frontendACLs += strings.Replace(frontendPathACL, "#", strconv.Itoa(haproxyURLId), -1)
				frontendACLs += `
`
			} else {
				frontendACLs += `  `
				frontendACLs += strings.Replace(frontendHeaderACL, "#", strconv.Itoa(haproxyURLId), -1)
				frontendACLs += `
`
			}
		}

		frontendTmpl := frontendACLs + frontendUse

		log.Println(frontendTmpl)

		// Generate frontend entry
		tmpl := template.Must(template.New("frontend").Parse(frontendTmpl))
		f, err := os.OpenFile(confPath+"/"+service.ACL+frontendType, os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			*result = 0
			return err
		}
		// fill in the template
		err = tmpl.Execute(f, data)
		if err != nil {
			*result = 0
			return err
		}

		// Generate backend entry
		tmpl = template.Must(template.New("backend").Parse(backendTmpl))
		f, err = os.OpenFile(confPath+"/"+service.ACL+".backend", os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			*result = 0
			return err
		}
		err = tmpl.Execute(f, data)
		if err != nil {
			*result = 0
			return err
		}
	}

	// Generate default_backend if needed
	if isDefaultBackedDefined {
		tmpl := template.Must(template.New("default_backend").Parse(defaultBackendTmpl))
		f, err := os.OpenFile(confPath+"/"+".default_backend", os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			*result = 0
			return err
		}
		err = tmpl.Execute(f, defaultBackend)
		if err != nil {
			*result = 0
			return err
		}
	}

	//join all the configs
	if err := h.generateCfg(); err != nil {
		*result = 0
		return err
	}

	if err := h.ReloadHaproxy(); err != nil {
		*result = 0
		return err
	}

	*result = 1
	return nil

}

// Remove a frontend and backend
func (h *Haproxy) Remove(r *http.Request, services *Services, result *Result) error {
	for _, service := range services.Services {
		log.Printf("Remove service %s:%s", service.HaproxyURLs, service.Port)
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".backend").Run()
		sh.Command("rm", "-f", confPath+"/"+service.ACL+".frontend").Run()
	}

	if err := h.generateCfg(); err != nil {
		*result = 0
		return err
	}

	if err := h.ReloadHaproxy(); err != nil {
		*result = 0
		return err
	}

	*result = 1
	return nil

}

func (h *Haproxy) generateCfg() error {
	if _, err := os.Stat(haproxyPath + "/haproxy.cfg"); !os.IsNotExist(err) {
		currentTime := string(time.Now().Format("20060102150405"))
		err := os.Rename(haproxyPath+"/haproxy.cfg", haproxyPath+"/haproxy.cfg.BAK."+currentTime)
		if err != nil {
			return err
		}
	}

	var haproxyCfg []byte

	var partFunc = func(part string) {
		// walk all files in the directory
		filepath.Walk(confPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() && strings.HasSuffix(info.Name(), part) {
				b, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				haproxyCfg = append(haproxyCfg, b...)
			}
			return nil

		})
	}

	//append the configs in the following order
	parts := []string{".globalcfg", ".defaultcfg", ".frontendcfg", ".frontendtop", ".frontend", ".frontendbottom", ".default_backend", ".backend"}
	for i := range parts {
		partFunc(parts[i])
	}

	//write the file
	err := ioutil.WriteFile(haproxyPath+"/haproxy.cfg", haproxyCfg, 0777)
	if err != nil {
		return err
	}

	return nil

}

// StartHaproxy ...
func (h *Haproxy) StartHaproxy() error {
	// restart haproxy container
	session := sh.NewSession()
	err := session.Command("haproxy", "-p", "/var/run/haproxy.pid", "-f", "/usr/local/etc/haproxy/haproxy.cfg").Run()
	if err != nil {
		return err
	}
	return nil
}

// ReloadHaproxy ...
func (h *Haproxy) ReloadHaproxy() error {
	session := sh.NewSession()
	err := session.Command("/usr/bin/reload.sh").Run()
	if err != nil {
		return err
	}
	return nil
}

// Generate regenerates haproxy config from existing configs
func (h *Haproxy) Generate(r *http.Request, service *Service, result *Result) error {
	if err := h.generateCfg(); err != nil {
		*result = 0
		return err
	}

	*result = 1
	return nil

}

func main() {

	//if running without docker
	if os.Getenv("CONF_PATH") != "" {
		confPath = os.Getenv("CONF_PATH")
	}

	if os.Getenv("HAPROXY_PATH") != "" {
		haproxyPath = os.Getenv("HAPROXY_PATH")
	}

	s := rpc.NewServer()
	s.RegisterCodec(json.NewCodec(), "application/json")
	haproxy := new(Haproxy)
	// generate default haproxy.cfg
	log.Println("Generating default haproxy.cfg")
	haproxy.generateCfg()
	haproxy.StartHaproxy()
	s.RegisterService(haproxy, "")
	r := mux.NewRouter()
	r.Handle("/haproxy", s)
	http.ListenAndServe(":34015", r)

}
<|endoftext|>


<commit_before>package gameserverapi

const (
	CtxConfigKey CtxKey = iota
	CtxLoggerWithoutUserKey
	CtxDBReadPoolKey
	CtxDBWritePoolKey
	CtxDBReadStaticPoolKey
	CtxStaticCacheKey
	CtxUsersLCacheKey
	CtxUsersSCacheKey
)

type CtxKey int
<commit_msg>minor<commit_after>package gameserverapi

const (
	CtxConfigKey CtxKey = iota
	CtxLoggerWithoutUserKey
	CtxDBReadPoolKey
	CtxDBWritePoolKey
	CtxDBReadStaticPoolKey
	CtxStaticCacheKey
	CtxUsersLCacheKey
	CtxUsersSCacheKey
	CtxCurrentUserIDKey
)

type CtxKey int
<|endoftext|>


<commit_before>package twitch

import (
	"fmt"
	"log"
	"sync"
	"time"
)

const (
	heartBeatRate  = time.Minute
	heartBeatLimit = 100
	heartDumpEvery = time.Minute * 30
)

// HeartbeatData - Single Beat of the Heart
type HeartbeatData struct {
	Status    string    `json:"status"`
	Game      string    `json:"game"`
	Time      time.Time `json:"time"`
	IsLive    bool      `json:"live"`
	ViewCount int       `json:"view"`
}

// Heartbeat - A beat which captures some data
// * Live Status
// * Viewer Count
// * host notifications
type Heartbeat struct {
	beats     []HeartbeatData
	hosts     map[ID]time.Time
	heartLock sync.RWMutex

	prevFollowCount int
	followers       []ChannelFollow

	internalBeat *time.Ticker
	client       *Client
}

// StartBeat - Blocking Loop Which
func (heart *Heartbeat) StartBeat() {
	heart.prevFollowCount = -1
	heart.hosts = make(map[ID]time.Time)

	// First Beat
	heart.beat(time.Now())

	timeSinceDump := time.Duration(0)
	heart.internalBeat = time.NewTicker(heartBeatRate)

	// Beat every X minutes
	for ts := range heart.internalBeat.C {
		heart.beat(ts)

		// Dumping to File
		timeSinceDump += heartBeatRate
		if timeSinceDump > heartDumpEvery {
			timeSinceDump = 0
			err := heart.client.DumpViewers()
			if err != nil {
				fmt.Printf("DUMP ERROR: %s", err)
			}

		}
	}
}

// GetBeat - Get Sepecific Beat
func (heart *Heartbeat) GetBeat(beatNum int) HeartbeatData {
	heart.heartLock.RLock()
	defer heart.heartLock.RUnlock()
	return heart.beats[beatNum]
}

// GetAllBeats - Get All the Beats
func (heart *Heartbeat) GetAllBeats() []HeartbeatData {
	heart.heartLock.RLock()
	defer heart.heartLock.RUnlock()

	log.Printf("Beats Source %d", len(heart.beats))
	return heart.beats[0:]
}

// GetAllHosts - Get All Hosts
func (heart *Heartbeat) GetAllHosts() []ID {
	keys := make([]ID, len(heart.hosts), len(heart.hosts))
	i := 0
	for k := range heart.hosts {
		keys[i] = k
		i++
	}
	return keys
}

func (heart *Heartbeat) beat(t time.Time) {
	heart.heartLock.Lock()
	defer heart.heartLock.Unlock()

	fmt.Println("-- BEAT --")

	var prevDataPoint *HeartbeatData
	if len(heart.beats) > 0 {
		prevDataPoint = &heart.beats[len(heart.beats)-1]
	}

	// Stream Viewer Count
	sb, err := heart.client.Stream.GetStreamByUser(heart.client.RoomID)
	if err != nil || sb == nil || (sb.AverageFPS == 0) {
		if prevDataPoint == nil || prevDataPoint.IsLive {
			heart.beats = append(heart.beats, HeartbeatData{Time: t})
			prevDataPoint = &heart.beats[len(heart.beats)-1]
		}
		return
	}
	heart.client.RoomStream = sb

	hbd := HeartbeatData{
		Status:    sb.Channel.Status,
		Game:      sb.Game,
		Time:      t,
		IsLive:    true,
		ViewCount: sb.Viewers,
	}
	if prevDataPoint == nil {
		prevDataPoint = &hbd
	}

	// Get Channel Followers
	// If there are more then 30 follows in a SECOND who cares
	fList, followNum, err := heart.client.Channel.GetFollowers(heart.client.RoomID, 30, true)
	heart.client.Viewers.UpdateFollowers(fList)

	// Check for new followers
	for _, f := range fList {
		t, err := time.Parse(time.RFC3339, f.CreatedAtString)
		if err != nil {
			panic(err)
		}

		if t.After(prevDataPoint.Time) {
			// New Follow
			heart.client.Alerts.Post(f.User.Name, AlertFollow, t)
		} else {
			// Avoid 99% of the work
			break
		}
	}

	heart.prevFollowCount = followNum

	// List of Hosts
	hostList, err := heart.client.Stream.GetHostsByUser(heart.client.RoomID)
	if err != nil {
		fmt.Printf("Host Check failed: %s\n", err.Error())
		return
	}

	hostDiff := len(heart.hosts) - len(hostList)
	for _, h := range hostList {
		srcID := IDFromInt(h.HostID)
		_, ok := heart.hosts[srcID]
		if !ok {
			// Trigger Alert
			heart.hosts[srcID] = time.Now()
			heart.client.Alerts.Post(IrcNick(h.HostLogin), AlertHost, h)
			hostDiff++
		}
	}

	// We lost a host
	if hostDiff > 0 {
		for k := range heart.hosts {
			isFound := false
			for k2 := 0; !isFound && k2 < len(hostList); k2++ {
				srcID := IDFromInt(hostList[k2].HostID)
				isFound = (k == srcID)
			}

			if isFound == false {
				delete(heart.hosts, k)
			}
		}
	}

	// Update Data Points
	prevDataPoint = &hbd
	if len(heart.beats) < heartBeatLimit {
		heart.beats = append(heart.beats, hbd)
	} else {
		heart.beats = append(heart.beats[1:], hbd)
	}

	heart.client.Alerts.Post(heart.client.RoomName, AlertNone, hbd)
}
<commit_msg>Debug Spam<commit_after>package twitch

import (
	"fmt"
	"log"
	"sync"
	"time"
)

const (
	heartBeatRate  = time.Minute
	heartBeatLimit = 100
	heartDumpEvery = time.Minute * 30
)

// HeartbeatData - Single Beat of the Heart
type HeartbeatData struct {
	Status    string    `json:"status"`
	Game      string    `json:"game"`
	Time      time.Time `json:"time"`
	IsLive    bool      `json:"live"`
	ViewCount int       `json:"view"`
}

// Heartbeat - A beat which captures some data
// * Live Status
// * Viewer Count
// * host notifications
type Heartbeat struct {
	beats     []HeartbeatData
	hosts     map[ID]time.Time
	heartLock sync.RWMutex

	prevFollowCount int
	followers       []ChannelFollow

	internalBeat *time.Ticker
	client       *Client
}

// StartBeat - Blocking Loop Which
func (heart *Heartbeat) StartBeat() {
	heart.prevFollowCount = -1
	heart.hosts = make(map[ID]time.Time)

	// First Beat
	heart.beat(time.Now())

	timeSinceDump := time.Duration(0)
	heart.internalBeat = time.NewTicker(heartBeatRate)

	// Beat every X minutes
	for ts := range heart.internalBeat.C {
		heart.beat(ts)

		// Dumping to File
		timeSinceDump += heartBeatRate
		if timeSinceDump > heartDumpEvery {
			timeSinceDump = 0
			err := heart.client.DumpViewers()
			if err != nil {
				fmt.Printf("DUMP ERROR: %s", err)
			}

		}
	}
}

// GetBeat - Get Sepecific Beat
func (heart *Heartbeat) GetBeat(beatNum int) HeartbeatData {
	heart.heartLock.RLock()
	defer heart.heartLock.RUnlock()
	return heart.beats[beatNum]
}

// GetAllBeats - Get All the Beats
func (heart *Heartbeat) GetAllBeats() []HeartbeatData {
	heart.heartLock.RLock()
	defer heart.heartLock.RUnlock()

	log.Printf("Beats Source %d", len(heart.beats))
	return heart.beats[0:]
}

// GetAllHosts - Get All Hosts
func (heart *Heartbeat) GetAllHosts() []ID {
	keys := make([]ID, len(heart.hosts), len(heart.hosts))
	i := 0
	for k := range heart.hosts {
		keys[i] = k
		i++
	}
	return keys
}

func (heart *Heartbeat) beat(t time.Time) {
	heart.heartLock.Lock()
	defer heart.heartLock.Unlock()

	fmt.Println("-- BEAT --")

	var prevDataPoint *HeartbeatData
	if len(heart.beats) > 0 {
		prevDataPoint = &heart.beats[len(heart.beats)-1]
	}

	// Stream Viewer Count
	sb, err := heart.client.Stream.GetStreamByUser(heart.client.RoomID)
	fmt.Println(sb)

	if err != nil || sb == nil || (sb.AverageFPS == 0) {
		if prevDataPoint == nil || prevDataPoint.IsLive {
			heart.beats = append(heart.beats, HeartbeatData{Time: t})
			prevDataPoint = &heart.beats[len(heart.beats)-1]
		}
		return
	}
	heart.client.RoomStream = sb

	hbd := HeartbeatData{
		Status:    sb.Channel.Status,
		Game:      sb.Game,
		Time:      t,
		IsLive:    true,
		ViewCount: sb.Viewers,
	}
	if prevDataPoint == nil {
		prevDataPoint = &hbd
	}

	// Get Channel Followers
	// If there are more then 30 follows in a SECOND who cares
	fList, followNum, err := heart.client.Channel.GetFollowers(heart.client.RoomID, 30, true)
	heart.client.Viewers.UpdateFollowers(fList)

	// Check for new followers
	for _, f := range fList {
		t, err := time.Parse(time.RFC3339, f.CreatedAtString)
		if err != nil {
			panic(err)
		}

		if t.After(prevDataPoint.Time) {
			// New Follow
			heart.client.Alerts.Post(f.User.Name, AlertFollow, t)
		} else {
			// Avoid 99% of the work
			break
		}
	}

	heart.prevFollowCount = followNum

	// List of Hosts
	hostList, err := heart.client.Stream.GetHostsByUser(heart.client.RoomID)
	if err != nil {
		fmt.Printf("Host Check failed: %s\n", err.Error())
		return
	}

	hostDiff := len(heart.hosts) - len(hostList)
	for _, h := range hostList {
		srcID := IDFromInt(h.HostID)
		_, ok := heart.hosts[srcID]
		if !ok {
			// Trigger Alert
			heart.hosts[srcID] = time.Now()
			heart.client.Alerts.Post(IrcNick(h.HostLogin), AlertHost, h)
			hostDiff++
		}
	}

	// We lost a host
	if hostDiff > 0 {
		for k := range heart.hosts {
			isFound := false
			for k2 := 0; !isFound && k2 < len(hostList); k2++ {
				srcID := IDFromInt(hostList[k2].HostID)
				isFound = (k == srcID)
			}

			if isFound == false {
				delete(heart.hosts, k)
			}
		}
	}

	// Update Data Points
	prevDataPoint = &hbd
	if len(heart.beats) < heartBeatLimit {
		heart.beats = append(heart.beats, hbd)
	} else {
		heart.beats = append(heart.beats[1:], hbd)
	}

	heart.client.Alerts.Post(heart.client.RoomName, AlertNone, hbd)
}
<|endoftext|>


<commit_before>package data_messages

import (
	"fmt"
  log "github.com/sirupsen/logrus"
  types "github.com/nttdots/go-dots/dots_common/types/data"
)

type ACLsRequest struct {
  ACLs types.ACLs `json:"ietf-dots-data-channel:acls"`
}

type ACLsResponse struct {
  ACLs types.ACLs `json:"ietf-dots-data-channel:acls"`
}

func validatePort(p *types.PortRangeOrOperator) bool {
  if p.LowerPort != nil {
    if p.Operator != nil {
      log.Error("Both 'lower-port' and 'operator' specified.")
      return false
    }
    if p.Port != nil {
      log.Error("Both 'lower-port' and 'port' specified.")
      return false
    }
    if p.UpperPort != nil {
      if *p.UpperPort < *p.LowerPort {
        log.WithField("lower-port", *p.LowerPort).WithField("upper-port", *p.UpperPort).Error( "'upper-port' must be greater than or equal to 'lower-port'.")
        return false
      }
    }
  } else {
    if p.Port == nil {
      log.Error("Both 'lower-port' and 'port' unspecified.")
      return false
    }
    if p.UpperPort != nil {
      log.Error("Both 'port' and 'upper-port' specified.")
      return false
    }
  }
  return true
}

func (r *ACLsRequest) Validate() (bool, string) {
  errorMsg := ""

  if len(r.ACLs.ACL) <= 0 {
    log.WithField("len", len(r.ACLs.ACL)).Error("'acl' is not exist.")
    errorMsg = fmt.Sprintf("Body Data Error : 'acl' is not exist")
    return false, errorMsg
  }
  if len(r.ACLs.ACL) > 1 {
    log.WithField("len", len(r.ACLs.ACL)).Error("multiple 'acl'.")
    errorMsg = fmt.Sprintf("Body Data Error : Have multiple 'acl' (%d)", len(r.ACLs.ACL))
    return false, errorMsg
  }

  acl := r.ACLs.ACL[0]
  if acl.Name == "" {
    log.Error("Missing required acl 'name' attribute.")
    errorMsg = fmt.Sprintf("Body Data Error : Missing acl 'name'")
    return false, errorMsg
  }

  if acl.PendingLifetime != nil {
    log.WithField("pending-lifetime", *acl.PendingLifetime).Error("'pending-lifetime' found.")
    errorMsg = fmt.Sprintf("Body Data Error : Found NoConfig Attribute 'pending-lifetime' (%v)", *acl.PendingLifetime)
    return false, errorMsg
  }

  if acl.Type != nil {
    if *acl.Type != types.ACLType_IPv4ACLType && *acl.Type != types.ACLType_IPv6ACLType {
      log.WithField("type", *acl.Type).Error("'type' must be 'ipv4-acl-type' or 'ipv6-acl-type'.")
      errorMsg = fmt.Sprintf("Body Data Error : 'type' must be 'ipv4-acl-type' or 'ipv6-acl-type'. Not support (%v)", *acl.Type)
      return false, errorMsg
    }
  }

  for _, ace := range acl.ACEs.ACE {
    if ace.Actions == nil || (ace.Actions.Forwarding == nil && ace.Actions.RateLimit == nil) {
      log.Error("Missing required acl 'actions' attribute.")
      errorMsg = fmt.Sprintf("Body Data Error : Missing acl 'actions'")
      return false, errorMsg
    }

    if ace.Statistics != nil {
      log.WithField("statistics", *ace.Statistics).Error("'statistics' found.")
      errorMsg = fmt.Sprintf("Body Data Error : Found NoConfig Attribute 'statistics' (%v)", *ace.Statistics)
      return false, errorMsg
    }

    if ace.Matches != nil {
      matches := ace.Matches
      if matches.IPv4 != nil && matches.IPv6 != nil {
        log.WithField("ipv4", *matches.IPv4).WithField("ipv6", *matches.IPv6).Error("Only one of 'ipv4' and 'ipv6' matches is allowed.")
        errorMsg = fmt.Sprintf("Body Data Error : Only one 'ipv4' or 'ipv6' of 'match' is allowed")
        return false, errorMsg
      }

      if *acl.ActivationType == types.ActivationType_Immediate && matches.IPv4.DestinationIPv4Network == nil{
        log.Error("Missing destination-ipv4-network value when ’activation-type’ is ’immediate’")
        errorMsg = fmt.Sprintf("Body Data Error : 'destination-ipv4-network' value is required when ’activation-type’ is ’immediate’")
        return false, errorMsg
      }

      if (matches.TCP != nil && matches.UDP  != nil) ||
         (matches.UDP != nil && matches.ICMP != nil) ||
         (matches.TCP != nil && matches.ICMP != nil) {
        log.WithField("tcp", matches.TCP).WithField("udp", matches.UDP).WithField("icmp", matches.ICMP).Error("Only one of 'tcp', 'udp' and 'icmp' matches is allowed.")
        errorMsg = fmt.Sprintf("Body Data Error : Only one 'tcp', 'udp' and 'icmp' of 'match' is allowed")
        return false, errorMsg
      }

      if acl.Type != nil {
        switch *acl.Type {
        case types.ACLType_IPv4ACLType:
          if matches.IPv6 != nil {
            log.WithField("ipv6", *matches.IPv6).Error("ACL with type 'ipv4-acl-type' must not have 'ace' with 'ipv6' matches.")
            errorMsg = fmt.Sprintf("Body Data Error : ACL with type 'ipv4-acl-type' must not have 'ace' with 'ipv6' matches")
            return false, errorMsg
          }
        case types.ACLType_IPv6ACLType:
          if matches.IPv4 != nil {
            log.WithField("ipv4", *matches.IPv4).Error("ACL with type 'ipv6-acl-type' must not have 'ace' with 'ipv4' matches.")
            errorMsg = fmt.Sprintf("Body Data Error : ACL with type 'ipv6-acl-type' must not have 'ace' with 'ipv4' matches")
            return false, errorMsg
          }
        }
      }

      if matches.TCP != nil {
        tcp := matches.TCP
        if tcp.SourcePort != nil && validatePort(tcp.SourcePort) == false {
          log.WithField("source-port", *tcp.SourcePort).Error("Invalid 'source-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'source-port' (%v)", *tcp.SourcePort)
          return false, errorMsg
        }
        if tcp.DestinationPort != nil && validatePort(tcp.DestinationPort) == false {
          log.WithField("destination-port", *tcp.DestinationPort).Error("Invalid 'destination-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'destination-port' (%v)", *tcp.DestinationPort)
          return false, errorMsg
        }
      }
      if matches.UDP != nil {
        udp := matches.UDP
        if udp.SourcePort != nil && validatePort(udp.SourcePort) == false {
          log.WithField("source-port", *udp.SourcePort).Error("Invalid 'source-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'source-port' (%v)", *udp.SourcePort)
          return false, errorMsg
        }
        if udp.DestinationPort != nil && validatePort(udp.DestinationPort) == false {
          log.WithField("destination-port", *udp.DestinationPort).Error("Invalid 'destination-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'destination-port' (%v)", *udp.DestinationPort)
          return false, errorMsg
        }
      }
    }
  }

  return true, ""
}

func (r *ACLsRequest) ValidateWithName(name string) (bool, string) {

  bValid, errorMsg := r.Validate()
  if !bValid {
    return false, errorMsg
  }

  acl := r.ACLs.ACL[0]
  if acl.Name != name {
    log.WithField("name(req)", acl.Name).WithField("name(URI)", name).Error("request/URI name mismatch.")
    errorMsg = fmt.Sprintf("Request/URI name mismatch : (%v) / (%v)", acl.Name, name)
    return false, errorMsg
  }

  return true, ""
}
<commit_msg>Data channel fix issues #92<commit_after>package data_messages

import (
	"fmt"
  log "github.com/sirupsen/logrus"
  types "github.com/nttdots/go-dots/dots_common/types/data"
)

type ACLsRequest struct {
  ACLs types.ACLs `json:"ietf-dots-data-channel:acls"`
}

type ACLsResponse struct {
  ACLs types.ACLs `json:"ietf-dots-data-channel:acls"`
}

func validatePort(p *types.PortRangeOrOperator) bool {
  if p.LowerPort != nil {
    if p.Operator != nil {
      log.Error("Both 'lower-port' and 'operator' specified.")
      return false
    }
    if p.Port != nil {
      log.Error("Both 'lower-port' and 'port' specified.")
      return false
    }
    if p.UpperPort != nil {
      if *p.UpperPort < *p.LowerPort {
        log.WithField("lower-port", *p.LowerPort).WithField("upper-port", *p.UpperPort).Error( "'upper-port' must be greater than or equal to 'lower-port'.")
        return false
      }
    }
  } else {
    if p.Port == nil {
      log.Error("Both 'lower-port' and 'port' unspecified.")
      return false
    }
    if p.UpperPort != nil {
      log.Error("Both 'port' and 'upper-port' specified.")
      return false
    }
  }
  return true
}

func (r *ACLsRequest) Validate() (bool, string) {
  errorMsg := ""

  if len(r.ACLs.ACL) <= 0 {
    log.WithField("len", len(r.ACLs.ACL)).Error("'acl' is not exist.")
    errorMsg = fmt.Sprintf("Body Data Error : 'acl' is not exist")
    return false, errorMsg
  }
  if len(r.ACLs.ACL) > 1 {
    log.WithField("len", len(r.ACLs.ACL)).Error("multiple 'acl'.")
    errorMsg = fmt.Sprintf("Body Data Error : Have multiple 'acl' (%d)", len(r.ACLs.ACL))
    return false, errorMsg
  }

  acl := r.ACLs.ACL[0]
  if acl.Name == "" {
    log.Error("Missing required acl 'name' attribute.")
    errorMsg = fmt.Sprintf("Body Data Error : Missing acl 'name'")
    return false, errorMsg
  }

  if acl.PendingLifetime != nil {
    log.WithField("pending-lifetime", *acl.PendingLifetime).Error("'pending-lifetime' found.")
    errorMsg = fmt.Sprintf("Body Data Error : Found NoConfig Attribute 'pending-lifetime' (%v)", *acl.PendingLifetime)
    return false, errorMsg
  }

  if acl.Type != nil {
    if *acl.Type != types.ACLType_IPv4ACLType && *acl.Type != types.ACLType_IPv6ACLType {
      log.WithField("type", *acl.Type).Error("'type' must be 'ipv4-acl-type' or 'ipv6-acl-type'.")
      errorMsg = fmt.Sprintf("Body Data Error : 'type' must be 'ipv4-acl-type' or 'ipv6-acl-type'. Not support (%v)", *acl.Type)
      return false, errorMsg
    }
  }

  for _, ace := range acl.ACEs.ACE {
    if ace.Actions == nil || (ace.Actions.Forwarding == nil && ace.Actions.RateLimit == nil) {
      log.Error("Missing required acl 'actions' attribute.")
      errorMsg = fmt.Sprintf("Body Data Error : Missing acl 'actions'")
      return false, errorMsg
    }

    if ace.Statistics != nil {
      log.WithField("statistics", *ace.Statistics).Error("'statistics' found.")
      errorMsg = fmt.Sprintf("Body Data Error : Found NoConfig Attribute 'statistics' (%v)", *ace.Statistics)
      return false, errorMsg
    }

    if ace.Matches != nil {
      matches := ace.Matches
      if matches.IPv4 != nil && matches.IPv6 != nil {
        log.WithField("ipv4", *matches.IPv4).WithField("ipv6", *matches.IPv6).Error("Only one of 'ipv4' and 'ipv6' matches is allowed.")
        errorMsg = fmt.Sprintf("Body Data Error : Only one 'ipv4' or 'ipv6' of 'match' is allowed")
        return false, errorMsg
      }

      if *acl.ActivationType == types.ActivationType_Immediate {
        if matches.IPv4 != nil && matches.IPv4.DestinationIPv4Network == nil {
          log.Error("Missing 'destination-ipv4-network' value when ’activation-type’ is ’immediate’")
          errorMsg = fmt.Sprintf("Body Data Error : 'destination-ipv4-network' value is required when ’activation-type’ is ’immediate’")
          return false, errorMsg
        }
        if matches.IPv6 != nil && matches.IPv6.DestinationIPv6Network == nil {
          log.Error("Missing 'destination-ipv6-network' value when ’activation-type’ is ’immediate’")
          errorMsg = fmt.Sprintf("Body Data Error : 'destination-ipv6-network' value is required when ’activation-type’ is ’immediate’")
          return false, errorMsg
        }
      }

      if (matches.TCP != nil && matches.UDP  != nil) ||
         (matches.UDP != nil && matches.ICMP != nil) ||
         (matches.TCP != nil && matches.ICMP != nil) {
        log.WithField("tcp", matches.TCP).WithField("udp", matches.UDP).WithField("icmp", matches.ICMP).Error("Only one of 'tcp', 'udp' and 'icmp' matches is allowed.")
        errorMsg = fmt.Sprintf("Body Data Error : Only one 'tcp', 'udp' and 'icmp' of 'match' is allowed")
        return false, errorMsg
      }

      if acl.Type != nil {
        switch *acl.Type {
        case types.ACLType_IPv4ACLType:
          if matches.IPv6 != nil {
            log.WithField("ipv6", *matches.IPv6).Error("ACL with type 'ipv4-acl-type' must not have 'ace' with 'ipv6' matches.")
            errorMsg = fmt.Sprintf("Body Data Error : ACL with type 'ipv4-acl-type' must not have 'ace' with 'ipv6' matches")
            return false, errorMsg
          }
        case types.ACLType_IPv6ACLType:
          if matches.IPv4 != nil {
            log.WithField("ipv4", *matches.IPv4).Error("ACL with type 'ipv6-acl-type' must not have 'ace' with 'ipv4' matches.")
            errorMsg = fmt.Sprintf("Body Data Error : ACL with type 'ipv6-acl-type' must not have 'ace' with 'ipv4' matches")
            return false, errorMsg
          }
        }
      }

      if matches.TCP != nil {
        tcp := matches.TCP
        if tcp.SourcePort != nil && validatePort(tcp.SourcePort) == false {
          log.WithField("source-port", *tcp.SourcePort).Error("Invalid 'source-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'source-port' (%v)", *tcp.SourcePort)
          return false, errorMsg
        }
        if tcp.DestinationPort != nil && validatePort(tcp.DestinationPort) == false {
          log.WithField("destination-port", *tcp.DestinationPort).Error("Invalid 'destination-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'destination-port' (%v)", *tcp.DestinationPort)
          return false, errorMsg
        }
      }
      if matches.UDP != nil {
        udp := matches.UDP
        if udp.SourcePort != nil && validatePort(udp.SourcePort) == false {
          log.WithField("source-port", *udp.SourcePort).Error("Invalid 'source-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'source-port' (%v)", *udp.SourcePort)
          return false, errorMsg
        }
        if udp.DestinationPort != nil && validatePort(udp.DestinationPort) == false {
          log.WithField("destination-port", *udp.DestinationPort).Error("Invalid 'destination-port'.")
          errorMsg = fmt.Sprintf("Body Data Error : Invalid 'destination-port' (%v)", *udp.DestinationPort)
          return false, errorMsg
        }
      }
    }
  }

  return true, ""
}

func (r *ACLsRequest) ValidateWithName(name string) (bool, string) {

  bValid, errorMsg := r.Validate()
  if !bValid {
    return false, errorMsg
  }

  acl := r.ACLs.ACL[0]
  if acl.Name != name {
    log.WithField("name(req)", acl.Name).WithField("name(URI)", name).Error("request/URI name mismatch.")
    errorMsg = fmt.Sprintf("Request/URI name mismatch : (%v) / (%v)", acl.Name, name)
    return false, errorMsg
  }

  return true, ""
}
<|endoftext|>


<commit_before>package atm

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
)

const AUTH_SEP = ":"
const XTIME = "X-Timestamp"
const CONTENT_TYPE = "Content-Type"
const CONTENT_MD5 = "Content-MD5"
const XNONCE = "X-Nonce"
const API_KEY = "api-key"

type KeyFinder func(string) (string, error)

type HmacOpts struct {
	AuthPrefix   string
	Expiration   time.Duration
	SecretKeyFor KeyFinder
}

func NewHmacOpts(f KeyFinder) *HmacOpts {
	o := &HmacOpts{
		AuthPrefix:   "ATM_Auth",
		Expiration:   5 * time.Minute,
		SecretKeyFor: f,
	}
	return o
}

func HMACAuth(o *HmacOpts) echo.HandlerFunc {
	return func(c *echo.Context) error {
		auth, err := newAuth(c.Request(), o)
		if nil != err {
			return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
		}
		if err := auth.Authentic(c.Request()); nil != err {
			return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
		}
		c.Set(API_KEY, auth.ApiKey)
		return nil
	}
}

type Authorizor struct {
	ApiKey    string
	Signature string
	Md5       string
	Type      string
	Nonce     string
	Xtime     string
	Method    string
	Uri       string
	Timestamp time.Time
	Opts      *HmacOpts
}

type hmacError struct {
	msg string
}

func (e hmacError) Error() string {
	return e.msg
}

func newAuth(r *http.Request, o *HmacOpts) (*Authorizor, error) {
	h := r.Header
	if len(h.Get(echo.Authorization)) <= 0 {
		return nil, hmacError{"Missing required Authorization header"}
	}
	a := &Authorizor{
		Opts:   o,
		Method: strings.ToUpper(r.Method),
		Uri:    r.URL.RequestURI(),
	}
	err := a.authorizorFromAuthHeader(h.Get(echo.Authorization))
	if nil != err {
		return nil, err
	}
	err = a.extractRequiredHeaders(&h)
	if nil != err {
		return nil, err
	}
	return a, nil
}

func (a *Authorizor) Authentic(r *http.Request) error {
	if err := a.invalidTime(); nil != err {
		return err
	}
	//TODO Actually check the nonce
	body := readerToByte(r.Body)
	r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
	contentHash := md5Of(body)
	if strings.ToLower(a.Md5) != strings.ToLower(contentHash) {
		return hmacError{fmt.Sprintf("Content MD5s do not match:%s", contentHash)}
	}
	secret, err := a.Opts.SecretKeyFor(a.ApiKey)
	if nil != err {
		return err
	}
	if "" == secret {
		return hmacError{fmt.Sprintf("No secret key for %s", a.ApiKey)}
	}
	generatedSig := a.signatureWith(secret)
	if "" == generatedSig {
		return hmacError{"Unable to generate hmac signature"}
	}
	if hmac.Equal([]byte(generatedSig), []byte(a.Signature)) {
		return nil
	}
	return hmacError{"HMAC Signature mismatch"}
}

func md5Of(b []byte) string {
	h := md5.New()
	h.Write(b)
	return hex.EncodeToString(h.Sum(nil))
}

func readerToByte(s io.Reader) []byte {
	b, _ := ioutil.ReadAll(s)
	return b
}

func (a *Authorizor) invalidTime() error {
	age := time.Since(a.Timestamp)
	if age < 0 {
		age *= -1
	}
	if age > a.Opts.Expiration {
		return hmacError{"Timestamp out of range"}
	}
	return nil
}

func (a *Authorizor) signatureWith(key string) string {
	c := a.SigningString()
	h := hmac.New(sha256.New, []byte(key))
	h.Write([]byte(c))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

//Build the content for signging & sign it with the key
//method\nURI\nMD5\ntype\ntimestamp\nnonce
func (a *Authorizor) SigningString() string {
	var b bytes.Buffer
	b.WriteString(a.Method)
	b.WriteString("\n")
	b.WriteString(a.Uri)
	b.WriteString("\n")
	b.WriteString(a.Md5)
	b.WriteString("\n")
	b.WriteString(a.Type)
	b.WriteString("\n")
	b.WriteString(a.Xtime)
	b.WriteString("\n")
	b.WriteString(a.Nonce)
	b.WriteString("\n")
	b.WriteString(a.ApiKey)

	return b.String()
}

// We need x-timestamp, content-md5, content-type, x-nonce
func (a *Authorizor) extractRequiredHeaders(h *http.Header) error {
	a.Md5 = h.Get(CONTENT_MD5)
	if "" == a.Md5 {
		return hmacError{fmt.Sprintf("Missing required %s header", CONTENT_MD5)}
	}
	a.Type = h.Get(CONTENT_TYPE)
	if "" == a.Type {
		return hmacError{fmt.Sprintf("Missing required %s header", CONTENT_TYPE)}
	}
	a.Nonce = h.Get(XNONCE)
	if "" == a.Nonce {
		return hmacError{fmt.Sprintf("Missing required %s header", XNONCE)}
	}
	return a.parseTimestamp(h.Get(XTIME))
}

func (a *Authorizor) parseTimestamp(t string) error {
	if "" == t {
		return hmacError{fmt.Sprintf("Missing required %s header", XTIME)}
	}
	a.Xtime = t
	ts, err := time.Parse(time.RFC3339, t)
	if nil != err {
		return hmacError{fmt.Sprintf("Invalid timestamp: %s", err.Error())}
	}
	a.Timestamp = ts
	return nil
}

func (a *Authorizor) authorizorFromAuthHeader(h string) error {
	l := len(a.Opts.AuthPrefix)
	if len(h)-l-1 <= 0 || h[:l] != a.Opts.AuthPrefix {
		return hmacError{fmt.Sprintf("Authorization header invalid format: Missing %s prefix", a.Opts.AuthPrefix)}
	}
	parts := strings.SplitN(strings.Trim(h[l+1:], " "), AUTH_SEP, 2)
	if len(parts) != 2 {
		return hmacError{"Authorization header invalid format: Missing ApiKey:Signature"}
	}
	a.ApiKey = parts[0]
	if "" == a.ApiKey {
		return hmacError{"Authorization header invalid format: Missing ApiKey"}
	}
	a.Signature = parts[1]
	if "" == a.Signature {
		return hmacError{"Authorization header invalid format: Missing Signature"}
	}
	return nil
}
<commit_msg>Add method to start helping Hmacing with clients<commit_after>package atm

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
)

const AUTH_SEP = ":"
const XTIME = "X-Timestamp"
const CONTENT_TYPE = "Content-Type"
const CONTENT_MD5 = "Content-MD5"
const XNONCE = "X-Nonce"
const API_KEY = "api-key"

type KeyFinder func(string) (string, error)

type HmacOpts struct {
	AuthPrefix   string
	Expiration   time.Duration
	SecretKeyFor KeyFinder
}

func NewHmacOpts(f KeyFinder) *HmacOpts {
	o := &HmacOpts{
		AuthPrefix:   "ATM_Auth",
		Expiration:   5 * time.Minute,
		SecretKeyFor: f,
	}
	return o
}

func HMACAuth(o *HmacOpts) echo.HandlerFunc {
	return func(c *echo.Context) error {
		auth, err := newAuth(c.Request(), o)
		if nil != err {
			return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
		}
		if err := auth.Authentic(c.Request()); nil != err {
			return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
		}
		c.Set(API_KEY, auth.ApiKey)
		return nil
	}
}

type Authorizor struct {
	ApiKey    string
	Signature string
	Md5       string
	Type      string
	Nonce     string
	Xtime     string
	Method    string
	Uri       string
	Timestamp time.Time
	Opts      *HmacOpts
}

type hmacError struct {
	msg string
}

func (e hmacError) Error() string {
	return e.msg
}

func AuthorizorForRequest(o *HmacOpts, method string, uri string) *Authorizor {
	return &Authorizor{
		Opts:   o,
		Method: strings.ToUpper(method),
		Uri:    uri,
	}
}

func newAuth(r *http.Request, o *HmacOpts) (*Authorizor, error) {
	h := r.Header
	if len(h.Get(echo.Authorization)) <= 0 {
		return nil, hmacError{"Missing required Authorization header"}
	}
	a := &Authorizor{
		Opts:   o,
		Method: strings.ToUpper(r.Method),
		Uri:    r.URL.RequestURI(),
	}
	err := a.authorizorFromAuthHeader(h.Get(echo.Authorization))
	if nil != err {
		return nil, err
	}
	err = a.extractRequiredHeaders(&h)
	if nil != err {
		return nil, err
	}
	return a, nil
}

func (a *Authorizor) Authentic(r *http.Request) error {
	if err := a.invalidTime(); nil != err {
		return err
	}
	//TODO Actually check the nonce
	body := readerToByte(r.Body)
	r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
	contentHash := md5Of(body)
	if strings.ToLower(a.Md5) != strings.ToLower(contentHash) {
		return hmacError{fmt.Sprintf("Content MD5s do not match:%s", contentHash)}
	}
	secret, err := a.Opts.SecretKeyFor(a.ApiKey)
	if nil != err {
		return err
	}
	if "" == secret {
		return hmacError{fmt.Sprintf("No secret key for %s", a.ApiKey)}
	}
	generatedSig := a.signatureWith(secret)
	if "" == generatedSig {
		return hmacError{"Unable to generate hmac signature"}
	}
	if hmac.Equal([]byte(generatedSig), []byte(a.Signature)) {
		return nil
	}
	return hmacError{"HMAC Signature mismatch"}
}

func md5Of(b []byte) string {
	h := md5.New()
	h.Write(b)
	return hex.EncodeToString(h.Sum(nil))
}

func readerToByte(s io.Reader) []byte {
	b, _ := ioutil.ReadAll(s)
	return b
}

func (a *Authorizor) invalidTime() error {
	age := time.Since(a.Timestamp)
	if age < 0 {
		age *= -1
	}
	if age > a.Opts.Expiration {
		return hmacError{"Timestamp out of range"}
	}
	return nil
}

func (a *Authorizor) signatureWith(key string) string {
	c := a.SigningString()
	h := hmac.New(sha256.New, []byte(key))
	h.Write([]byte(c))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

//Build the content for signging & sign it with the key
//method\nURI\nMD5\ntype\ntimestamp\nnonce
func (a *Authorizor) SigningString() string {
	var b bytes.Buffer
	b.WriteString(a.Method)
	b.WriteString("\n")
	b.WriteString(a.Uri)
	b.WriteString("\n")
	b.WriteString(a.Md5)
	b.WriteString("\n")
	b.WriteString(a.Type)
	b.WriteString("\n")
	b.WriteString(a.Xtime)
	b.WriteString("\n")
	b.WriteString(a.Nonce)
	b.WriteString("\n")
	b.WriteString(a.ApiKey)

	return b.String()
}

// We need x-timestamp, content-md5, content-type, x-nonce
func (a *Authorizor) extractRequiredHeaders(h *http.Header) error {
	a.Md5 = h.Get(CONTENT_MD5)
	if "" == a.Md5 {
		return hmacError{fmt.Sprintf("Missing required %s header", CONTENT_MD5)}
	}
	a.Type = h.Get(CONTENT_TYPE)
	if "" == a.Type {
		return hmacError{fmt.Sprintf("Missing required %s header", CONTENT_TYPE)}
	}
	a.Nonce = h.Get(XNONCE)
	if "" == a.Nonce {
		return hmacError{fmt.Sprintf("Missing required %s header", XNONCE)}
	}
	return a.parseTimestamp(h.Get(XTIME))
}

func (a *Authorizor) parseTimestamp(t string) error {
	if "" == t {
		return hmacError{fmt.Sprintf("Missing required %s header", XTIME)}
	}
	a.Xtime = t
	ts, err := time.Parse(time.RFC3339, t)
	if nil != err {
		return hmacError{fmt.Sprintf("Invalid timestamp: %s", err.Error())}
	}
	a.Timestamp = ts
	return nil
}

func (a *Authorizor) authorizorFromAuthHeader(h string) error {
	l := len(a.Opts.AuthPrefix)
	if len(h)-l-1 <= 0 || h[:l] != a.Opts.AuthPrefix {
		return hmacError{fmt.Sprintf("Authorization header invalid format: Missing %s prefix", a.Opts.AuthPrefix)}
	}
	parts := strings.SplitN(strings.Trim(h[l+1:], " "), AUTH_SEP, 2)
	if len(parts) != 2 {
		return hmacError{"Authorization header invalid format: Missing ApiKey:Signature"}
	}
	a.ApiKey = parts[0]
	if "" == a.ApiKey {
		return hmacError{"Authorization header invalid format: Missing ApiKey"}
	}
	a.Signature = parts[1]
	if "" == a.Signature {
		return hmacError{"Authorization header invalid format: Missing Signature"}
	}
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Fixing so it only looks for open ports<commit_after><|endoftext|>


<commit_before>package inputoutput

import (
	"github.com/dobyrch/termboy-go/components"
	"github.com/dobyrch/termboy-go/constants"
	"log"
	"github.com/dobyrch/termboy-go/types"
	"fmt"
)

const PREFIX string = "IO"
const ESC byte = 0x1B
const ROW_1 byte = 0x10
const ROW_2 byte = 0x20
const SCREEN_WIDTH int = 160
const SCREEN_HEIGHT int = 144

var DefaultControlScheme ControlScheme = ControlScheme{'W', 'S', 'A', 'D', 90, 88, 294, 288}

type ControlScheme struct {
	UP     int
	DOWN   int
	LEFT   int
	RIGHT  int
	A      int
	B      int
	START  int
	SELECT int
}

type KeyHandler struct {
	controlScheme ControlScheme
	colSelect     byte
	rows          [2]byte
	irqHandler    components.IRQHandler
}

func (k *KeyHandler) Init(cs ControlScheme) {
	k.controlScheme = cs
	k.Reset()
}

func (k *KeyHandler) Name() string {
	return PREFIX + "-KEYB"
}

func (k *KeyHandler) Reset() {
	log.Printf("%s: Resetting", k.Name())
	k.rows[0], k.rows[1] = 0x0F, 0x0F
	k.colSelect = 0x00
}

func (k *KeyHandler) LinkIRQHandler(m components.IRQHandler) {
	k.irqHandler = m
	log.Printf("%s: Linked IRQ Handler to Keyboard Handler", k.Name())
}

func (k *KeyHandler) Read(addr types.Word) byte {
	var value byte

	switch k.colSelect {
	case ROW_1:
		value = k.rows[1]
	case ROW_2:
		value = k.rows[0]
	default:
		value = 0x00
	}

	return value
}

func (k *KeyHandler) Write(addr types.Word, value byte) {
	k.colSelect = value & 0x30
}

//released sets bit for key to 0
func (k *KeyHandler) KeyDown(key int) {
	k.irqHandler.RequestInterrupt(constants.JOYP_HILO_IRQ)
	switch key {
	case k.controlScheme.UP:
		k.rows[0] &= 0xB
	case k.controlScheme.DOWN:
		k.rows[0] &= 0x7
	case k.controlScheme.LEFT:
		k.rows[0] &= 0xD
	case k.controlScheme.RIGHT:
		k.rows[0] &= 0xE
	case k.controlScheme.A:
		k.rows[1] &= 0xE
	case k.controlScheme.B:
		k.rows[1] &= 0xD
	case k.controlScheme.START:
		k.rows[1] &= 0x7
	case k.controlScheme.SELECT:
		k.rows[1] &= 0xB
	}
}

//released sets bit for key to 1
func (k *KeyHandler) KeyUp(key int) {
	switch key {
	case k.controlScheme.UP:
		k.rows[0] |= 0x4
	case k.controlScheme.DOWN:
		k.rows[0] |= 0x8
	case k.controlScheme.LEFT:
		k.rows[0] |= 0x2
	case k.controlScheme.RIGHT:
		k.rows[0] |= 0x1
	case k.controlScheme.A:
		k.rows[1] |= 0x1
	case k.controlScheme.B:
		k.rows[1] |= 0x2
	case k.controlScheme.START:
		k.rows[1] |= 0x8
	case k.controlScheme.SELECT:
		k.rows[1] |= 0x4
	}
}

type IO struct {
	KeyHandler          *KeyHandler
	Display             *Display
	ScreenOutputChannel chan *types.Screen
	AudioOutputChannel  chan int
}

func NewIO() *IO {
	var i *IO = new(IO)
	i.KeyHandler = new(KeyHandler)
	i.Display = new(Display)
	i.ScreenOutputChannel = make(chan *types.Screen)
	i.AudioOutputChannel = make(chan int)
	return i
}

func (i *IO) Init(title string, screenSize int, onCloseHandler func()) error {
	/*
	var err error

	err = glfw.Init()
	if err != nil {
		return err
	}
	*/

	err := i.Display.init(title, screenSize)
	if err != nil {
		return err
	}

	i.KeyHandler.Init(DefaultControlScheme) //TODO: allow user to define controlscheme
	/*glfw.SetKeyCallback(func(key, state int) {
		if state == glfw.KeyPress {
			i.KeyHandler.KeyDown(key)
		} else {
			i.KeyHandler.KeyUp(key)
		}
	})

	glfw.SetWindowCloseCallback(func() int {
		glfw.CloseWindow()
		glfw.Terminate()
		onCloseHandler()
		return 0
	})
	*/

	return nil
}

//This will wait for updates to the display or audio and dispatch them accordingly
func (i *IO) Run() {
	for {
		select {
		case data := <-i.ScreenOutputChannel:
			i.Display.drawFrame(data)
		case data := <-i.AudioOutputChannel:
			log.Println("Writing %d to audio!", data)
		}
	}
}

type coord struct {
	x int
	y int
}

type Display struct {
	Name                 string
	ScreenSizeMultiplier int
	Screen map[coord]byte
}

func (s *Display) init(title string, screenSizeMultiplier int) error {
	s.Screen = make(map[coord]byte)
	//TODO: use ScreenSizeMultiplier as an indicator of whether to use
	//TODO: left half block or top half block
	//TODO: Perhaps use escape code to set title of terminal?

	//TODO: wrap all ansii prints in its own class with methods for each func
        //TODO: show the cursor after termination
        fmt.Printf("%c[?25l", ESC) //Hide the cursor
        fmt.Printf("%c[2J", ESC) //Clear screen
        fmt.Printf("%c[H", ESC) //Position cursor in top left

        /*for i := 0; i < 256; i++ {
                fmt.Printf("%c[48;5;%dm%d\n", ESC, i, i)
        }*/
        //TODO: see end of wiki article to prevent hanging on non-linux systems
        fmt.Printf("%c]P0000000", ESC)
        fmt.Printf("%c]P4555555", ESC)
        fmt.Printf("%c]P6AAAAAA", ESC)
        fmt.Printf("%c]P7FFFFFF", ESC)
        fmt.Printf("%c]P8000000", ESC)
        fmt.Printf("%c]PC555555", ESC)
        fmt.Printf("%c]PEAAAAAA", ESC)
        fmt.Printf("%c]PFFFFFFF", ESC)

	return nil
}

func (s *Display) drawFrame(screenData *types.Screen) {
	for y := 0; y < SCREEN_HEIGHT; y += 2 {
                for x := 0; x < SCREEN_WIDTH; x++ {
                        c1 := screenData[y][x].Red
                        c2 := screenData[y+1][x].Red

			if (s.Screen[coord{x, y}] != c1 ||
			    s.Screen[coord{x, y+1}] != c2) {
				s.Screen[coord{x, y}] = c1
				s.Screen[coord{x, y+1}] = c2
				var fg, bg int

				/*switch {
				case c1 < 100:
					fmt.Printf("%c[30m%c", ESC, '█')
				default:
					fmt.Printf("%c[37m%c", ESC, '█')
				}*/

				//TODO: in ansii class, set color/bold attr and append codes as needed
				//TODO: (and define all codes as consts)
				switch c1 {
				case 0:
					fg = 30
				case 96:
					fg = 34
				case 196:
					fg = 36
				case 235:
					fg = 37
				}

				switch c2 {
				case 0:
					bg = 40
				case 96:
					bg = 44
				case 196:
					bg = 46
				case 235:
					bg = 47
				}

				fmt.Printf("%c[%d;%dH", ESC, y/2 + 1, x + 1)
				//TODO: have a 'big' and 'small' mode (top/left half)
				//TODO: check if setfont is available on BSD and can change height
				fmt.Printf("%c[%d;%dm%c", ESC, fg, bg, '▀')
			}
                }
        }
}
<commit_msg>Terminated OSC escape sequence<commit_after>package inputoutput

import (
	"github.com/dobyrch/termboy-go/components"
	"github.com/dobyrch/termboy-go/constants"
	"log"
	"github.com/dobyrch/termboy-go/types"
	"fmt"
)

const PREFIX string = "IO"
const ESC byte = 0x1B
const ROW_1 byte = 0x10
const ROW_2 byte = 0x20
const SCREEN_WIDTH int = 160
const SCREEN_HEIGHT int = 144

var DefaultControlScheme ControlScheme = ControlScheme{'W', 'S', 'A', 'D', 90, 88, 294, 288}

type ControlScheme struct {
	UP     int
	DOWN   int
	LEFT   int
	RIGHT  int
	A      int
	B      int
	START  int
	SELECT int
}

type KeyHandler struct {
	controlScheme ControlScheme
	colSelect     byte
	rows          [2]byte
	irqHandler    components.IRQHandler
}

func (k *KeyHandler) Init(cs ControlScheme) {
	k.controlScheme = cs
	k.Reset()
}

func (k *KeyHandler) Name() string {
	return PREFIX + "-KEYB"
}

func (k *KeyHandler) Reset() {
	log.Printf("%s: Resetting", k.Name())
	k.rows[0], k.rows[1] = 0x0F, 0x0F
	k.colSelect = 0x00
}

func (k *KeyHandler) LinkIRQHandler(m components.IRQHandler) {
	k.irqHandler = m
	log.Printf("%s: Linked IRQ Handler to Keyboard Handler", k.Name())
}

func (k *KeyHandler) Read(addr types.Word) byte {
	var value byte

	switch k.colSelect {
	case ROW_1:
		value = k.rows[1]
	case ROW_2:
		value = k.rows[0]
	default:
		value = 0x00
	}

	return value
}

func (k *KeyHandler) Write(addr types.Word, value byte) {
	k.colSelect = value & 0x30
}

//released sets bit for key to 0
func (k *KeyHandler) KeyDown(key int) {
	k.irqHandler.RequestInterrupt(constants.JOYP_HILO_IRQ)
	switch key {
	case k.controlScheme.UP:
		k.rows[0] &= 0xB
	case k.controlScheme.DOWN:
		k.rows[0] &= 0x7
	case k.controlScheme.LEFT:
		k.rows[0] &= 0xD
	case k.controlScheme.RIGHT:
		k.rows[0] &= 0xE
	case k.controlScheme.A:
		k.rows[1] &= 0xE
	case k.controlScheme.B:
		k.rows[1] &= 0xD
	case k.controlScheme.START:
		k.rows[1] &= 0x7
	case k.controlScheme.SELECT:
		k.rows[1] &= 0xB
	}
}

//released sets bit for key to 1
func (k *KeyHandler) KeyUp(key int) {
	switch key {
	case k.controlScheme.UP:
		k.rows[0] |= 0x4
	case k.controlScheme.DOWN:
		k.rows[0] |= 0x8
	case k.controlScheme.LEFT:
		k.rows[0] |= 0x2
	case k.controlScheme.RIGHT:
		k.rows[0] |= 0x1
	case k.controlScheme.A:
		k.rows[1] |= 0x1
	case k.controlScheme.B:
		k.rows[1] |= 0x2
	case k.controlScheme.START:
		k.rows[1] |= 0x8
	case k.controlScheme.SELECT:
		k.rows[1] |= 0x4
	}
}

type IO struct {
	KeyHandler          *KeyHandler
	Display             *Display
	ScreenOutputChannel chan *types.Screen
	AudioOutputChannel  chan int
}

func NewIO() *IO {
	var i *IO = new(IO)
	i.KeyHandler = new(KeyHandler)
	i.Display = new(Display)
	i.ScreenOutputChannel = make(chan *types.Screen)
	i.AudioOutputChannel = make(chan int)
	return i
}

func (i *IO) Init(title string, screenSize int, onCloseHandler func()) error {
	/*
	var err error

	err = glfw.Init()
	if err != nil {
		return err
	}
	*/

	err := i.Display.init(title, screenSize)
	if err != nil {
		return err
	}

	i.KeyHandler.Init(DefaultControlScheme) //TODO: allow user to define controlscheme
	/*glfw.SetKeyCallback(func(key, state int) {
		if state == glfw.KeyPress {
			i.KeyHandler.KeyDown(key)
		} else {
			i.KeyHandler.KeyUp(key)
		}
	})

	glfw.SetWindowCloseCallback(func() int {
		glfw.CloseWindow()
		glfw.Terminate()
		onCloseHandler()
		return 0
	})
	*/

	return nil
}

//This will wait for updates to the display or audio and dispatch them accordingly
func (i *IO) Run() {
	for {
		select {
		case data := <-i.ScreenOutputChannel:
			i.Display.drawFrame(data)
		case data := <-i.AudioOutputChannel:
			log.Println("Writing %d to audio!", data)
		}
	}
}

type coord struct {
	x int
	y int
}

type Display struct {
	Name                 string
	ScreenSizeMultiplier int
	Screen map[coord]byte
}

func (s *Display) init(title string, screenSizeMultiplier int) error {
	s.Screen = make(map[coord]byte)
	//TODO: use ScreenSizeMultiplier as an indicator of whether to use
	//TODO: left half block or top half block
	//TODO: Perhaps use escape code to set title of terminal?

	//TODO: wrap all ansii prints in its own class with methods for each func
        //TODO: show the cursor after termination
        fmt.Printf("%c[?25l", ESC) //Hide the cursor
        fmt.Printf("%c[2J", ESC) //Clear screen
        fmt.Printf("%c[H", ESC) //Position cursor in top left

        /*for i := 0; i < 256; i++ {
                fmt.Printf("%c[48;5;%dm%d\n", ESC, i, i)
        }*/
        //TODO: see end of wiki article to prevent hanging on non-linux systems
        fmt.Printf("%c]P0000000%c\\", ESC, ESC)
        fmt.Printf("%c]P4555555%c\\", ESC, ESC)
        fmt.Printf("%c]P6AAAAAA%c\\", ESC, ESC)
        fmt.Printf("%c]P7FFFFFF%c\\", ESC, ESC)
        fmt.Printf("%c]P8000000%c\\", ESC, ESC)
        fmt.Printf("%c]PC555555%c\\", ESC, ESC)
        fmt.Printf("%c]PEAAAAAA%c\\", ESC, ESC)
        fmt.Printf("%c]PFFFFFFF%c\\", ESC, ESC)

	return nil
}

func (s *Display) drawFrame(screenData *types.Screen) {
	for y := 0; y < SCREEN_HEIGHT; y += 2 {
                for x := 0; x < SCREEN_WIDTH; x++ {
                        c1 := screenData[y][x].Red
                        c2 := screenData[y+1][x].Red

			if (s.Screen[coord{x, y}] != c1 ||
			    s.Screen[coord{x, y+1}] != c2) {
				s.Screen[coord{x, y}] = c1
				s.Screen[coord{x, y+1}] = c2
				var fg, bg int

				/*switch {
				case c1 < 100:
					fmt.Printf("%c[30m%c", ESC, '█')
				default:
					fmt.Printf("%c[37m%c", ESC, '█')
				}*/

				//TODO: in ansii class, set color/bold attr and append codes as needed
				//TODO: (and define all codes as consts)
				switch c1 {
				case 0:
					fg = 30
				case 96:
					fg = 34
				case 196:
					fg = 36
				case 235:
					fg = 37
				}

				switch c2 {
				case 0:
					bg = 40
				case 96:
					bg = 44
				case 196:
					bg = 46
				case 235:
					bg = 47
				}

				fmt.Printf("%c[%d;%dH", ESC, y/2 + 1, x + 1)
				//TODO: have a 'big' and 'small' mode (top/left half)
				//TODO: check if setfont is available on BSD and can change height
				fmt.Printf("%c[%d;%dm%c", ESC, fg, bg, '▀')
			}
                }
        }
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
)

const (
	flagClobber = 1 << iota
	flagForce
	flagVerbose
	flagNoCmds
	flagNoLinks
	flagNoMacro
	flagUnlink = flagNoCmds | (1 << iota)
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] conf src\n", path.Base(os.Args[0]))
	fmt.Fprintf(os.Stderr, "http://foosoft.net/projects/homemaker/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func main() {
	taskName := flag.String("task", "default", "name of task to execute")
	dstDir := flag.String("dest", os.Getenv("HOME"), "target directory for tasks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")
	nocmds := flag.Bool("nocmds", false, "don't execute commands")
	nolinks := flag.Bool("nolinks", false, "don't create links")
	variant := flag.String("variant", "", "execution variant for tasks and macros")
	unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")

	flag.Usage = usage
	flag.Parse()

	flags := 0
	if *clobber {
		flags |= flagClobber
	}
	if *force {
		flags |= flagForce
	}
	if *verbose {
		flags |= flagVerbose
	}
	if *nocmds {
		flags |= flagNoCmds
	}
	if *nolinks {
		flags |= flagNoLinks
	}
	if *unlink {
		flags |= flagUnlink
	}

	if flag.NArg() == 2 {
		confFile := makeAbsPath(flag.Arg(0))

		conf, err := newConfig(confFile)
		if err != nil {
			log.Fatal(err)
		}

		conf.srcDir = makeAbsPath(flag.Arg(1))
		conf.dstDir = makeAbsPath(*dstDir)
		conf.variant = *variant
		conf.flags = flags

		os.Setenv("HM_CONFIG", confFile)
		os.Setenv("HM_TASK", *taskName)
		os.Setenv("HM_SRC", conf.srcDir)
		os.Setenv("HM_DEST", conf.dstDir)
		os.Setenv("HM_VARIANT", conf.variant)

		if err := processTask(*taskName, conf); err != nil {
			log.Fatal(err)
		}
	} else {
		usage()
		os.Exit(2)
	}
}
<commit_msg>http => https<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
)

const (
	flagClobber = 1 << iota
	flagForce
	flagVerbose
	flagNoCmds
	flagNoLinks
	flagNoMacro
	flagUnlink = flagNoCmds | (1 << iota)
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] conf src\n", path.Base(os.Args[0]))
	fmt.Fprintf(os.Stderr, "https://foosoft.net/projects/homemaker/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func main() {
	taskName := flag.String("task", "default", "name of task to execute")
	dstDir := flag.String("dest", os.Getenv("HOME"), "target directory for tasks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")
	nocmds := flag.Bool("nocmds", false, "don't execute commands")
	nolinks := flag.Bool("nolinks", false, "don't create links")
	variant := flag.String("variant", "", "execution variant for tasks and macros")
	unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")

	flag.Usage = usage
	flag.Parse()

	flags := 0
	if *clobber {
		flags |= flagClobber
	}
	if *force {
		flags |= flagForce
	}
	if *verbose {
		flags |= flagVerbose
	}
	if *nocmds {
		flags |= flagNoCmds
	}
	if *nolinks {
		flags |= flagNoLinks
	}
	if *unlink {
		flags |= flagUnlink
	}

	if flag.NArg() == 2 {
		confFile := makeAbsPath(flag.Arg(0))

		conf, err := newConfig(confFile)
		if err != nil {
			log.Fatal(err)
		}

		conf.srcDir = makeAbsPath(flag.Arg(1))
		conf.dstDir = makeAbsPath(*dstDir)
		conf.variant = *variant
		conf.flags = flags

		os.Setenv("HM_CONFIG", confFile)
		os.Setenv("HM_TASK", *taskName)
		os.Setenv("HM_SRC", conf.srcDir)
		os.Setenv("HM_DEST", conf.dstDir)
		os.Setenv("HM_VARIANT", conf.variant)

		if err := processTask(*taskName, conf); err != nil {
			log.Fatal(err)
		}
	} else {
		usage()
		os.Exit(2)
	}
}
<|endoftext|>