<commit_before>package web

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"mime"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

const (
	maxFormSize int = 10 << 20 // 10 MB is a lot of text.
	formBufSize int = 512
)

func contentType(val string) string {
	var ctype string

	if strings.ContainsRune(val, '/') {
		ctype = val
	} else {
		if !strings.HasPrefix(val, ".") {
			val = "." + val
		}
		ctype = mime.TypeByExtension(val)
	}

	return ctype
}

// TryParse try parse val to v
func TryParse(val string, v interface{}) error {
	if v == nil {
		return errors.New("TryParse(nil)")
	}

	rv := reflect.ValueOf(v)

	if rv.Kind() != reflect.Ptr {
		return errors.New("TryParse(non-pointer " + reflect.TypeOf(v).String() + ")")
	}

	if rv.IsNil() {
		return errors.New("TryParse(nil)")
	}

	if rv.Kind() == reflect.Ptr && !rv.IsNil() {
		rv = rv.Elem()
	}

	if !rv.CanSet() {
		return errors.New("TryParse(can not set value to v)")
	}

	switch rv.Kind() {
	case reflect.String:
		rv.SetString(val)
		return nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return err
		}
		if rv.OverflowInt(n) {
			return errors.New("TryParse(reflect.Value.OverflowInt)")
		}
		rv.SetInt(n)
		return nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		n, err := strconv.ParseUint(val, 10, 64)
		if err != nil {
			return err
		}
		if rv.OverflowUint(n) {
			return errors.New("TryParse(reflect.Value.OverflowUint)")
		}
		rv.SetUint(n)
		return nil
	case reflect.Float32, reflect.Float64:
		n, err := strconv.ParseFloat(val, rv.Type().Bits())
		if err != nil {
			return err
		}
		if rv.OverflowFloat(n) {
			return errors.New("TryParse(reflect.Value.OverflowFloat)")
		}
		rv.SetFloat(n)
		return nil
	case reflect.Bool:
		n, err := strconv.ParseBool(val)
		if err != nil {
			return err
		}
		rv.SetBool(n)
		return nil
	default:
		return json.Unmarshal([]byte(val), v)
	}
}

// clean clean old to new and clean space ant \t
func clean(val string, old byte, new byte) string {
	l := len(val)

	prev := 0

	var str strings.Builder
	str.Grow(l)

	for pos := 0; pos < l; pos++ {
		r := val[pos]

		switch r {
		case '/':
			if pos > prev {
				str.WriteString(val[prev:pos])
			}
			prev = pos + 1

			if pos > 0 && prev < l {
				str.WriteByte('_')
			}
		case ' ', '\t':
			if pos > prev {
				str.WriteString(val[prev:pos])
			}
			prev = pos + 1
		}
	}

	if prev < l {
		str.WriteString(val[prev:])
	}

	return str.String()
}

// binaryReader decode data from binary
func binaryReader(r io.Reader, v interface{}) error {
	return errors.New("binaryReader not implemented")
}

// binaryWriter encode data to binary
func binaryWriter(w io.Writer, v interface{}) error {
	return errors.New("binaryWriter not implemented")
}

// formReader decode data from form
// ContentType: application/x-www-form-urlencoded
func formReader(r io.Reader, v interface{}) error {
	if v == nil {
		return errors.New("formReader(nil)")
	}

	rv := reflect.ValueOf(v)

	if rv.Kind() != reflect.Ptr {
		return errors.New("formReader(non-pointer " + rv.Type().String() + ")")
	}

	if rv.IsNil() {
		return errors.New("formReader(nil)")
	}

	for rv.Kind() == reflect.Ptr && !rv.IsNil() {
		rv = rv.Elem()
	}

	if rv.Kind() != reflect.Struct {
		return fmt.Errorf("formReader(unsupported type '%s')", rv.Type().String())
	}

	rt := rv.Type()

	m := make(map[string]int)

	for i := 0; i < rt.NumField(); i++ {
		tag := rt.Field(i).Tag.Get("json")
		if len(tag) > 0 && tag != "-" {
			m[tag] = i
		}
	}

	// stream
	formSize := 0

	buf := make([]byte, 0, formBufSize)

	var (
		key string
		val string
	)

	isKey := true

	for {
		prev := 0
		n, err := r.Read(buf[0:formBufSize])

		if err != nil {

			if err == io.EOF {

				if len(key) > 0 {
					if err := formKevValue(key, val, &m, &rv); err != nil {
						return err
					}
				}
				break
			}

			return err
		}

		formSize += n

		if formSize > maxFormSize {
			return errors.New("http: POST too large")
		}

		buf = buf[:n]

		for i := 0; i < n; i++ {
			r := buf[i]
			switch r {
			case '&', ';':
				if i > prev {
					val += string(buf[prev:i])
				}

				if err := formKevValue(key, val, &m, &rv); err != nil {
					return err
				}

				key = key[0:0]
				val = val[0:0]
				prev = i + 1
				isKey = true
			case '=':
				if i > prev {
					key += string(buf[prev:i])
				}
				prev = i + 1
				isKey = false
			}
		}

		if prev < n {
			if isKey {
				key += string(buf[prev:])
			} else {
				val += string(buf[prev:])
			}
		}
	}

	return nil
}

func formKevValue(key string, value string, m *map[string]int, v *reflect.Value) error {
	log.Printf("key: %s value: %s", key, value)
	var err error

	key, err = url.QueryUnescape(key)

	if err != nil {
		return err
	}

	if i, ok := (*m)[key]; ok {

		value, err = url.QueryUnescape(value)

		if err != nil {
			return err
		}

		if len(value) > 0 {
			field := v.Field(i)

			if err := tryParseField(value, &field); err != nil {
				return err
			}
		}
	}

	return nil
}

// formDataReader decode data from form
// ContentType: multipart/form-data
func formDataReader(r io.Reader, v interface{}) error {
	return errors.New("formDataReader not implemented")
}

// tryParseField try parse val to v
func tryParseField(val string, v *reflect.Value) error {

	if v.Kind() == reflect.Ptr {
		if v.IsNil() {
			v.Set(reflect.New(v.Type().Elem()))
		}
		*v = v.Elem()
	}

	if !v.IsValid() {
		return errors.New("tryParseField(rv invalid)")
	}

	if !v.CanSet() {
		return errors.New("tryParseField(can not set value to rv)")
	}

	switch v.Kind() {
	case reflect.String:
		v.SetString(val)
		return nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return err
		}
		if v.OverflowInt(n) {
			return errors.New("tryParseField(reflect.Value.OverflowInt)")
		}
		v.SetInt(n)
		return nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		n, err := strconv.ParseUint(val, 10, 64)
		if err != nil {
			return err
		}
		if v.OverflowUint(n) {
			return errors.New("tryParseField(reflect.Value.OverflowUint)")
		}
		v.SetUint(n)
		return nil
	case reflect.Float32, reflect.Float64:
		n, err := strconv.ParseFloat(val, v.Type().Bits())
		if err != nil {
			return err
		}
		if v.OverflowFloat(n) {
			return errors.New("tryParseField(reflect.Value.OverflowFloat)")
		}
		v.SetFloat(n)
		return nil
	case reflect.Bool:
		n, err := strconv.ParseBool(val)
		if err != nil {
			return err
		}
		v.SetBool(n)
		return nil
	default:
		return fmt.Errorf("tryParseField(unsupported type '%s')", v.Type().String())
	}
}
<commit_msg>add log<commit_after>package web

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"mime"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

const (
	maxFormSize int = 10 << 20 // 10 MB is a lot of text.
	formBufSize int = 512
)

func contentType(val string) string {
	var ctype string

	if strings.ContainsRune(val, '/') {
		ctype = val
	} else {
		if !strings.HasPrefix(val, ".") {
			val = "." + val
		}
		ctype = mime.TypeByExtension(val)
	}

	return ctype
}

// TryParse try parse val to v
func TryParse(val string, v interface{}) error {
	if v == nil {
		return errors.New("TryParse(nil)")
	}

	rv := reflect.ValueOf(v)

	if rv.Kind() != reflect.Ptr {
		return errors.New("TryParse(non-pointer " + reflect.TypeOf(v).String() + ")")
	}

	if rv.IsNil() {
		return errors.New("TryParse(nil)")
	}

	if rv.Kind() == reflect.Ptr && !rv.IsNil() {
		rv = rv.Elem()
	}

	if !rv.CanSet() {
		return errors.New("TryParse(can not set value to v)")
	}

	switch rv.Kind() {
	case reflect.String:
		rv.SetString(val)
		return nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return err
		}
		if rv.OverflowInt(n) {
			return errors.New("TryParse(reflect.Value.OverflowInt)")
		}
		rv.SetInt(n)
		return nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		n, err := strconv.ParseUint(val, 10, 64)
		if err != nil {
			return err
		}
		if rv.OverflowUint(n) {
			return errors.New("TryParse(reflect.Value.OverflowUint)")
		}
		rv.SetUint(n)
		return nil
	case reflect.Float32, reflect.Float64:
		n, err := strconv.ParseFloat(val, rv.Type().Bits())
		if err != nil {
			return err
		}
		if rv.OverflowFloat(n) {
			return errors.New("TryParse(reflect.Value.OverflowFloat)")
		}
		rv.SetFloat(n)
		return nil
	case reflect.Bool:
		n, err := strconv.ParseBool(val)
		if err != nil {
			return err
		}
		rv.SetBool(n)
		return nil
	default:
		return json.Unmarshal([]byte(val), v)
	}
}

// clean clean old to new and clean space ant \t
func clean(val string, old byte, new byte) string {
	l := len(val)

	prev := 0

	var str strings.Builder
	str.Grow(l)

	for pos := 0; pos < l; pos++ {
		r := val[pos]

		switch r {
		case '/':
			if pos > prev {
				str.WriteString(val[prev:pos])
			}
			prev = pos + 1

			if pos > 0 && prev < l {
				str.WriteByte('_')
			}
		case ' ', '\t':
			if pos > prev {
				str.WriteString(val[prev:pos])
			}
			prev = pos + 1
		}
	}

	if prev < l {
		str.WriteString(val[prev:])
	}

	return str.String()
}

// binaryReader decode data from binary
func binaryReader(r io.Reader, v interface{}) error {
	return errors.New("binaryReader not implemented")
}

// binaryWriter encode data to binary
func binaryWriter(w io.Writer, v interface{}) error {
	return errors.New("binaryWriter not implemented")
}

// formReader decode data from form
// ContentType: application/x-www-form-urlencoded
func formReader(r io.Reader, v interface{}) error {
	log.Printf("formReader: %v\n", v)
	if v == nil {
		return errors.New("formReader(nil)")
	}

	rv := reflect.ValueOf(v)

	if rv.Kind() != reflect.Ptr {
		return errors.New("formReader(non-pointer " + rv.Type().String() + ")")
	}

	if rv.IsNil() {
		return errors.New("formReader(nil)")
	}

	for rv.Kind() == reflect.Ptr && !rv.IsNil() {
		rv = rv.Elem()
	}

	if rv.Kind() != reflect.Struct {
		return fmt.Errorf("formReader(unsupported type '%s')", rv.Type().String())
	}

	rt := rv.Type()

	m := make(map[string]int)

	for i := 0; i < rt.NumField(); i++ {
		tag := rt.Field(i).Tag.Get("json")
		if len(tag) > 0 && tag != "-" {
			m[tag] = i
		}
	}

	// stream
	formSize := 0

	buf := make([]byte, 0, formBufSize)

	var (
		key string
		val string
	)

	isKey := true

	for {
		prev := 0
		n, err := r.Read(buf[0:formBufSize])

		if err != nil {

			if err == io.EOF {

				if len(key) > 0 {
					if err := formKevValue(key, val, &m, &rv); err != nil {
						return err
					}
				}
				break
			}

			return err
		}

		formSize += n

		if formSize > maxFormSize {
			return errors.New("http: POST too large")
		}

		buf = buf[:n]

		for i := 0; i < n; i++ {
			r := buf[i]
			switch r {
			case '&', ';':
				if i > prev {
					val += string(buf[prev:i])
				}

				if err := formKevValue(key, val, &m, &rv); err != nil {
					return err
				}

				key = key[0:0]
				val = val[0:0]
				prev = i + 1
				isKey = true
			case '=':
				if i > prev {
					key += string(buf[prev:i])
				}
				prev = i + 1
				isKey = false
			}
		}

		if prev < n {
			if isKey {
				key += string(buf[prev:])
			} else {
				val += string(buf[prev:])
			}
		}
	}

	return nil
}

func formKevValue(key string, value string, m *map[string]int, v *reflect.Value) error {
	log.Printf("key: %s value: %s\n", key, value)
	var err error

	key, err = url.QueryUnescape(key)

	if err != nil {
		return err
	}

	if i, ok := (*m)[key]; ok {

		value, err = url.QueryUnescape(value)

		if err != nil {
			return err
		}

		if len(value) > 0 {
			field := v.Field(i)

			if err := tryParseField(value, &field); err != nil {
				return err
			}
		}
	}

	return nil
}

// formDataReader decode data from form
// ContentType: multipart/form-data
func formDataReader(r io.Reader, v interface{}) error {
	return errors.New("formDataReader not implemented")
}

// tryParseField try parse val to v
func tryParseField(val string, v *reflect.Value) error {

	if v.Kind() == reflect.Ptr {
		if v.IsNil() {
			v.Set(reflect.New(v.Type().Elem()))
		}
		*v = v.Elem()
	}

	if !v.IsValid() {
		return errors.New("tryParseField(rv invalid)")
	}

	if !v.CanSet() {
		return errors.New("tryParseField(can not set value to rv)")
	}

	switch v.Kind() {
	case reflect.String:
		v.SetString(val)
		return nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return err
		}
		if v.OverflowInt(n) {
			return errors.New("tryParseField(reflect.Value.OverflowInt)")
		}
		v.SetInt(n)
		return nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		n, err := strconv.ParseUint(val, 10, 64)
		if err != nil {
			return err
		}
		if v.OverflowUint(n) {
			return errors.New("tryParseField(reflect.Value.OverflowUint)")
		}
		v.SetUint(n)
		return nil
	case reflect.Float32, reflect.Float64:
		n, err := strconv.ParseFloat(val, v.Type().Bits())
		if err != nil {
			return err
		}
		if v.OverflowFloat(n) {
			return errors.New("tryParseField(reflect.Value.OverflowFloat)")
		}
		v.SetFloat(n)
		return nil
	case reflect.Bool:
		n, err := strconv.ParseBool(val)
		if err != nil {
			return err
		}
		v.SetBool(n)
		return nil
	default:
		return fmt.Errorf("tryParseField(unsupported type '%s')", v.Type().String())
	}
}
<|endoftext|>


<commit_before>package ntdll

import (
	"reflect"
	"unsafe"
)

// CallWithExpandingBuffer calls fn which encapsulates the actual API
// call using buf and resultLength. If fn returns a status that
// indicates a too small buffer, the buffer is expanded and the call
// retried, until it succeeds (or fails for another reason).
//
// If the API call succeeds, the buffer size is adjusted according to
// resultLength.
//
// Example:
/*
var buf []byte
var rlen uint32
if st := CallWithExpandingBuffer(func() NtStatus {
    return NtQueryKey(
		h, KeyFullInformation,
		&buf[0],
		uint32(len(buf)),
		&rlen,
	)
}, &buf, &rlen); st.IsError() {
	...
}
*/
func CallWithExpandingBuffer(fn func() NtStatus, buf *[]byte, resultLength *uint32) NtStatus {
	if len(*buf) == 0 {
		*buf = make([]byte, 32, 32)
	}
	for {
		st := fn()
		if int(*resultLength) <= cap(*buf) {
			(*reflect.SliceHeader)(unsafe.Pointer(buf)).Len = int(*resultLength)
		} else {
			orig := buf
			*buf = make([]byte, int(*resultLength))
			copy(*buf, *orig)
		}
		if st == STATUS_BUFFER_OVERFLOW || st == STATUS_BUFFER_TOO_SMALL || st == STATUS_INFO_LENGTH_MISMATCH {
			continue
		} else {
			return st
		}
	}
}
<commit_msg>Improve CallWithExpandingBuffer documentation<commit_after>package ntdll

import (
	"reflect"
	"unsafe"
)

// CallWithExpandingBuffer is intended to more seamlessly dealing with
// API calls that fill a caller-provided buffer with data. Usually,
// these functions expect the address and current size of the buffer
// and a reference to which the required buffer size will be written.
//
// This function calls fn which encapsulates the actual API call using
// buf and resultLength. If fn returns an NtStatus value that
// indicates an insufficient buffer sizze, the buffer is resized and
// the call is retried. This happens until the call succeeds or fails
// for a non-buffer-related reason.
//
// The size of buf is adjusted according to resultLength.
//
// Example:
/*
var keyName string
var buf []byte
var requiredLength uint32
if st := CallWithExpandingBuffer(func() NtStatus {
	return NtQueryKey(h, KeyNameInformation, &buf[0], uint32(len(buf)), &requiredLength)
}, &buf, &requiredLength); st.IsError() {
	// handle non buffer-size-related error
} else {
	kbi := (*KeyNameInformationT)(unsafe.Pointer(&buf[0]))
	keyName = NewUnicodeStringFromBuffer(
		(*uint16)(unsafe.Pointer(&kbi.Name)),
		int(kbi.NameLength)).String()
}
*/
func CallWithExpandingBuffer(fn func() NtStatus, buf *[]byte, resultLength *uint32) NtStatus {
	if len(*buf) == 0 {
		*buf = make([]byte, 32, 32)
	}
	for {
		st := fn()
		if int(*resultLength) <= cap(*buf) {
			(*reflect.SliceHeader)(unsafe.Pointer(buf)).Len = int(*resultLength)
		} else {
			orig := buf
			*buf = make([]byte, int(*resultLength))
			copy(*buf, *orig)
		}
		if st == STATUS_BUFFER_OVERFLOW || st == STATUS_BUFFER_TOO_SMALL || st == STATUS_INFO_LENGTH_MISMATCH {
			continue
		} else {
			return st
		}
	}
}
<|endoftext|>


<commit_before>package jsonez

import (
	"errors"
	"fmt"
	"runtime"
)

/**
 * Utility functions used by other functions/methods
 */

/**
 * Function to get the current function name for
 * Error reporting
 */
func funcName() string {
	pc, _, _, _ := runtime.Caller(1)
	return runtime.FuncForPC(pc).Name()
}

/**
 * Function to create a null object
 */
func AllocNull() *GoJSON {
	child := new(GoJSON)
	child.Jsontype = JSON_NULL

	return child
}

/**
 * Function to create a GoJSON string objext
 */ /**
 * Function to create a GoJSON bool object
 */
func AllocString(val string) *GoJSON {
	child := new(GoJSON)

	child.Valstr = val
	child.Jsontype = JSON_STRING

	return child
}

/**
 * Function to create a GoJSON bool object
 */
func AllocBool(val bool) *GoJSON {
	child := new(GoJSON)

	child.Jsontype = JSON_BOOL
	if val == false {
		child.Valbool = false
	} else {
		child.Valbool = true
	}

	return child
}

/**
 * Function to create a GoJSON number object
 */
func AllocNumber(val float64, numtype int) *GoJSON {
	child := new(GoJSON)

	if numtype == JSON_INT {
		child.Valint = int64(val)
		child.Jsontype = JSON_INT
	} else if numtype == JSON_UINT {
		child.Valuint = uint64(val)
		child.Jsontype = JSON_UINT
	} else {
		child.Valdouble = val
		child.Jsontype = JSON_DOUBLE
	}

	return child
}

/**
 * Function to create a GoJSON array object
 */
func AllocArray() *GoJSON {
	var child *GoJSON
	child = new(GoJSON)
	child.Jsontype = JSON_ARRAY

	return child
}

/**
 * Function to create a GoJSON object for
 * an JSON object
 */
func AllocObject() *GoJSON {
	child := new(GoJSON)
	child.Jsontype = JSON_OBJECT

	return child
}

/**
 * Function to resolve a link when
 * new entries are added to an array or an
 * object.
 */
func resolveLink(prev *GoJSON, cur *GoJSON) {
	prev.Next = cur
	cur.Prev = prev
}

/**
 * Function to resolve an interface to a json type
 */
func resolveInterface(v interface{}) (int, error) {
	switch v.(type) {
	case int:
		return JSON_INT, nil
	case uint:
		return JSON_UINT, nil
	case float64:
		return JSON_DOUBLE, nil
	case bool:
		return JSON_BOOL, nil
	case string:
		return JSON_STRING, nil
	default:
		errorStr := fmt.Sprintf("%s: Unknown data type", funcName())
		return -1, errors.New(errorStr)
	}

}
<commit_msg>changes to resolveInterface<commit_after>package jsonez

import (
	"errors"
	"fmt"
	"runtime"
)

/**
 * Utility functions used by other functions/methods
 */

/**
 * Function to get the current function name for
 * Error reporting
 */
func funcName() string {
	pc, _, _, _ := runtime.Caller(1)
	return runtime.FuncForPC(pc).Name()
}

/**
 * Function to create a null object
 */
func AllocNull() *GoJSON {
	child := new(GoJSON)
	child.Jsontype = JSON_NULL

	return child
}

/**
 * Function to create a GoJSON string objext
 */ /**
 * Function to create a GoJSON bool object
 */
func AllocString(val string) *GoJSON {
	child := new(GoJSON)

	child.Valstr = val
	child.Jsontype = JSON_STRING

	return child
}

/**
 * Function to create a GoJSON bool object
 */
func AllocBool(val bool) *GoJSON {
	child := new(GoJSON)

	child.Jsontype = JSON_BOOL
	if val == false {
		child.Valbool = false
	} else {
		child.Valbool = true
	}

	return child
}

/**
 * Function to create a GoJSON number object
 */
func AllocNumber(val float64, numtype int) *GoJSON {
	child := new(GoJSON)

	if numtype == JSON_INT {
		child.Valint = int64(val)
		child.Jsontype = JSON_INT
	} else if numtype == JSON_UINT {
		child.Valuint = uint64(val)
		child.Jsontype = JSON_UINT
	} else {
		child.Valdouble = val
		child.Jsontype = JSON_DOUBLE
	}

	return child
}

/**
 * Function to create a GoJSON array object
 */
func AllocArray() *GoJSON {
	var child *GoJSON
	child = new(GoJSON)
	child.Jsontype = JSON_ARRAY

	return child
}

/**
 * Function to create a GoJSON object for
 * an JSON object
 */
func AllocObject() *GoJSON {
	child := new(GoJSON)
	child.Jsontype = JSON_OBJECT

	return child
}

/**
 * Function to resolve a link when
 * new entries are added to an array or an
 * object.
 */
func resolveLink(prev *GoJSON, cur *GoJSON) {
	prev.Next = cur
	cur.Prev = prev
}

/**
 * Function to resolve an interface to a json type
 */
func resolveInterface(v interface{}) (int, error) {
	switch v.(type) {
	case int:
		return JSON_INT, nil
	case int8:
		return JSON_INT, nil
	case int16:
		return JSON_INT, nil
	case int32:
		return JSON_INT, nil
	case int64:
		return JSON_INT, nil
	case uint:
		return JSON_INT, nil
	case uint8:
		return JSON_INT, nil
	case uint16:
		return JSON_INT, nil
	case uint32:
		return JSON_INT, nil
	case uint64:
		return JSON_INT, nil
	case float64:
		return JSON_DOUBLE, nil
	case bool:
		return JSON_BOOL, nil
	case string:
		return JSON_STRING, nil
	default:
		errorStr := fmt.Sprintf("%s: Unknown data type", funcName())
		return -1, errors.New(errorStr)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

// A map for Puppet Versions JSON
type PuppetVersions map[string]interface{}

// A map for Production Versions JSON
type RunningServices interface{}

// Our bare page
type Page struct {
	Title string
	Body  []byte
	Pv    PuppetVersions
	Rv    RunningServices
}

func puppetversions(url string) (PuppetVersions, error) {
	// Get response and handle any errors on return
	resp, err := http.Get(url)
	defer resp.Body.Close()
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// Read JSON from request
	jsonDataFromHttp, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Unmarshel the JSON to our struct
	var v PuppetVersions
	err = json.Unmarshal(jsonDataFromHttp, &v)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func getServices(url string) (interface{}, error) {
	resp, err := http.Get(url)
	defer resp.Body.Close()
	if err != nil {
		log.Println(err)
		return nil, err
	}

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

	var available_services interface{}

	err = json.Unmarshal(jsonDataFromHttp, &available_services)
	if err != nil {
		return nil, err
	}

	return available_services, nil
}

//func parseInfo(info map[string]string) (version string, err error) {
//	for k, v := range info {
//		for key, value := range info {
//			version := info["version"]
//			return version, nil
//		}
//	}
//	return "Didn't parse the info map", err
//}

func getVersions(services interface{}) (runningversions map[string]map[string]string, err error) {

	rv := make(map[string]map[string]string)

	s := services.(map[string]interface{})
	for k, v := range s {
		log.Println("Ranging over ", k)
		switch values := v.(type) {
		case map[string]interface{}:
			for name, endpoints := range values {
				log.Println("Found service: ", name)
				log.Println("Available endpoints: ", endpoints)

				rv[name] = make(map[string]string)

				switch eps := endpoints.(type) {
				case []interface{}:
					for _, ep := range eps {
						log.Println("Endpoint: ", ep)
						switch ep_string := ep.(type) {
						case string:
							query_arry := strings.Fields(ep_string)
							if len(query_arry) == 2 {
								log.Println("IP 1: ", query_arry[0])
								log.Println("IP 2: ", query_arry[1])
								info_ep := query_arry[1]
								rv[name][info_ep] = "blah"

							} else {
								log.Println("IP 1: ", query_arry[0])
								info_ep := query_arry[0]
								rv[name][info_ep] = "blah"

							}
						}
					}
				}
			}
		}
	}
	runningversions = rv
	return runningversions, nil
}

// If our string has one component it's a service address
//				if len(query_arry) == 1 {
//					mgmt_ip := query_arry[0]
//
//					// Add info endpoint
//					info_uri_slice := []string{mgmt_ip, "/info"}
//					info_uri := strings.Join(info_uri_slice, "")
//
//					log.Println("Querying SERVICE address for ", service_name, ": ", mgmt_ip)
//					// Query the URI
//					resp, err := http.Get(info_uri)
//					defer resp.Body.Close()
//					if err != nil {
//						log.Println("ERROR querying ", service_name, " ", err)
//						return nil, err
//					}
//					// Get data and unmarshel the JSON to our map
//					jsonDataFromHttp, err := ioutil.ReadAll(resp.Body)
//					if err != nil {
//						log.Println("ERROR unmarsheling data for ", service_name, " from ", jsonDataFromHttp)
//						return nil, err
//					}
//					var info_response map[string]string
//					err = json.Unmarshal(jsonDataFromHttp, &info_response)
//					if err != nil {
//						return nil, err
//					}
//					// Parse out the version from the response
//					log.Println("INFO for ", info_uri, ":\n", info_response)
//					version, _ := parseInfo(info_response)
//
//					runningversions.Service[service_name][info_uri] = append(runningversions.Service[service_name][info_uri], version)

// If our string has two components then use the mgmt address
//				} else if len(query_arry) == 2 {
//					mgmt_ip := query_arry[1]
//
//					// Add info endpoint
//					info_uri_slice := []string{mgmt_ip, "/info"}
//					info_uri := strings.Join(info_uri_slice, "")
//					log.Println("Querying SERVICE address for ", service_name, ": ", mgmt_ip)
//					// Query the URI
//					resp, err := http.Get(info_uri)
//					defer resp.Body.Close()
//					if err != nil {
//						log.Println("ERROR querying ", service_name, " ", err)
//						return nil, err
//					}
//					// Get data and unmarshel the JSON to our map
//					jsonDataFromHttp, err := ioutil.ReadAll(resp.Body)
//					if err != nil {
//						log.Println("ERROR unmarsheling data for ", service_name, " from ", jsonDataFromHttp)
//						return nil, err
//					}
//					var info_response map[string]interface{}
//					err = json.Unmarshal(jsonDataFromHttp, &info_response)
//					if err != nil {
//						return nil, err
//					}
//					// Parse out the version from the response
//					log.Println("INFO for ", service_name, ":\n", info_response)
//					versions := parseInfo(service_name, info_response)
//
//					return versions, nil
//
//					// If our string as more than two components, error
//				} else if len(query_arry) > 2 {
//					return nil, err
//				}
//	return runningversions, err
//}

func loadPage(title string) (*Page, error) {
	// Get the versions from the puppet master
	log.Println("Getting Puppet Versions - Make sure VPN is on!")
	pv, err := puppetversions("http://puppet.ec2.srcclr.com:1015/versions")
	if err != nil {
		log.Println("Failed to get Puppet Versions from http://puppet.ec2.srcclr.com:1015/versions\n")
		log.Println(err)
	}
	log.Println("Puppet Versions: ", pv)

	// Get running services, prs
	log.Println("Getting available services...")
	prs, err := getServices("http://localhost:3000/services")
	if err != nil {
		log.Println("Failed getting production versions")
	}

	log.Println("RUNNING SERVICES @localhost:3000/services: ", prs)

	// Get running versions
	log.Println("Getting running versions for ", prs)
	rv, err := getVersions(prs)
	if err != nil {
		log.Println("Failed getting versions for ", prs)
	}

	log.Println("Running Versions: ", rv)

	filename := title + ".html"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return &Page{
			Title: title,
			Body:  body,
			Pv:    pv,
			Rv:    rv,
		},
		nil
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Loading page view...")
	title := "versionctl"
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}

	// Parse the template, execute and write it to stdout for good measure
	log.Println("Parsing go template...")
	t, _ := template.ParseFiles("versionctl.html")
	t.Execute(w, p)
	log.Println("Serving:\n", string(p.Title), string(p.Body))
}

func main() {
	log.Println("Starting vctl...")
	http.HandleFunc("/", viewHandler)
	http.ListenAndServe(":9000", nil)
}
<commit_msg>vctl.go<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

// A map for Puppet Versions JSON
type PuppetVersions map[string]interface{}

// A map for Production Versions JSON
type RunningServices interface{}

// Our bare page
type Page struct {
	Title string
	Body  []byte
	Pv    PuppetVersions
	Rv    RunningServices
}

func puppetversions(url string) (PuppetVersions, error) {
	// Get response and handle any errors on return
	resp, err := http.Get(url)
	defer resp.Body.Close()
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// Read JSON from request
	jsonDataFromHttp, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Unmarshel the JSON to our struct
	var v PuppetVersions
	err = json.Unmarshal(jsonDataFromHttp, &v)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func getServices(url string) (interface{}, error) {
	resp, err := http.Get(url)
	defer resp.Body.Close()
	if err != nil {
		log.Println(err)
		return nil, err
	}

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

	var available_services interface{}

	err = json.Unmarshal(jsonDataFromHttp, &available_services)
	if err != nil {
		return nil, err
	}

	return available_services, nil
}

//func parseInfo(info map[string]string) (version string, err error) {
//	for k, v := range info {
//		for key, value := range info {
//			version := info["version"]
//			return version, nil
//		}
//	}
//	return "Didn't parse the info map", err
//}

func queryServiceVersion(endpoint) (string, error) {
	log.Println("Querying SERVICE address: ", endpoint)
	// Query the URI
	resp, err := http.Get(endpoint)
	defer resp.Body.Close()
	if err != nil {
		log.Println("ERROR querying ", endpoint, " ", err)
		return nil, err
	}
	// Get data and unmarshel the JSON to our map
	jsonDataFromHttp, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("ERROR unmarsheling data for ", service_name, " from ", jsonDataFromHttp)
		return nil, err
	}
	var info_response map[string]string
	err = json.Unmarshal(jsonDataFromHttp, &info_response)
	if err != nil {
		return nil, err
	}
	// Parse out the version from the response
	log.Println("INFO for ", info_uri, ":\n", info_response)

}

func getVersions(services interface{}) (runningversions map[string]map[string]string, err error) {

	rv := make(map[string]map[string]string)

	s := services.(map[string]interface{})
	for k, v := range s {
		log.Println("Ranging over ", k)
		switch values := v.(type) {
		case map[string]interface{}:
			for name, endpoints := range values {
				log.Println("Found service: ", name)
				log.Println("Available endpoints: ", endpoints)

				rv[name] = make(map[string]string)

				switch eps := endpoints.(type) {
				case []interface{}:
					for _, ep := range eps {
						log.Println("Endpoint: ", ep)
						switch ep_string := ep.(type) {
						case string:
							query_arry := strings.Fields(ep_string)
							if len(query_arry) == 2 {
								log.Println("IP 1: ", query_arry[0])
								log.Println("IP 2: ", query_arry[1])
								info_ep := query_arry[1]
								rv[name][info_ep] = "blah"

							} else {
								log.Println("IP 1: ", query_arry[0])
								info_ep := query_arry[0]
								rv[name][info_ep] = "blah"

							}
						}
					}
				}
			}
		}
	}
	runningversions = rv
	return runningversions, nil
}

// If our string has one component it's a service address
//				if len(query_arry) == 1 {
//					mgmt_ip := query_arry[0]
//
//					// Add info endpoint
//					info_uri_slice := []string{mgmt_ip, "/info"}
//					info_uri := strings.Join(info_uri_slice, "")
//
//					version, _ := parseInfo(info_response)
//
//					runningversions.Service[service_name][info_uri] = append(runningversions.Service[service_name][info_uri], version)

// If our string has two components then use the mgmt address
//				} else if len(query_arry) == 2 {
//					mgmt_ip := query_arry[1]
//
//					// Add info endpoint
//					info_uri_slice := []string{mgmt_ip, "/info"}
//					info_uri := strings.Join(info_uri_slice, "")
//					log.Println("Querying SERVICE address for ", service_name, ": ", mgmt_ip)
//					// Query the URI
//					resp, err := http.Get(info_uri)
//					defer resp.Body.Close()
//					if err != nil {
//						log.Println("ERROR querying ", service_name, " ", err)
//						return nil, err
//					}
//					// Get data and unmarshel the JSON to our map
//					jsonDataFromHttp, err := ioutil.ReadAll(resp.Body)
//					if err != nil {
//						log.Println("ERROR unmarsheling data for ", service_name, " from ", jsonDataFromHttp)
//						return nil, err
//					}
//					var info_response map[string]interface{}
//					err = json.Unmarshal(jsonDataFromHttp, &info_response)
//					if err != nil {
//						return nil, err
//					}
//					// Parse out the version from the response
//					log.Println("INFO for ", service_name, ":\n", info_response)
//					versions := parseInfo(service_name, info_response)
//
//					return versions, nil
//
//					// If our string as more than two components, error
//				} else if len(query_arry) > 2 {
//					return nil, err
//				}
//	return runningversions, err
//}

func loadPage(title string) (*Page, error) {
	// Get the versions from the puppet master
	log.Println("Getting Puppet Versions - Make sure VPN is on!")
	pv, err := puppetversions("http://puppet.ec2.srcclr.com:1015/versions")
	if err != nil {
		log.Println("Failed to get Puppet Versions from http://puppet.ec2.srcclr.com:1015/versions\n")
		log.Println(err)
	}
	log.Println("Puppet Versions: ", pv)

	// Get running services, prs
	log.Println("Getting available services...")
	prs, err := getServices("http://is.qa.ec2.srcclr.com:3000/services")
	if err != nil {
		log.Println("Failed getting production versions")
	}

	log.Println("RUNNING SERVICES @localhost:3000/services: ", prs)

	// Get running versions
	log.Println("Getting running versions for ", prs)
	rv, err := getVersions(prs)
	if err != nil {
		log.Println("Failed getting versions for ", prs)
	}

	log.Println("Running Versions: ", rv)

	filename := title + ".html"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return &Page{
			Title: title,
			Body:  body,
			Pv:    pv,
			Rv:    rv,
		},
		nil
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Loading page view...")
	title := "versionctl"
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}

	// Parse the template, execute and write it to stdout for good measure
	log.Println("Parsing go template...")
	t, _ := template.ParseFiles("versionctl.html")
	t.Execute(w, p)
	log.Println("Serving:\n", string(p.Title), string(p.Body))
}

func main() {
	log.Println("Starting vctl...")
	http.HandleFunc("/", viewHandler)
	http.ListenAndServe(":9000", nil)
}
<|endoftext|>


<commit_before>package Golf

import (
	"bytes"
	"errors"
	"html/template"
)

// View handles templates rendering
type View struct {
	FuncMap template.FuncMap

	// A view may have multiple template managers, e.g., one for the admin panel,
	// another one for the user end.
	templateLoader map[string]*TemplateManager
}

// NewView creates a new View instance.
func NewView() *View {
	view := new(View)
	view.templateLoader = make(map[string]*TemplateManager)
	view.FuncMap = make(template.FuncMap)
	view.FuncMap["Html"] = func(s string) template.HTML { return template.HTML(s) }
	return view
}

// Render renders a template by indicating the file path and the name of the template loader.
func (view *View) Render(loaderName string, filePath string, data map[string]interface{}) (string, error) {
	loader, ok := view.templateLoader[loaderName]
	if !ok {
		panic(errors.New("Template loader not found: " + loaderName))
	}
	var buf bytes.Buffer
	e := loader.Render(&buf, filePath, data)
	if e != nil {
		return "", e
	}
	return buf.String(), nil
}

// RenderFromString does the same thing as render but renders a template by
// indicating the template source from tplSrc.
func (view *View) RenderFromString(loaderName string, tplSrc string, data map[string]interface{}) (string, error) {
	var buf bytes.Buffer
	// If loaderName is not indicated, use the default template library of Go, no syntax like
	// `extends` or `include` will be supported.
	if loaderName == "" {
		tmpl := template.New("error")
		tmpl.Parse(tplSrc)
		e := tmpl.Execute(&buf, data)
		return buf.String(), e
	}
	loader, ok := view.templateLoader[loaderName]
	if !ok {
		panic(Errorf("Template loader not fount: %s", loaderName))
	}
	e := loader.Render(&buf, tplSrc, data)
	if e != nil {
		return "", e
	}
	return buf.String(), nil
}

// SetTemplateLoader sets the template loader by indicating the name and the path.
func (view *View) SetTemplateLoader(name string, path string) {
	loader := &TemplateManager{
		Loader: &FileSystemLoader{
			BaseDir: path,
		},
		FuncMap: view.FuncMap,
	}
	view.templateLoader[name] = loader
}

// ErrorPage returns an error page.
func (view *View) ErrorPage(err error) string {
	text := `<!DOCTYPE HTML>  <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>{{.Title}}</title>

{{.Title}}

{{.Content}}

`
	tmpl := template.New("error")
	tmpl.Parse(text)
	var buf bytes.Buffer
	tmpl.Execute(&buf, map[string]interface{}{"Title": "Server Error", "Content": err.Error()})
	return buf.String()
}
<commit_msg>[refactor] Remove unused methods.<commit_after>package Golf

import (
	"bytes"
	"errors"
	"html/template"
)

// View handles templates rendering
type View struct {
	FuncMap template.FuncMap

	// A view may have multiple template managers, e.g., one for the admin panel,
	// another one for the user end.
	templateLoader map[string]*TemplateManager
}

// NewView creates a new View instance.
func NewView() *View {
	view := new(View)
	view.templateLoader = make(map[string]*TemplateManager)
	view.FuncMap = make(template.FuncMap)
	view.FuncMap["Html"] = func(s string) template.HTML { return template.HTML(s) }
	return view
}

// Render renders a template by indicating the file path and the name of the template loader.
func (view *View) Render(loaderName string, filePath string, data map[string]interface{}) (string, error) {
	loader, ok := view.templateLoader[loaderName]
	if !ok {
		panic(errors.New("Template loader not found: " + loaderName))
	}
	var buf bytes.Buffer
	e := loader.Render(&buf, filePath, data)
	if e != nil {
		return "", e
	}
	return buf.String(), nil
}

// RenderFromString does the same thing as render but renders a template by
// indicating the template source from tplSrc.
func (view *View) RenderFromString(loaderName string, tplSrc string, data map[string]interface{}) (string, error) {
	var buf bytes.Buffer
	// If loaderName is not indicated, use the default template library of Go, no syntax like
	// `extends` or `include` will be supported.
	if loaderName == "" {
		tmpl := template.New("error")
		tmpl.Parse(tplSrc)
		e := tmpl.Execute(&buf, data)
		return buf.String(), e
	}
	loader, ok := view.templateLoader[loaderName]
	if !ok {
		panic(Errorf("Template loader not fount: %s", loaderName))
	}
	e := loader.Render(&buf, tplSrc, data)
	if e != nil {
		return "", e
	}
	return buf.String(), nil
}

// SetTemplateLoader sets the template loader by indicating the name and the path.
func (view *View) SetTemplateLoader(name string, path string) {
	loader := &TemplateManager{
		Loader: &FileSystemLoader{
			BaseDir: path,
		},
		FuncMap: view.FuncMap,
	}
	view.templateLoader[name] = loader
}
<|endoftext|>


<commit_before>package styx

import (
	"os"
	"path"
	"strings"
	"sync/atomic"

	"golang.org/x/net/context"

	"aqwari.net/net/styx/internal/styxfile"
	"aqwari.net/net/styx/styxproto"
)

// The walk RPC is one of the more complex requests in the 9P protocol
// (though that's not saying much). There are a number of different
// responses that a server must make:
//
// 	- If the file exists: Rwalk with nwname qids
// 	- If no elements in the path exist: Rerror
// 	- If at least 1 element in the path exists: Rwalk with n(<nwname) qids
//
// In addition, walks are relative to another file, so a server must track
// that as well. The styx package attempts to hide this complexity from the
// (API) user.
//
// For any Twalk requests with nwname > 1, the styx package will synthesize
// nwname individual Twalk requests, such that each request is the next
// element in the path.
//
// The relative path in the Twalk request is combined with the path of fid
// to produce an absolute path to the requested file. All paths are Cleaned
// before being passed to user code.
//
// Thus given the following protocol message (fids replaced with their paths):
//
// 	Twalk /usr/share 6 ../include/linux/../../bin
//
// The user's program will instead see and respond to, *IN THIS ORDER*
//
// 	Twalk /usr
// 	Twalk /usr/include
// 	Twalk /usr/include/linux
// 	Twalk /usr/include
// 	Twalk /usr
// 	Twalk /usr/bin
//
// The order that the program sees the path in is important, as it allows
// certain synthetic file systems to create resources "on-demand", as the
// client asks for them.
type walkElem struct {
	index int
	qid   styxproto.Qid // nil if not present
}

type walker struct {
	qids, found []styxproto.Qid
	filled      []int32
	count       int
	complete    chan struct{}
	collect     chan walkElem
	newfid      uint32
	path        string

	// for cancellation
	cx context.Context

	session *Session
	tag     uint16
}

func newWalker(s *Session, cx context.Context, msg styxproto.Twalk, base string, elem ...string) *walker {
	qids := make([]styxproto.Qid, len(elem))
	found := qids[:0]
	newpath := path.Join(base, strings.Join(elem, "/"))
	w := &walker{
		qids:     qids,
		found:    found,
		filled:   make([]int32, len(elem)),
		complete: make(chan struct{}),
		collect:  make(chan walkElem),
		session:  s,
		newfid:   msg.Newfid(),
		path:     newpath,
		tag:      msg.Tag(),
		cx:       cx,
	}
	go w.run()
	return w
}

// runs in its own goroutine
func (w *walker) run() {
Loop:
	for {
		select {
		case <-w.cx.Done():
			break Loop
		case el, ok := <-w.collect:
			if !ok {
				break Loop
			}
			filledp := &w.filled[el.index]
			if !atomic.CompareAndSwapInt32(filledp, 0, 1) {
				continue
			}
			w.count++
			w.qids[el.index] = el.qid
			for i := len(w.found); i < cap(w.found); i++ {
				if w.qids[i] != nil {
					w.found = w.found[:i+1]
				}
			}
			if w.count == len(w.qids) {
				break Loop
			}
		}
	}
	close(w.complete)
	w.session.conn.clearTag(w.tag)
	if len(w.found) == 0 {
		w.session.conn.Rerror(w.tag, "No such file or directory")
	} else {
		w.session.files.Put(w.newfid, file{name: w.path})
		w.session.conn.sessionFid.Put(w.newfid, w.session)
		w.session.IncRef()
		if err := w.session.conn.Rwalk(w.tag, w.found...); err != nil {
			panic(err) // should never happen
		}
	}
	w.session.conn.Flush()
}

// A client sends a Twalk message both to probe if a file exists, and to
// move a "cursor" within the filesystem hierarchy. In a traditional file
// system, a Twalk request is similar to using chdir to change the current
// directory.  File servers are free to attach additional meaning to Twalk
// requests. For instance, a server may create directories on-demand as
// clients walk to them.
//
// The 9P protocol allows for clients to walk multiple directories with
// a single 9P message. The styx package translates such requests into
// multiple Twalk values, providing the following guarantees:
//
// 	- Path() will return a cleaned, absolute path
// 	- Consecutive, related Twalk requests will differ by at
// 	  most 1 path element.
//
// The default response to a Twalk request is an Rerror message saying
// "No such file or directory".
type Twalk struct {
	index int
	walk  *walker
	reqInfo
}

func (t Twalk) WithContext(ctx context.Context) Request {
	t.ctx = ctx
	return t
}

func (t Twalk) handled() bool {
	return atomic.LoadInt32(&t.walk.filled[t.index]) == 1
}

// Rwalk signals to the client that the file named by the Twalk's
// Path method exists and is of the given mode. The permission bits of
// mode are ignored, and only the file type bits, such as os.ModeDir,
// are sent to the client.
func (t Twalk) Rwalk(mode os.FileMode) {
	qid := t.session.conn.qid(t.Path(), styxfile.QidType(styxfile.Mode9P(mode)))
	select {
	case t.walk.collect <- walkElem{qid: qid, index: t.index}:
	case <-t.walk.complete:
	}
}

// Rerror signals to the client that the file named by the Twalk's
// Path method does not exist.
func (t Twalk) Rerror(format string, args ...interface{}) {
	select {
	case t.walk.collect <- walkElem{index: t.index}:
	case <-t.walk.complete:
	}
}

func (t Twalk) defaultResponse() {
	t.Rerror("No such file or directory")
}
<commit_msg>Remove unnecessary use of atomic package<commit_after>package styx

import (
	"os"
	"path"
	"strings"

	"golang.org/x/net/context"

	"aqwari.net/net/styx/internal/styxfile"
	"aqwari.net/net/styx/styxproto"
)

// The walk RPC is one of the more complex requests in the 9P protocol
// (though that's not saying much). There are a number of different
// responses that a server must make:
//
// 	- If the file exists: Rwalk with nwname qids
// 	- If no elements in the path exist: Rerror
// 	- If at least 1 element in the path exists: Rwalk with n(<nwname) qids
//
// In addition, walks are relative to another file, so a server must track
// that as well. The styx package attempts to hide this complexity from the
// (API) user.
//
// For any Twalk requests with nwname > 1, the styx package will synthesize
// nwname individual Twalk requests, such that each request is the next
// element in the path.
//
// The relative path in the Twalk request is combined with the path of fid
// to produce an absolute path to the requested file. All paths are Cleaned
// before being passed to user code.
//
// Thus given the following protocol message (fids replaced with their paths):
//
// 	Twalk /usr/share 6 ../include/linux/../../bin
//
// The user's program will instead see and respond to, *IN THIS ORDER*
//
// 	Twalk /usr
// 	Twalk /usr/include
// 	Twalk /usr/include/linux
// 	Twalk /usr/include
// 	Twalk /usr
// 	Twalk /usr/bin
//
// The order that the program sees the path in is important, as it allows
// certain synthetic file systems to create resources "on-demand", as the
// client asks for them.
type walkElem struct {
	index int
	qid   styxproto.Qid // nil if not present
}

type walker struct {
	qids, found []styxproto.Qid
	filled      []int32
	count       int
	complete    chan struct{}
	collect     chan walkElem
	newfid      uint32
	path        string

	// for cancellation
	cx context.Context

	session *Session
	tag     uint16
}

func newWalker(s *Session, cx context.Context, msg styxproto.Twalk, base string, elem ...string) *walker {
	qids := make([]styxproto.Qid, len(elem))
	found := qids[:0]
	newpath := path.Join(base, strings.Join(elem, "/"))
	w := &walker{
		qids:     qids,
		found:    found,
		filled:   make([]int32, len(elem)),
		complete: make(chan struct{}),
		collect:  make(chan walkElem),
		session:  s,
		newfid:   msg.Newfid(),
		path:     newpath,
		tag:      msg.Tag(),
		cx:       cx,
	}
	go w.run()
	return w
}

// runs in its own goroutine
func (w *walker) run() {
Loop:
	for {
		select {
		case <-w.cx.Done():
			break Loop
		case el, ok := <-w.collect:
			if !ok {
				break Loop
			}
			w.count++
			w.qids[el.index] = el.qid
			for i := len(w.found); i < cap(w.found); i++ {
				if w.qids[i] != nil {
					w.found = w.found[:i+1]
				}
			}
			if w.count == len(w.qids) {
				break Loop
			}
		}
	}
	close(w.complete)
	w.session.conn.clearTag(w.tag)
	if len(w.found) == 0 {
		w.session.conn.Rerror(w.tag, "No such file or directory")
	} else {
		w.session.files.Put(w.newfid, file{name: w.path})
		w.session.conn.sessionFid.Put(w.newfid, w.session)
		w.session.IncRef()
		if err := w.session.conn.Rwalk(w.tag, w.found...); err != nil {
			panic(err) // should never happen
		}
	}
	w.session.conn.Flush()
}

// A client sends a Twalk message both to probe if a file exists, and to
// move a "cursor" within the filesystem hierarchy. In a traditional file
// system, a Twalk request is similar to using chdir to change the current
// directory.  File servers are free to attach additional meaning to Twalk
// requests. For instance, a server may create directories on-demand as
// clients walk to them.
//
// The 9P protocol allows for clients to walk multiple directories with
// a single 9P message. The styx package translates such requests into
// multiple Twalk values, providing the following guarantees:
//
// 	- Path() will return a cleaned, absolute path
// 	- Consecutive, related Twalk requests will differ by at
// 	  most 1 path element.
//
// The default response to a Twalk request is an Rerror message saying
// "No such file or directory".
type Twalk struct {
	index int
	walk  *walker
	reqInfo
}

func (t Twalk) WithContext(ctx context.Context) Request {
	t.ctx = ctx
	return t
}

func (t Twalk) handled() bool {
	return t.walk.filled[t.index] == 1
}

// Rwalk signals to the client that the file named by the Twalk's
// Path method exists and is of the given mode. The permission bits of
// mode are ignored, and only the file type bits, such as os.ModeDir,
// are sent to the client.
func (t Twalk) Rwalk(mode os.FileMode) {
	qid := t.session.conn.qid(t.Path(), styxfile.QidType(styxfile.Mode9P(mode)))
	t.walk.filled[t.index] = 1
	select {
	case t.walk.collect <- walkElem{qid: qid, index: t.index}:
	case <-t.walk.complete:
	}
}

// Rerror signals to the client that the file named by the Twalk's
// Path method does not exist.
func (t Twalk) Rerror(format string, args ...interface{}) {
	select {
	case t.walk.collect <- walkElem{index: t.index}:
	case <-t.walk.complete:
	}
}

func (t Twalk) defaultResponse() {
	t.Rerror("No such file or directory")
}
<|endoftext|>


<commit_before>// Package work eases concurrency patterns by providing often used helpers.
package work

import (
	"runtime"
	"sync"
	"sync/atomic"
)

// numRoutines defines the default maximum number of goroutines based on GOMAXPROCS.
var numRoutines int

func init() {
	numRoutines = runtime.GOMAXPROCS(0)
}

// do spawns workers with index 0 to n-1, limiting their numbers by max.
func do(n int, worker func(int), max int) {
	var wg sync.WaitGroup
	if n <= max {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				worker(idx)
				wg.Done()
			}(i)
		}
		wg.Wait()
		return
	}

	// spawn the maximum number of goroutines
	wg.Add(max)
	for i := 0; i < max; i++ {
		go func(idx int) {
			for ; idx < n; idx += max {
				worker(idx)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}

// doWithError spawns workers with index 0 to n-1, limiting their numbers by max.
// Similar to do but with error handling.
// The first error encountered aborts all processing and is then returned.
func doWithError(n int, worker func(int) error, max int) error {
	var (
		errv atomic.Value // worker error
		wg   sync.WaitGroup
	)

	if n <= max {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				if errv.Load() == nil {
					if err := worker(idx); err != nil {
						errv.Store(err)
					}
				}
				wg.Done()
			}(i)
		}
		wg.Wait()

		if err := errv.Load(); err != nil {
			return err.(error)
		}
		return nil
	}

	// spawn the maximum number of goroutines
	wg.Add(max)
	for i := 0; i < max; i++ {
		go func(idx int) {
			for ; idx < n && errv.Load() == nil; idx += max {
				if err := worker(idx); err != nil {
					errv.Store(err)
					break
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}

// Do spawns workers with index 0 to n-1, limiting their numbers by GOMAXPROCS.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func Do(n int, worker, finalizer func(idx int)) {
	DoN(n, worker, finalizer, numRoutines)
}

// DoN spawns workers with index 0 to n-1, limiting their numbers by max.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoN(n int, worker, finalizer func(idx int), max int) {
	switch n {
	case 0:
		return
	case 1:
		worker(0)
		if finalizer != nil {
			finalizer(0)
		}
		return
	}

	if finalizer == nil {
		do(n, worker, max)
		return
	}

	var (
		donec   = make(chan struct{}, max) // worker throttling
		workc   = make(chan int)           // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		defer wgf.Done()
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		pos := 0
		for idx := range workc {
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			finalizer(idx)
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n {
					return
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				finalizer(pos)
			}
		}
	}()

	// process all items in the list, with a concurrency of max
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			worker(idx)
			workc <- idx
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
	}

	// done when all go routines are
	wg.Wait()
	close(workc)
	wgf.Wait()

	return
}

// DoWithError spawns workers with index 0 to n-1, limiting their numbers by max.
// Similar to Do but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoWithError(n int, worker, finalizer func(idx int) error) error {
	return DoNWithError(n, worker, finalizer, numRoutines)
}

// DoNWithError spawns workers with index 0 to n-1, limiting their numbers by GOMAXPROCS.
// Similar to DoN but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoNWithError(n int, worker, finalizer func(idx int) error, max int) error {
	switch n {
	case 0:
		return nil
	case 1:
		if err := worker(0); err != nil {
			return err
		}
		if finalizer != nil {
			return finalizer(0)
		}
		return nil
	}

	if finalizer == nil {
		return doWithError(n, worker, max)
	}

	var (
		errv    atomic.Value               // worker/finalizer error
		donec   = make(chan struct{}, max) // worker done channel
		workc   = make(chan int)           // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		// current index to be processed
		pos := 0
		// the finalizer routine exits when the channel is closed
		// or when it has completed all work
		for idx := range workc {
			if errv.Load() != nil {
				// upon error, wait for the workers to complete
				// which will close the channel
				continue
			}
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			if err := finalizer(pos); err != nil {
				errv.Store(err)
				continue
			}
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n || errv.Load() != nil {
					break
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				if err := finalizer(pos); err != nil {
					errv.Store(err)
					break
				}
			}
		}
		wgf.Done()
	}()

	// process all items in the list, with a concurrency of max
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			if errv.Load() == nil {
				if err := worker(idx); err != nil {
					errv.Store(err)
				} else {
					workc <- idx
				}
			}
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
		if errv.Load() != nil {
			break
		}
	}

	// wait for workers
	wg.Wait()
	// since workc is blocking, the finalizer has received all items
	// so we can safely close it and shutdown the finalizer routine
	close(workc)

	// wait for finalizer
	wgf.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<commit_msg>simplified finalizer logic<commit_after>// Package work eases concurrency patterns by providing often used helpers.
package work

import (
	"runtime"
	"sync"
	"sync/atomic"
)

// numRoutines defines the default maximum number of goroutines based on GOMAXPROCS.
var numRoutines int

func init() {
	numRoutines = runtime.GOMAXPROCS(0)
}

// do spawns workers with index 0 to n-1, limiting their numbers by max.
func do(n int, worker func(int), max int) {
	var wg sync.WaitGroup
	if n <= max {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				worker(idx)
				wg.Done()
			}(i)
		}
		wg.Wait()
		return
	}

	// spawn the maximum number of goroutines
	wg.Add(max)
	for i := 0; i < max; i++ {
		go func(idx int) {
			for ; idx < n; idx += max {
				worker(idx)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}

// doWithError spawns workers with index 0 to n-1, limiting their numbers by max.
// Similar to do but with error handling.
// The first error encountered aborts all processing and is then returned.
func doWithError(n int, worker func(int) error, max int) error {
	var (
		errv atomic.Value // worker error
		wg   sync.WaitGroup
	)

	if n <= max {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				if errv.Load() == nil {
					if err := worker(idx); err != nil {
						errv.Store(err)
					}
				}
				wg.Done()
			}(i)
		}
		wg.Wait()

		if err := errv.Load(); err != nil {
			return err.(error)
		}
		return nil
	}

	// spawn the maximum number of goroutines
	wg.Add(max)
	for i := 0; i < max; i++ {
		go func(idx int) {
			for ; idx < n && errv.Load() == nil; idx += max {
				if err := worker(idx); err != nil {
					errv.Store(err)
					break
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}

// Do spawns workers with index 0 to n-1, limiting their numbers by GOMAXPROCS.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func Do(n int, worker, finalizer func(idx int)) {
	DoN(n, worker, finalizer, numRoutines)
}

// DoN spawns workers with index 0 to n-1, limiting their numbers by max.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoN(n int, worker, finalizer func(idx int), max int) {
	switch n {
	case 0:
		return
	case 1:
		worker(0)
		if finalizer != nil {
			finalizer(0)
		}
		return
	}

	if finalizer == nil {
		do(n, worker, max)
		return
	}

	var (
		donec   = make(chan struct{}, max) // worker throttling
		workc   = make(chan int)           // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		defer wgf.Done()
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		pos := 0
		for idx := range workc {
			buffer[idx] = struct{}{}
			// process the results that were already received
			// ensuring they are processed in order
			for ; ; pos++ {
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				finalizer(pos)
			}
		}
	}()

	// process all items in the list, with a concurrency of max
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			worker(idx)
			workc <- idx
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
	}

	// done when all go routines are
	wg.Wait()
	close(workc)
	wgf.Wait()

	return
}

// DoWithError spawns workers with index 0 to n-1, limiting their numbers by max.
// Similar to Do but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoWithError(n int, worker, finalizer func(idx int) error) error {
	return DoNWithError(n, worker, finalizer, numRoutines)
}

// DoNWithError spawns workers with index 0 to n-1, limiting their numbers by GOMAXPROCS.
// Similar to DoN but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoNWithError(n int, worker, finalizer func(idx int) error, max int) error {
	switch n {
	case 0:
		return nil
	case 1:
		if err := worker(0); err != nil {
			return err
		}
		if finalizer != nil {
			return finalizer(0)
		}
		return nil
	}

	if finalizer == nil {
		return doWithError(n, worker, max)
	}

	var (
		errv    atomic.Value               // worker/finalizer error
		donec   = make(chan struct{}, max) // worker done channel
		workc   = make(chan int)           // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		// current index to be processed
		pos := 0
		// the finalizer routine exits when the channel is closed
		// or when it has completed all work
		for idx := range workc {
			buffer[idx] = struct{}{}
			// process the results that were already received
			// ensuring they are processed in order
			for ; errv.Load() == nil; pos++ {
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				if err := finalizer(pos); err != nil {
					errv.Store(err)
					break
				}
			}
		}
		wgf.Done()
	}()

	// process all items in the list, with a concurrency of max
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			if errv.Load() == nil {
				if err := worker(idx); err != nil {
					errv.Store(err)
				} else {
					workc <- idx
				}
			}
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
		if errv.Load() != nil {
			break
		}
	}

	// wait for workers
	wg.Wait()
	// since workc is blocking, the finalizer has received all items
	// so we can safely close it and shutdown the finalizer routine
	close(workc)

	// wait for finalizer
	wgf.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<|endoftext|>


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

package pkcs7

// These are private constants and functions from the crypto/x509 package that
// are useful when dealing with signatures verified by x509 certificates

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
)

var (
	oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
	oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
	oidSignatureRSAPSS          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}

	oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
	oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
	oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}

	oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}

	// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
	// but it's specified by ISO. Microsoft's makecert.exe has been known
	// to produce certificates with this OID.
	oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
)

var signatureAlgorithmDetails = []struct {
	algo       x509.SignatureAlgorithm
	name       string
	oid        asn1.ObjectIdentifier
	pubKeyAlgo x509.PublicKeyAlgorithm
	hash       crypto.Hash
}{
	{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
	{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
	{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
	{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
	{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
	{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
	{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
	{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
	{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
	{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
	{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
	{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
	{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
	{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
	{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
	{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
}

// pssParameters reflects the parameters in an AlgorithmIdentifier that
// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
type pssParameters struct {
	// The following three fields are not marked as
	// optional because the default values specify SHA-1,
	// which is no longer suitable for use in signatures.
	Hash         pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
	MGF          pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
	SaltLength   int                      `asn1:"explicit,tag:2"`
	TrailerField int                      `asn1:"optional,explicit,tag:3,default:1"`
}

// asn1.NullBytes is not available prior to Go 1.9
var nullBytes = []byte{asn1.TagNull, 0}

func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
	if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
		for _, details := range signatureAlgorithmDetails {
			if ai.Algorithm.Equal(details.oid) {
				return details.algo
			}
		}
		return x509.UnknownSignatureAlgorithm
	}

	// RSA PSS is special because it encodes important parameters
	// in the Parameters.

	var params pssParameters
	if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, &params); err != nil {
		return x509.UnknownSignatureAlgorithm
	}

	var mgf1HashFunc pkix.AlgorithmIdentifier
	if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
		return x509.UnknownSignatureAlgorithm
	}

	// PSS is greatly overburdened with options. This code forces
	// them into three buckets by requiring that the MGF1 hash
	// function always match the message hash function (as
	// recommended in
	// https://tools.ietf.org/html/rfc3447#section-8.1), that the
	// salt length matches the hash length, and that the trailer
	// field has the default value.
	if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) ||
		!params.MGF.Algorithm.Equal(oidMGF1) ||
		!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
		!bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) ||
		params.TrailerField != 1 {
		return x509.UnknownSignatureAlgorithm
	}

	switch {
	case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
		return x509.SHA256WithRSAPSS
	case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
		return x509.SHA384WithRSAPSS
	case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
		return x509.SHA512WithRSAPSS
	}

	return x509.UnknownSignatureAlgorithm
}
<commit_msg>Don't use asn1.TagNull, it was also introduced in Go 1.9<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the go/golang LICENSE file.

package pkcs7

// These are private constants and functions from the crypto/x509 package that
// are useful when dealing with signatures verified by x509 certificates

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
)

var (
	oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
	oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
	oidSignatureRSAPSS          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}

	oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
	oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
	oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}

	oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}

	// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
	// but it's specified by ISO. Microsoft's makecert.exe has been known
	// to produce certificates with this OID.
	oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
)

var signatureAlgorithmDetails = []struct {
	algo       x509.SignatureAlgorithm
	name       string
	oid        asn1.ObjectIdentifier
	pubKeyAlgo x509.PublicKeyAlgorithm
	hash       crypto.Hash
}{
	{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
	{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
	{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
	{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
	{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
	{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
	{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
	{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
	{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
	{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
	{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
	{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
	{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
	{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
	{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
	{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
}

// pssParameters reflects the parameters in an AlgorithmIdentifier that
// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
type pssParameters struct {
	// The following three fields are not marked as
	// optional because the default values specify SHA-1,
	// which is no longer suitable for use in signatures.
	Hash         pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
	MGF          pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
	SaltLength   int                      `asn1:"explicit,tag:2"`
	TrailerField int                      `asn1:"optional,explicit,tag:3,default:1"`
}

// asn1.NullBytes is not available prior to Go 1.9
var nullBytes = []byte{5, 0}

func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
	if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
		for _, details := range signatureAlgorithmDetails {
			if ai.Algorithm.Equal(details.oid) {
				return details.algo
			}
		}
		return x509.UnknownSignatureAlgorithm
	}

	// RSA PSS is special because it encodes important parameters
	// in the Parameters.

	var params pssParameters
	if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, &params); err != nil {
		return x509.UnknownSignatureAlgorithm
	}

	var mgf1HashFunc pkix.AlgorithmIdentifier
	if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
		return x509.UnknownSignatureAlgorithm
	}

	// PSS is greatly overburdened with options. This code forces
	// them into three buckets by requiring that the MGF1 hash
	// function always match the message hash function (as
	// recommended in
	// https://tools.ietf.org/html/rfc3447#section-8.1), that the
	// salt length matches the hash length, and that the trailer
	// field has the default value.
	if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) ||
		!params.MGF.Algorithm.Equal(oidMGF1) ||
		!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
		!bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) ||
		params.TrailerField != 1 {
		return x509.UnknownSignatureAlgorithm
	}

	switch {
	case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
		return x509.SHA256WithRSAPSS
	case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
		return x509.SHA384WithRSAPSS
	case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
		return x509.SHA512WithRSAPSS
	}

	return x509.UnknownSignatureAlgorithm
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"regexp"
	"strings"
	"time"

	"github.com/kteza1/ninjasphere-limitlessled/core"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/model"
)

/*LimitlessLedBridge Struct for info about bridge.*/
type LimitlessLedBridge struct {
	*net.UDPConn
}

type LimitlessLedZone struct {
	driver            ninja.Driver
	info              *model.Device
	sendEvent         func(event string, payload interface{}) error
	onOffChannel      *channels.OnOffChannel
	brightnessChannel *channels.BrightnessChannel
	colorChannel      *channels.ColorChannel
	zoneNumber        int
	bridge            *LimitlessLedBridge
}

func newLimitlessLedZone(driver ninja.Driver, id int) *LimitlessLedZone {
	name := fmt.Sprintf("MiLight-Z%d", id)
	zone := &LimitlessLedZone{
		driver: driver,
		info: &model.Device{
			NaturalID:     fmt.Sprintf("light%d", id),
			NaturalIDType: "limitlessled zone",
			Name:          &name,
			Signatures: &map[string]string{
				"ninja:manufacturer": "LimitlessLed",
				"ninja:productName":  "LimitlessLed",
				"ninja:productType":  "Light",
				"ninja:thingType":    "light",
			},
		},
	}
	zone.onOffChannel = channels.NewOnOffChannel(zone)
	zone.brightnessChannel = channels.NewBrightnessChannel(zone)
	zone.colorChannel = channels.NewColorChannel(zone)
	return zone
}

/*SetOnOff -->*/
func (l *LimitlessLedZone) SetOnOff(state bool) error {
	zone := l.zoneNumber
	switch zone {
	case 1:
		if state == true {
			fmt.Println("Switch on zone1")
			l.bridge.SendCommand(core.ZONE1_ON)
		} else {
			fmt.Println("Switching off zone1")
			l.bridge.SendCommand(core.ZONE1_OFF)
		}
	case 2:
		if state == true {
			fmt.Println("Switch on zone2")
			l.bridge.SendCommand(core.ZONE2_ON)
		} else {
			fmt.Println("Switching off zone2")
			l.bridge.SendCommand(core.ZONE2_OFF)
		}
	case 3:
		if state == true {
			fmt.Println("Switch on zone3")
			l.bridge.SendCommand(core.ZONE3_ON)
		} else {
			fmt.Println("Switching off zone3")
			l.bridge.SendCommand(core.ZONE3_OFF)
		}
	case 4:
		if state == true {
			fmt.Println("Switch on zone4")
			l.bridge.SendCommand(core.ZONE4_ON)
		} else {
			fmt.Println("Switching off zone4")
			l.bridge.SendCommand(core.ZONE4_OFF)
		}
	}
	return nil
}

//ToggleOnOff -->
func (l *LimitlessLedBridge) ToggleOnOff() error {
	log.Println("Toggling")
	return nil
}

//ToggleOnOff -->
func (l *LimitlessLedZone) ToggleOnOff() error {
	log.Println("Toggling")
	return nil
}

func (l *LimitlessLedZone) SetColor(state *channels.ColorState) error {
	fmt.Println("setting color state to %v", state)
	return nil
}

func (l *LimitlessLedZone) SetBrightness(state float64) error {
	fmt.Println("setting brightness to %f", state)
	return nil
}

func (l *LimitlessLedZone) GetDeviceInfo() *model.Device {
	return l.info
}

func (l *LimitlessLedZone) GetDriver() ninja.Driver {
	return l.driver
}

//SetEventHandler -->
func (l *LimitlessLedZone) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	l.sendEvent = sendEvent
}

var reg, _ = regexp.Compile("[^a-z0-9]")

//SetName --> Exported by service/device schema
func (l *LimitlessLedZone) SetName(name *string) (*string, error) {
	log.Printf("Setting device name to %s", *name)
	safe := reg.ReplaceAllString(strings.ToLower(*name), "")
	if len(safe) > 16 {
		safe = safe[0:16]
	}
	log.Printf("Pretending we can only set 5 lowercase alphanum. Name now: %s", safe)
	l.sendEvent("renamed", safe)
	return &safe, nil
}

func Dial(host string) (*LimitlessLedBridge, error) {
	addr, err := net.ResolveUDPAddr("udp4", host)

	if err != nil {
		fmt.Println("Error Resolving")
		return nil, err
	}

	s, err := net.DialUDP("udp4", nil, addr)
	if err != nil {
		fmt.Println("Error dialing")
		return nil, err
	}
	return &LimitlessLedBridge{s}, err
}

func (bridge *LimitlessLedBridge) SendCommand(command []byte) {
	fmt.Println("Sending command")
	/* Sending each command twice since bridge can get slow sometimes */
	for i := 0; i < 2; i++ {
		_, err := bridge.Write(command)
		if err != nil {
			fmt.Println("Error writing")
			return
		}
		time.Sleep(time.Millisecond * 50)
	}
	time.Sleep(time.Millisecond * 100)
}
<commit_msg>removing unnecessary code<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"regexp"
	"strings"
	"time"

	"github.com/kteza1/ninjasphere-limitlessled/core"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/model"
)

/*LimitlessLedBridge Struct for info about bridge.*/
type LimitlessLedBridge struct {
	*net.UDPConn
}

type LimitlessLedZone struct {
	driver            ninja.Driver
	info              *model.Device
	sendEvent         func(event string, payload interface{}) error
	onOffChannel      *channels.OnOffChannel
	brightnessChannel *channels.BrightnessChannel
	colorChannel      *channels.ColorChannel
	zoneNumber        int
	bridge            *LimitlessLedBridge
}

func newLimitlessLedZone(driver ninja.Driver, id int) *LimitlessLedZone {
	name := fmt.Sprintf("MiLight-Z%d", id)
	zone := &LimitlessLedZone{
		driver: driver,
		info: &model.Device{
			NaturalID:     fmt.Sprintf("light%d", id),
			NaturalIDType: "limitlessled zone",
			Name:          &name,
			Signatures: &map[string]string{
				"ninja:manufacturer": "LimitlessLed",
				"ninja:productName":  "LimitlessLed",
				"ninja:productType":  "Light",
				"ninja:thingType":    "light",
			},
		},
	}
	zone.onOffChannel = channels.NewOnOffChannel(zone)
	zone.brightnessChannel = channels.NewBrightnessChannel(zone)
	zone.colorChannel = channels.NewColorChannel(zone)
	return zone
}

/*SetOnOff -->*/
func (l *LimitlessLedZone) SetOnOff(state bool) error {
	zone := l.zoneNumber
	switch zone {
	case 1:
		if state == true {
			fmt.Println("Switch on zone1")
			l.bridge.SendCommand(core.ZONE1_ON)
		} else {
			fmt.Println("Switching off zone1")
			l.bridge.SendCommand(core.ZONE1_OFF)
		}
	case 2:
		if state == true {
			fmt.Println("Switch on zone2")
			l.bridge.SendCommand(core.ZONE2_ON)
		} else {
			fmt.Println("Switching off zone2")
			l.bridge.SendCommand(core.ZONE2_OFF)
		}
	case 3:
		if state == true {
			fmt.Println("Switch on zone3")
			l.bridge.SendCommand(core.ZONE3_ON)
		} else {
			fmt.Println("Switching off zone3")
			l.bridge.SendCommand(core.ZONE3_OFF)
		}
	case 4:
		if state == true {
			fmt.Println("Switch on zone4")
			l.bridge.SendCommand(core.ZONE4_ON)
		} else {
			fmt.Println("Switching off zone4")
			l.bridge.SendCommand(core.ZONE4_OFF)
		}
	}
	return nil
}

//ToggleOnOff -->
func (l *LimitlessLedZone) ToggleOnOff() error {
	log.Println("Toggling")
	return nil
}

func (l *LimitlessLedZone) SetColor(state *channels.ColorState) error {
	fmt.Println("setting color state to %v", state)
	return nil
}

func (l *LimitlessLedZone) SetBrightness(state float64) error {
	fmt.Println("setting brightness to %f", state)
	return nil
}

func (l *LimitlessLedZone) GetDeviceInfo() *model.Device {
	return l.info
}

func (l *LimitlessLedZone) GetDriver() ninja.Driver {
	return l.driver
}

//SetEventHandler -->
func (l *LimitlessLedZone) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	l.sendEvent = sendEvent
}

var reg, _ = regexp.Compile("[^a-z0-9]")

//SetName --> Exported by service/device schema
func (l *LimitlessLedZone) SetName(name *string) (*string, error) {
	log.Printf("Setting device name to %s", *name)
	safe := reg.ReplaceAllString(strings.ToLower(*name), "")
	if len(safe) > 16 {
		safe = safe[0:16]
	}
	log.Printf("Pretending we can only set 5 lowercase alphanum. Name now: %s", safe)
	l.sendEvent("renamed", safe)
	return &safe, nil
}

func Dial(host string) (*LimitlessLedBridge, error) {
	addr, err := net.ResolveUDPAddr("udp4", host)

	if err != nil {
		fmt.Println("Error Resolving")
		return nil, err
	}

	s, err := net.DialUDP("udp4", nil, addr)
	if err != nil {
		fmt.Println("Error dialing")
		return nil, err
	}
	return &LimitlessLedBridge{s}, err
}

func (bridge *LimitlessLedBridge) SendCommand(command []byte) {
	fmt.Println("Sending command")
	/* Sending each command twice since bridge can get slow sometimes */
	for i := 0; i < 2; i++ {
		_, err := bridge.Write(command)
		if err != nil {
			fmt.Println("Error writing")
			return
		}
		time.Sleep(time.Millisecond * 50)
	}
	time.Sleep(time.Millisecond * 100)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/flynn/flynn/logaggregator/client"
	"github.com/flynn/flynn/pkg/ctxhelper"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/syslog/rfc5424"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/flynn/flynn/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
)

func apiHandler(agg *Aggregator) http.Handler {
	api := aggregatorAPI{agg: agg}
	r := httprouter.New()

	r.GET("/log/:channel_id", httphelper.WrapHandler(api.GetLog))
	return httphelper.ContextInjector(
		"logaggregator-api",
		httphelper.NewRequestLogger(r),
	)
}

type aggregatorAPI struct {
	agg *Aggregator
}

func (a *aggregatorAPI) GetLog(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	ctx, cancel := context.WithCancel(ctx)
	if cn, ok := w.(http.CloseNotifier); ok {
		go func() {
			select {
			case <-cn.CloseNotify():
				cancel()
			case <-ctx.Done():
			}
		}()
	}
	defer cancel()

	params, _ := ctxhelper.ParamsFromContext(ctx)
	channelID := params.ByName("channel_id")

	follow := false
	if strFollow := req.FormValue("follow"); strFollow == "true" {
		follow = true
	}

	lines := -1 // default to all lines
	if strLines := req.FormValue("lines"); strLines != "" {
		var err error
		lines, err = strconv.Atoi(strLines)
		if err != nil || lines < 0 || lines > 10000 {
			httphelper.ValidationError(w, "lines", "lines must be an integer between 0 and 10000")
			return
		}
	}

	filters := make(filterSlice, 0)
	if jobID := req.FormValue("job_id"); jobID != "" {
		filters = append(filters, filterJobID(jobID))
	}
	if processTypeVals, ok := req.Form["process_type"]; ok && len(processTypeVals) > 0 {
		val := processTypeVals[len(processTypeVals)-1]
		filters = append(filters, filterProcessType(val))
	}

	w.WriteHeader(200)

	var msgc <-chan *rfc5424.Message
	if follow {
		msgc = a.agg.ReadLastNAndSubscribe(channelID, lines, filters, ctx.Done())
	} else {
		msgc = a.agg.ReadLastN(channelID, lines, filters, ctx.Done())
	}
	writeMessages(ctx, w, msgc)
}

func writeMessages(ctx context.Context, w http.ResponseWriter, msgc <-chan *rfc5424.Message) {
	ticker := time.NewTicker(50 * time.Millisecond)
	defer ticker.Stop()

	enc := json.NewEncoder(w)
	for {
		select {
		case syslogMsg, ok := <-msgc:
			if !ok { // channel is closed / done
				return
			}
			if err := enc.Encode(NewMessageFromSyslog(syslogMsg)); err != nil {
				log15.Error("error writing msg", "err", err)
				return
			}
		case <-ticker.C:
			w.(http.Flusher).Flush()
		case <-ctx.Done():
			return
		}
	}
}

func NewMessageFromSyslog(m *rfc5424.Message) client.Message {
	processType, jobID := splitProcID(m.ProcID)
	return client.Message{
		HostID:      string(m.Hostname),
		JobID:       string(jobID),
		Msg:         string(m.Msg),
		ProcessType: string(processType),
		// TODO(bgentry): source is always "app" for now, could be router in future
		Source:    "app",
		Stream:    streamFromMessage(m),
		Timestamp: m.Timestamp,
	}
}

var procIDsep = []byte{'.'}

func splitProcID(procID []byte) (processType, jobID []byte) {
	split := bytes.Split(procID, procIDsep)
	if len(split) > 0 {
		processType = split[0]
	}
	if len(split) > 1 {
		jobID = split[1]
	}
	return
}

func streamFromMessage(m *rfc5424.Message) string {
	switch string(m.MsgID) {
	case "ID1":
		return "stdout"
	case "ID2":
		return "stderr"
	default:
		return "unknown"
	}
}
<commit_msg>logaggregator: Add health status endpoint<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/flynn/flynn/logaggregator/client"
	"github.com/flynn/flynn/pkg/ctxhelper"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/status"
	"github.com/flynn/flynn/pkg/syslog/rfc5424"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/flynn/flynn/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
)

func apiHandler(agg *Aggregator) http.Handler {
	api := aggregatorAPI{agg: agg}
	r := httprouter.New()

	r.Handler("GET", status.Path, status.HealthyHandler)
	r.GET("/log/:channel_id", httphelper.WrapHandler(api.GetLog))
	return httphelper.ContextInjector(
		"logaggregator-api",
		httphelper.NewRequestLogger(r),
	)
}

type aggregatorAPI struct {
	agg *Aggregator
}

func (a *aggregatorAPI) GetLog(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	ctx, cancel := context.WithCancel(ctx)
	if cn, ok := w.(http.CloseNotifier); ok {
		go func() {
			select {
			case <-cn.CloseNotify():
				cancel()
			case <-ctx.Done():
			}
		}()
	}
	defer cancel()

	params, _ := ctxhelper.ParamsFromContext(ctx)
	channelID := params.ByName("channel_id")

	follow := false
	if strFollow := req.FormValue("follow"); strFollow == "true" {
		follow = true
	}

	lines := -1 // default to all lines
	if strLines := req.FormValue("lines"); strLines != "" {
		var err error
		lines, err = strconv.Atoi(strLines)
		if err != nil || lines < 0 || lines > 10000 {
			httphelper.ValidationError(w, "lines", "lines must be an integer between 0 and 10000")
			return
		}
	}

	filters := make(filterSlice, 0)
	if jobID := req.FormValue("job_id"); jobID != "" {
		filters = append(filters, filterJobID(jobID))
	}
	if processTypeVals, ok := req.Form["process_type"]; ok && len(processTypeVals) > 0 {
		val := processTypeVals[len(processTypeVals)-1]
		filters = append(filters, filterProcessType(val))
	}

	w.WriteHeader(200)

	var msgc <-chan *rfc5424.Message
	if follow {
		msgc = a.agg.ReadLastNAndSubscribe(channelID, lines, filters, ctx.Done())
	} else {
		msgc = a.agg.ReadLastN(channelID, lines, filters, ctx.Done())
	}
	writeMessages(ctx, w, msgc)
}

func writeMessages(ctx context.Context, w http.ResponseWriter, msgc <-chan *rfc5424.Message) {
	ticker := time.NewTicker(50 * time.Millisecond)
	defer ticker.Stop()

	enc := json.NewEncoder(w)
	for {
		select {
		case syslogMsg, ok := <-msgc:
			if !ok { // channel is closed / done
				return
			}
			if err := enc.Encode(NewMessageFromSyslog(syslogMsg)); err != nil {
				log15.Error("error writing msg", "err", err)
				return
			}
		case <-ticker.C:
			w.(http.Flusher).Flush()
		case <-ctx.Done():
			return
		}
	}
}

func NewMessageFromSyslog(m *rfc5424.Message) client.Message {
	processType, jobID := splitProcID(m.ProcID)
	return client.Message{
		HostID:      string(m.Hostname),
		JobID:       string(jobID),
		Msg:         string(m.Msg),
		ProcessType: string(processType),
		// TODO(bgentry): source is always "app" for now, could be router in future
		Source:    "app",
		Stream:    streamFromMessage(m),
		Timestamp: m.Timestamp,
	}
}

var procIDsep = []byte{'.'}

func splitProcID(procID []byte) (processType, jobID []byte) {
	split := bytes.Split(procID, procIDsep)
	if len(split) > 0 {
		processType = split[0]
	}
	if len(split) > 1 {
		jobID = split[1]
	}
	return
}

func streamFromMessage(m *rfc5424.Message) string {
	switch string(m.MsgID) {
	case "ID1":
		return "stdout"
	case "ID2":
		return "stderr"
	default:
		return "unknown"
	}
}
<|endoftext|>