<commit_before>package descriptor

import (
	"fmt"
	"strings"

	"github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/httprule"
	descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
	gogen "github.com/golang/protobuf/protoc-gen-go/generator"
)

// GoPackage represents a golang package
type GoPackage struct {
	// Path is the package path to the package.
	Path string
	// Name is the package name of the package
	Name string
	// Alias is an alias of the package unique within the current invokation of grpc-gateway generator.
	Alias string
}

// Standard returns whether the import is a golang standard pacakge.
func (p GoPackage) Standard() bool {
	return !strings.Contains(p.Path, ".")
}

// String returns a string representation of this package in the form of import line in golang.
func (p GoPackage) String() string {
	if p.Alias == "" {
		return fmt.Sprintf("%q", p.Path)
	}
	return fmt.Sprintf("%s %q", p.Alias, p.Path)
}

// File wraps descriptor.FileDescriptorProto for richer features.
type File struct {
	*descriptor.FileDescriptorProto
	// GoPkg is the go package of the go file generated from this file..
	GoPkg GoPackage
	// Messages is the list of messages defined in this file.
	Messages []*Message
	// Enums is the list of enums defined in this file.
	Enums []*Enum
	// Services is the list of services defined in this file.
	Services []*Service
}

// proto2 determines if the syntax of the file is proto2.
func (f *File) proto2() bool {
	return f.Syntax == nil || f.GetSyntax() == "proto2"
}

// Message describes a protocol buffer message types
type Message struct {
	// File is the file where the message is defined
	File *File
	// Outers is a list of outer messages if this message is a nested type.
	Outers []string
	*descriptor.DescriptorProto
	Fields []*Field
}

// FQMN returns a fully qualified message name of this message.
func (m *Message) FQMN() string {
	components := []string{""}
	if m.File.Package != nil {
		components = append(components, m.File.GetPackage())
	}
	components = append(components, m.Outers...)
	components = append(components, m.GetName())
	return strings.Join(components, ".")
}

// GoType returns a go type name for the message type.
// It prefixes the type name with the package alias if
// its belonging package is not "currentPackage".
func (m *Message) GoType(currentPackage string) string {
	var components []string
	components = append(components, m.Outers...)
	components = append(components, m.GetName())

	name := strings.Join(components, "_")
	if m.File.GoPkg.Path == currentPackage {
		return name
	}
	pkg := m.File.GoPkg.Name
	if alias := m.File.GoPkg.Alias; alias != "" {
		pkg = alias
	}
	return fmt.Sprintf("%s.%s", pkg, name)
}

// Enum describes a protocol buffer enum types
type Enum struct {
	// File is the file where the enum is defined
	File *File
	// Outers is a list of outer messages if this enum is a nested type.
	Outers []string
	*descriptor.EnumDescriptorProto
}

// FQEN returns a fully qualified enum name of this enum.
func (e *Enum) FQEN() string {
	components := []string{""}
	if e.File.Package != nil {
		components = append(components, e.File.GetPackage())
	}
	components = append(components, e.Outers...)
	components = append(components, e.GetName())
	return strings.Join(components, ".")
}

// Service wraps descriptor.ServiceDescriptorProto for richer features.
type Service struct {
	// File is the file where this service is defined.
	File *File
	*descriptor.ServiceDescriptorProto
	// Methods is the list of methods defined in this service.
	Methods []*Method
}

// Method wraps descriptor.MethodDescriptorProto for richer features.
type Method struct {
	// Service is the service which this method belongs to.
	Service *Service
	*descriptor.MethodDescriptorProto

	// RequestType is the message type of requests to this method.
	RequestType *Message
	// ResponseType is the message type of responses from this method.
	ResponseType *Message
	Bindings     []*Binding
}

// Binding describes how an HTTP endpoint is bound to a gRPC method.
type Binding struct {
	// Method is the method which the endpoint is bound to.
	Method *Method
	// Index is a zero-origin index of the binding in the target method
	Index int
	// PathTmpl is path template where this method is mapped to.
	PathTmpl httprule.Template
	// HTTPMethod is the HTTP method which this method is mapped to.
	HTTPMethod string
	// PathParams is the list of parameters provided in HTTP request paths.
	PathParams []Parameter
	// Body describes parameters provided in HTTP request body.
	Body *Body
}

// ExplicitParams returns a list of explicitly bound parameters of "b",
// i.e. a union of field path for body and field paths for path parameters.
func (b *Binding) ExplicitParams() []string {
	var result []string
	if b.Body != nil {
		result = append(result, b.Body.FieldPath.String())
	}
	for _, p := range b.PathParams {
		result = append(result, p.FieldPath.String())
	}
	return result
}

// Field wraps descriptor.FieldDescriptorProto for richer features.
type Field struct {
	// Message is the message type which this field belongs to.
	Message *Message
	// FieldMessage is the message type of the field.
	FieldMessage *Message
	*descriptor.FieldDescriptorProto
}

// Parameter is a parameter provided in http requests
type Parameter struct {
	// FieldPath is a path to a proto field which this parameter is mapped to.
	FieldPath
	// Target is the proto field which this parameter is mapped to.
	Target *Field
	// Method is the method which this parameter is used for.
	Method *Method
}

// ConvertFuncExpr returns a go expression of a converter function.
// The converter function converts a string into a value for the parameter.
func (p Parameter) ConvertFuncExpr() (string, error) {
	tbl := proto3ConvertFuncs
	if p.Target.Message.File.proto2() {
		tbl = proto2ConvertFuncs
	}
	typ := p.Target.GetType()
	conv, ok := tbl[typ]
	if !ok {
		return "", fmt.Errorf("unsupported field type %s of parameter %s in %s.%s", typ, p.FieldPath, p.Method.Service.GetName(), p.Method.GetName())
	}
	return conv, nil
}

// Body describes a http requtest body to be sent to the method.
type Body struct {
	// FieldPath is a path to a proto field which the request body is mapped to.
	// The request body is mapped to the request type itself if FieldPath is empty.
	FieldPath FieldPath
}

// RHS returns a right-hand-side expression in go to be used to initialize method request object.
// It starts with "msgExpr", which is the go expression of the method request object.
func (b Body) RHS(msgExpr string) string {
	return b.FieldPath.RHS(msgExpr)
}

// FieldPath is a path to a field from a request message.
type FieldPath []FieldPathComponent

// String returns a string representation of the field path.
func (p FieldPath) String() string {
	var components []string
	for _, c := range p {
		components = append(components, c.Name)
	}
	return strings.Join(components, ".")
}

// IsNestedProto3 indicates whether the FieldPath is a nested Proto3 path.
func (p FieldPath) IsNestedProto3() bool {
	if len(p) > 1 && !p[0].Target.Message.File.proto2() {
		return true
	}
	return false
}

// RHS is a right-hand-side expression in go to be used to assign a value to the target field.
// It starts with "msgExpr", which is the go expression of the method request object.
func (p FieldPath) RHS(msgExpr string) string {
	l := len(p)
	if l == 0 {
		return msgExpr
	}
	components := []string{msgExpr}
	for i, c := range p {
		if i == l-1 {
			components = append(components, c.RHS())
			continue
		}
		components = append(components, c.LHS())
	}
	return strings.Join(components, ".")
}

// FieldPathComponent is a path component in FieldPath
type FieldPathComponent struct {
	// Name is a name of the proto field which this component corresponds to.
	// TODO(yugui) is this necessary?
	Name string
	// Target is the proto field which this component corresponds to.
	Target *Field
}

// RHS returns a right-hand-side expression in go for this field.
func (c FieldPathComponent) RHS() string {
	return gogen.CamelCase(c.Name)
}

// LHS returns a left-hand-side expression in go for this field.
func (c FieldPathComponent) LHS() string {
	if c.Target.Message.File.proto2() {
		return fmt.Sprintf("Get%s()", gogen.CamelCase(c.Name))
	}
	return gogen.CamelCase(c.Name)
}

var (
	proto3ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
		descriptor.FieldDescriptorProto_TYPE_DOUBLE:  "runtime.Float64",
		descriptor.FieldDescriptorProto_TYPE_FLOAT:   "runtime.Float32",
		descriptor.FieldDescriptorProto_TYPE_INT64:   "runtime.Int64",
		descriptor.FieldDescriptorProto_TYPE_UINT64:  "runtime.Uint64",
		descriptor.FieldDescriptorProto_TYPE_INT32:   "runtime.Int32",
		descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64",
		descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32",
		descriptor.FieldDescriptorProto_TYPE_BOOL:    "runtime.Bool",
		descriptor.FieldDescriptorProto_TYPE_STRING:  "runtime.String",
		// FieldDescriptorProto_TYPE_GROUP
		// FieldDescriptorProto_TYPE_MESSAGE
		// FieldDescriptorProto_TYPE_BYTES
		// TODO(yugui) Handle bytes
		descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32",
		// FieldDescriptorProto_TYPE_ENUM
		// TODO(yugui) Handle Enum
		descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32",
		descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64",
		descriptor.FieldDescriptorProto_TYPE_SINT32:   "runtime.Int32",
		descriptor.FieldDescriptorProto_TYPE_SINT64:   "runtime.Int64",
	}

	proto2ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
		descriptor.FieldDescriptorProto_TYPE_DOUBLE:  "runtime.Float64P",
		descriptor.FieldDescriptorProto_TYPE_FLOAT:   "runtime.Float32P",
		descriptor.FieldDescriptorProto_TYPE_INT64:   "runtime.Int64P",
		descriptor.FieldDescriptorProto_TYPE_UINT64:  "runtime.Uint64P",
		descriptor.FieldDescriptorProto_TYPE_INT32:   "runtime.Int32P",
		descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64P",
		descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32P",
		descriptor.FieldDescriptorProto_TYPE_BOOL:    "runtime.BoolP",
		descriptor.FieldDescriptorProto_TYPE_STRING:  "runtime.StringP",
		// FieldDescriptorProto_TYPE_GROUP
		// FieldDescriptorProto_TYPE_MESSAGE
		// FieldDescriptorProto_TYPE_BYTES
		// TODO(yugui) Handle bytes
		descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32P",
		// FieldDescriptorProto_TYPE_ENUM
		// TODO(yugui) Handle Enum
		descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32P",
		descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64P",
		descriptor.FieldDescriptorProto_TYPE_SINT32:   "runtime.Int32P",
		descriptor.FieldDescriptorProto_TYPE_SINT64:   "runtime.Int64P",
	}
)
<commit_msg>fix typo<commit_after>package descriptor

import (
	"fmt"
	"strings"

	"github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/httprule"
	descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
	gogen "github.com/golang/protobuf/protoc-gen-go/generator"
)

// GoPackage represents a golang package
type GoPackage struct {
	// Path is the package path to the package.
	Path string
	// Name is the package name of the package
	Name string
	// Alias is an alias of the package unique within the current invokation of grpc-gateway generator.
	Alias string
}

// Standard returns whether the import is a golang standard package.
func (p GoPackage) Standard() bool {
	return !strings.Contains(p.Path, ".")
}

// String returns a string representation of this package in the form of import line in golang.
func (p GoPackage) String() string {
	if p.Alias == "" {
		return fmt.Sprintf("%q", p.Path)
	}
	return fmt.Sprintf("%s %q", p.Alias, p.Path)
}

// File wraps descriptor.FileDescriptorProto for richer features.
type File struct {
	*descriptor.FileDescriptorProto
	// GoPkg is the go package of the go file generated from this file..
	GoPkg GoPackage
	// Messages is the list of messages defined in this file.
	Messages []*Message
	// Enums is the list of enums defined in this file.
	Enums []*Enum
	// Services is the list of services defined in this file.
	Services []*Service
}

// proto2 determines if the syntax of the file is proto2.
func (f *File) proto2() bool {
	return f.Syntax == nil || f.GetSyntax() == "proto2"
}

// Message describes a protocol buffer message types
type Message struct {
	// File is the file where the message is defined
	File *File
	// Outers is a list of outer messages if this message is a nested type.
	Outers []string
	*descriptor.DescriptorProto
	Fields []*Field
}

// FQMN returns a fully qualified message name of this message.
func (m *Message) FQMN() string {
	components := []string{""}
	if m.File.Package != nil {
		components = append(components, m.File.GetPackage())
	}
	components = append(components, m.Outers...)
	components = append(components, m.GetName())
	return strings.Join(components, ".")
}

// GoType returns a go type name for the message type.
// It prefixes the type name with the package alias if
// its belonging package is not "currentPackage".
func (m *Message) GoType(currentPackage string) string {
	var components []string
	components = append(components, m.Outers...)
	components = append(components, m.GetName())

	name := strings.Join(components, "_")
	if m.File.GoPkg.Path == currentPackage {
		return name
	}
	pkg := m.File.GoPkg.Name
	if alias := m.File.GoPkg.Alias; alias != "" {
		pkg = alias
	}
	return fmt.Sprintf("%s.%s", pkg, name)
}

// Enum describes a protocol buffer enum types
type Enum struct {
	// File is the file where the enum is defined
	File *File
	// Outers is a list of outer messages if this enum is a nested type.
	Outers []string
	*descriptor.EnumDescriptorProto
}

// FQEN returns a fully qualified enum name of this enum.
func (e *Enum) FQEN() string {
	components := []string{""}
	if e.File.Package != nil {
		components = append(components, e.File.GetPackage())
	}
	components = append(components, e.Outers...)
	components = append(components, e.GetName())
	return strings.Join(components, ".")
}

// Service wraps descriptor.ServiceDescriptorProto for richer features.
type Service struct {
	// File is the file where this service is defined.
	File *File
	*descriptor.ServiceDescriptorProto
	// Methods is the list of methods defined in this service.
	Methods []*Method
}

// Method wraps descriptor.MethodDescriptorProto for richer features.
type Method struct {
	// Service is the service which this method belongs to.
	Service *Service
	*descriptor.MethodDescriptorProto

	// RequestType is the message type of requests to this method.
	RequestType *Message
	// ResponseType is the message type of responses from this method.
	ResponseType *Message
	Bindings     []*Binding
}

// Binding describes how an HTTP endpoint is bound to a gRPC method.
type Binding struct {
	// Method is the method which the endpoint is bound to.
	Method *Method
	// Index is a zero-origin index of the binding in the target method
	Index int
	// PathTmpl is path template where this method is mapped to.
	PathTmpl httprule.Template
	// HTTPMethod is the HTTP method which this method is mapped to.
	HTTPMethod string
	// PathParams is the list of parameters provided in HTTP request paths.
	PathParams []Parameter
	// Body describes parameters provided in HTTP request body.
	Body *Body
}

// ExplicitParams returns a list of explicitly bound parameters of "b",
// i.e. a union of field path for body and field paths for path parameters.
func (b *Binding) ExplicitParams() []string {
	var result []string
	if b.Body != nil {
		result = append(result, b.Body.FieldPath.String())
	}
	for _, p := range b.PathParams {
		result = append(result, p.FieldPath.String())
	}
	return result
}

// Field wraps descriptor.FieldDescriptorProto for richer features.
type Field struct {
	// Message is the message type which this field belongs to.
	Message *Message
	// FieldMessage is the message type of the field.
	FieldMessage *Message
	*descriptor.FieldDescriptorProto
}

// Parameter is a parameter provided in http requests
type Parameter struct {
	// FieldPath is a path to a proto field which this parameter is mapped to.
	FieldPath
	// Target is the proto field which this parameter is mapped to.
	Target *Field
	// Method is the method which this parameter is used for.
	Method *Method
}

// ConvertFuncExpr returns a go expression of a converter function.
// The converter function converts a string into a value for the parameter.
func (p Parameter) ConvertFuncExpr() (string, error) {
	tbl := proto3ConvertFuncs
	if p.Target.Message.File.proto2() {
		tbl = proto2ConvertFuncs
	}
	typ := p.Target.GetType()
	conv, ok := tbl[typ]
	if !ok {
		return "", fmt.Errorf("unsupported field type %s of parameter %s in %s.%s", typ, p.FieldPath, p.Method.Service.GetName(), p.Method.GetName())
	}
	return conv, nil
}

// Body describes a http requtest body to be sent to the method.
type Body struct {
	// FieldPath is a path to a proto field which the request body is mapped to.
	// The request body is mapped to the request type itself if FieldPath is empty.
	FieldPath FieldPath
}

// RHS returns a right-hand-side expression in go to be used to initialize method request object.
// It starts with "msgExpr", which is the go expression of the method request object.
func (b Body) RHS(msgExpr string) string {
	return b.FieldPath.RHS(msgExpr)
}

// FieldPath is a path to a field from a request message.
type FieldPath []FieldPathComponent

// String returns a string representation of the field path.
func (p FieldPath) String() string {
	var components []string
	for _, c := range p {
		components = append(components, c.Name)
	}
	return strings.Join(components, ".")
}

// IsNestedProto3 indicates whether the FieldPath is a nested Proto3 path.
func (p FieldPath) IsNestedProto3() bool {
	if len(p) > 1 && !p[0].Target.Message.File.proto2() {
		return true
	}
	return false
}

// RHS is a right-hand-side expression in go to be used to assign a value to the target field.
// It starts with "msgExpr", which is the go expression of the method request object.
func (p FieldPath) RHS(msgExpr string) string {
	l := len(p)
	if l == 0 {
		return msgExpr
	}
	components := []string{msgExpr}
	for i, c := range p {
		if i == l-1 {
			components = append(components, c.RHS())
			continue
		}
		components = append(components, c.LHS())
	}
	return strings.Join(components, ".")
}

// FieldPathComponent is a path component in FieldPath
type FieldPathComponent struct {
	// Name is a name of the proto field which this component corresponds to.
	// TODO(yugui) is this necessary?
	Name string
	// Target is the proto field which this component corresponds to.
	Target *Field
}

// RHS returns a right-hand-side expression in go for this field.
func (c FieldPathComponent) RHS() string {
	return gogen.CamelCase(c.Name)
}

// LHS returns a left-hand-side expression in go for this field.
func (c FieldPathComponent) LHS() string {
	if c.Target.Message.File.proto2() {
		return fmt.Sprintf("Get%s()", gogen.CamelCase(c.Name))
	}
	return gogen.CamelCase(c.Name)
}

var (
	proto3ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
		descriptor.FieldDescriptorProto_TYPE_DOUBLE:  "runtime.Float64",
		descriptor.FieldDescriptorProto_TYPE_FLOAT:   "runtime.Float32",
		descriptor.FieldDescriptorProto_TYPE_INT64:   "runtime.Int64",
		descriptor.FieldDescriptorProto_TYPE_UINT64:  "runtime.Uint64",
		descriptor.FieldDescriptorProto_TYPE_INT32:   "runtime.Int32",
		descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64",
		descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32",
		descriptor.FieldDescriptorProto_TYPE_BOOL:    "runtime.Bool",
		descriptor.FieldDescriptorProto_TYPE_STRING:  "runtime.String",
		// FieldDescriptorProto_TYPE_GROUP
		// FieldDescriptorProto_TYPE_MESSAGE
		// FieldDescriptorProto_TYPE_BYTES
		// TODO(yugui) Handle bytes
		descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32",
		// FieldDescriptorProto_TYPE_ENUM
		// TODO(yugui) Handle Enum
		descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32",
		descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64",
		descriptor.FieldDescriptorProto_TYPE_SINT32:   "runtime.Int32",
		descriptor.FieldDescriptorProto_TYPE_SINT64:   "runtime.Int64",
	}

	proto2ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
		descriptor.FieldDescriptorProto_TYPE_DOUBLE:  "runtime.Float64P",
		descriptor.FieldDescriptorProto_TYPE_FLOAT:   "runtime.Float32P",
		descriptor.FieldDescriptorProto_TYPE_INT64:   "runtime.Int64P",
		descriptor.FieldDescriptorProto_TYPE_UINT64:  "runtime.Uint64P",
		descriptor.FieldDescriptorProto_TYPE_INT32:   "runtime.Int32P",
		descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64P",
		descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32P",
		descriptor.FieldDescriptorProto_TYPE_BOOL:    "runtime.BoolP",
		descriptor.FieldDescriptorProto_TYPE_STRING:  "runtime.StringP",
		// FieldDescriptorProto_TYPE_GROUP
		// FieldDescriptorProto_TYPE_MESSAGE
		// FieldDescriptorProto_TYPE_BYTES
		// TODO(yugui) Handle bytes
		descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32P",
		// FieldDescriptorProto_TYPE_ENUM
		// TODO(yugui) Handle Enum
		descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32P",
		descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64P",
		descriptor.FieldDescriptorProto_TYPE_SINT32:   "runtime.Int32P",
		descriptor.FieldDescriptorProto_TYPE_SINT64:   "runtime.Int64P",
	}
)
<|endoftext|>


<commit_before>package hammer

import (
	"errors"
	"fmt"
)

// ExpandRecursive fills in inheritance for the Multi field
func (p *Package) ExpandRecursive(parent *Package) error {
	p.Parent = parent // should be called with nil as a parent for the top level
	p.Children = []*Package{}

	for _, sub := range p.Multi {
		grandchild, err := p.expandSingle(sub)
		if err != nil {
			return err
		}

		p.Children = append(p.Children, grandchild)
		grandchild.ExpandRecursive(p)
	}

	return nil
}

func (p *Package) expandSingle(child *Package) (*Package, error) {
	base := &Package{}

	// copy fields
	*base = *p

	// reset fields we should never inherit
	base.Multi = []*Package{}

	scripts := Scripts{}
	for name, script := range base.Scripts {
		// we want each sub-type to have their own build script so we can share a
		// common build as much as possible.
		if name == "build" {
			continue
		}
		scripts[name] = script
	}
	base.Scripts = scripts

	// single-value fields
	type Field struct {
		child interface{}
		base  interface{}
	}
	fields := []Field{
		{child.Architecture, &base.Architecture},
		{child.Depends, &base.Depends},
		{child.Description, &base.Description},
		{child.Epoch, &base.Epoch},
		{child.ExtraArgs, &base.ExtraArgs},
		{child.Iteration, &base.Iteration},
		{child.License, &base.License},
		{child.Multi, &base.Multi},
		{child.Name, &base.Name},
		{child.Resources, &base.Resources},
		{child.Scripts, &base.Scripts},
		{child.Targets, &base.Targets},
		{child.Type, &base.Type},
		{child.URL, &base.URL},
		{child.Vendor, &base.Vendor},
		{child.Version, &base.Version},
	}
	for _, field := range fields {
		switch value := field.child.(type) {
		case string: // basic values
			if value != "" {
				target, ok := field.base.(*string)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []string: // dependencies
			if len(value) != 0 {
				target, ok := field.base.(*[]string)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []Resource:
			if len(value) != 0 {
				target, ok := field.base.(*[]Resource)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []Target:
			if len(value) != 0 {
				target, ok := field.base.(*[]Target)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []*Package:
			if len(value) != 0 {
				target, ok := field.base.(*[]*Package)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
				fmt.Println(value)
			}

		case Scripts:
			target, ok := field.base.(*Scripts)
			if !ok {
				return nil, errors.New("bad value for field base")
			}

			for name, script := range value {
				scripts[name] = script
			}

			*target = scripts

		default:
			return nil, errors.New("don't know how to handle value")
		}
	}

	return base, nil
}
<commit_msg>expand: make sure logger has the right name<commit_after>package hammer

import (
	"errors"
	"fmt"
)

// ExpandRecursive fills in inheritance for the Multi field
func (p *Package) ExpandRecursive(parent *Package) error {
	p.Parent = parent // should be called with nil as a parent for the top level
	p.Children = []*Package{}

	for _, sub := range p.Multi {
		grandchild, err := p.expandSingle(sub)
		if err != nil {
			return err
		}

		p.Children = append(p.Children, grandchild)
		grandchild.ExpandRecursive(p)
	}

	return nil
}

func (p *Package) expandSingle(child *Package) (*Package, error) {
	base := NewPackage()

	// copy fields
	*base = *p

	// reset fields we should never inherit
	base.Multi = []*Package{}

	scripts := Scripts{}
	for name, script := range base.Scripts {
		// we want each sub-type to have their own build script so we can share a
		// common build as much as possible.
		if name == "build" {
			continue
		}
		scripts[name] = script
	}
	base.Scripts = scripts

	// single-value fields
	type Field struct {
		child interface{}
		base  interface{}
	}
	fields := []Field{
		{child.Architecture, &base.Architecture},
		{child.Depends, &base.Depends},
		{child.Description, &base.Description},
		{child.Epoch, &base.Epoch},
		{child.ExtraArgs, &base.ExtraArgs},
		{child.Iteration, &base.Iteration},
		{child.License, &base.License},
		{child.Multi, &base.Multi},
		{child.Name, &base.Name},
		{child.Resources, &base.Resources},
		{child.Scripts, &base.Scripts},
		{child.Targets, &base.Targets},
		{child.Type, &base.Type},
		{child.URL, &base.URL},
		{child.Vendor, &base.Vendor},
		{child.Version, &base.Version},
	}
	for _, field := range fields {
		switch value := field.child.(type) {
		case string: // basic values
			if value != "" {
				target, ok := field.base.(*string)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []string: // dependencies
			if len(value) != 0 {
				target, ok := field.base.(*[]string)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []Resource:
			if len(value) != 0 {
				target, ok := field.base.(*[]Resource)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []Target:
			if len(value) != 0 {
				target, ok := field.base.(*[]Target)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
			}

		case []*Package:
			if len(value) != 0 {
				target, ok := field.base.(*[]*Package)
				if !ok {
					return nil, errors.New("bad value for field base")
				}
				*target = value
				fmt.Println(value)
			}

		case Scripts:
			target, ok := field.base.(*Scripts)
			if !ok {
				return nil, errors.New("bad value for field base")
			}

			for name, script := range value {
				scripts[name] = script
			}

			*target = scripts

		default:
			return nil, errors.New("don't know how to handle value")
		}
	}

	// set stuff via methods
	base.logger = p.logger.WithField("name", base.Name)

	return base, nil
}
<|endoftext|>


<commit_before>package header

const (
	// Accept header
	Accept = `Accept`

	// AcceptEncoding header
	AcceptEncoding = `Accept-Encoding`

	// AcceptLanguage Accept-Language header
	AcceptLanguage = `Accept-Language`

	// AcceptCharset Accept-Charset header
	AcceptCharset = `Accept-Charset`

	// Allow request methods
	Allow = `Allow`

	// Authorization header
	Authorization = `Authorization`

	// ContentDisposition header
	ContentDisposition = `Content-Disposition`

	// ContentEncoding header
	ContentEncoding = `Content-Encoding`

	// ContentLength header
	ContentLength = `Content-Length`

	// ContentType header
	ContentType = `Content-Type`

	// IfModifiedSince header
	IfModifiedSince = `If-Modified-Since`

	// IfMatch If-Match header
	IfMatch = `If-Match`

	// IfNoneMatch If-None-Match header
	IfNoneMatch = `If-None-Match`

	// IfRange If-Range header
	IfRange = `If-Range`

	// IfUnmodifiedSince If-Unmodified-Since header
	IfUnmodifiedSince = `If-Unmodified-Since`

	// LastModified header
	LastModified = `Last-Modified`

	// Location header
	Location = `Location`

	// Upgrade header
	Upgrade = `Upgrade`

	// Vary header
	Vary = `Vary`

	// WWWAuthenticate header
	WWWAuthenticate = `WWW-Authenticate`

	// XForwardedFor header
	XForwardedFor = `X-Forwarded-For`

	// XForwardedProto header
	XForwardedProto = `X-Forwarded-Proto`

	// XScheme header
	XScheme = `X-Scheme`

	// XRealIP header
	XRealIP = `X-Real-IP`

	// Origin header
	Origin = `Origin`

	// AccessControlRequestMethod header
	AccessControlRequestMethod = `Access-Control-Request-Method`

	// AccessControlRequestHeaders header
	AccessControlRequestHeaders = `Access-Control-Request-Headers`

	// AccessControlAllowOrigin header
	AccessControlAllowOrigin = `Access-Control-Allow-Origin`

	// AccessControlAllowMethods header
	AccessControlAllowMethods = `Access-Control-Allow-Methods`

	// AccessControlAllowHeaders header
	AccessControlAllowHeaders = `Access-Control-Allow-Headers`

	// AccessControlAllowCredentials header
	AccessControlAllowCredentials = `Access-Control-Allow-Credentials`

	// AccessControlExposeHeaders header
	AccessControlExposeHeaders = `Access-Control-Expose-Headers`

	// AccessControlMaxAge header
	AccessControlMaxAge = `Access-Control-Max-Age`

	// MsEchoRequest Ms-Echo-Request header
	MsEchoRequest = `Ms-Echo-Request`

	// RetryAfter Retry-After header
	RetryAfter = `Retry-After`

	// Referer header
	Referer = `Referer`

	// UserAgent User-Agent header
	UserAgent = `User-Agent`

	// Pragma header
	Pragma = `Pragma`

	// CacheControl Cache-Control header
	CacheControl = `Cache-Control`

	// Expires header
	Expires = `Expires`

	// ETag header
	ETag = `ETag`

	// Range Range header
	Range = `Range`

	// AcceptRanges Accept-Ranges header
	AcceptRanges = `Accept-Ranges`

	// ContentRange Content-Range header
	ContentRange = `Content-Range`

	// Кастомные заголовки

	// XUserAgent X-User-Agent header. Аналог User-Agent
	XUserAgent = `X-User-Agent`

	// XConnectionType X-Connection-Type header. Тип подключения, значения: ["3g", "wifi", "cable", "broadband", ...]
	XConnectionType = `X-Connection-Type`

	// XInternetServiceProvider X-Internet-Service-Provider header. Расширенная сегментация типа подключения по ISP (интернет-провайдер)
	XInternetServiceProvider = `X-Internet-Service-Provider`

	// XCarrier X-Carrier header. Расширенный тип мобильного трафика, значения: ["tele2", "mts", ...]
	XCarrier = `X-Carrier`

	// XTimeZone X-Time-Zone header. Значения: [0, 3, -1, 12, ...]
	XTimeZone = `X-Time-Zone`

	// XDayOfWeek X-Day-Of-Week header. День недели, значения: [0,1,2,3,4,5,6] 0=пнд, -1=не известен
	XDayOfWeek = `X-Day-Of-Week`

	// XGeoContinent X-Geo-Continent header. Географический континент
	XGeoContinent = `X-Geo-Continent`

	// XGeoRegion X-Geo-Region header. Географический регион
	XGeoRegion = `X-Geo-Region`

	// XGeoCountry X-Geo-Country header. Страна
	XGeoCountry = `X-Geo-Country`

	// XGeoCity X-Geo-City header. Город
	XGeoCity = `X-Geo-City`

	// XDeviceManufacturer X-Device-Manufacturer header. Производитель/Марка устройства
	XDeviceManufacturer = `X-Device-Manufacturer`

	// XDeviceModel X-Device-Model header. Модель устройства
	XDeviceModel = `X-Device-Model`

	// XScreenSize X-Screen-Size header. Разрешение экрана
	XScreenSize = `X-Screen-Size`

	// XProxyType X-ProxyType header. Тип прокси, значения: ["vpn", "tor", "dch", "pub", "web"]
	XProxyType = `X-ProxyType`

	// XRedefinitionDeny X-Redefinition-Deny header. Запрет переопределения значений маркеров, значения: ["all", "query", "path", "entrypoint"]
	XRedefinitionDeny = `X-Redefinition-Deny`

	// AutoSubmitted Auto-Submitted header
	AutoSubmitted = `Auto-Submitted`

	// Date header
	Date = `Date`

	// DeliveredTo Delivered-To header
	DeliveredTo = `Delivered-To`

	// MessageID Message-Id header
	MessageID = `Message-Id`

	// Received header
	Received = `Received`

	// ReturnPath Return-Path header
	ReturnPath = `Return-Path`

	// XFailedRecipients X-Failed-Recipients header
	XFailedRecipients = `X-Failed-Recipients`

	// XRequestedWith X-Requested-With header
	XRequestedWith = `X-Requested-With`
)
<commit_msg>Added new header<commit_after>package header

const (
	// Accept header
	Accept = `Accept`

	// AcceptEncoding header
	AcceptEncoding = `Accept-Encoding`

	// AcceptLanguage Accept-Language header
	AcceptLanguage = `Accept-Language`

	// AcceptCharset Accept-Charset header
	AcceptCharset = `Accept-Charset`

	// Allow request methods
	Allow = `Allow`

	// Authorization header
	Authorization = `Authorization`

	// ContentDisposition header
	ContentDisposition = `Content-Disposition`

	// ContentEncoding header
	ContentEncoding = `Content-Encoding`

	// ContentLength header
	ContentLength = `Content-Length`

	// ContentType header
	ContentType = `Content-Type`

	// IfModifiedSince header
	IfModifiedSince = `If-Modified-Since`

	// IfMatch If-Match header
	IfMatch = `If-Match`

	// IfNoneMatch If-None-Match header
	IfNoneMatch = `If-None-Match`

	// IfRange If-Range header
	IfRange = `If-Range`

	// IfUnmodifiedSince If-Unmodified-Since header
	IfUnmodifiedSince = `If-Unmodified-Since`

	// LastModified header
	LastModified = `Last-Modified`

	// Location header
	Location = `Location`

	// Upgrade header
	Upgrade = `Upgrade`

	// Vary header
	Vary = `Vary`

	// WWWAuthenticate header
	WWWAuthenticate = `WWW-Authenticate`

	// XForwardedFor header
	XForwardedFor = `X-Forwarded-For`

	// XForwardedProto header
	XForwardedProto = `X-Forwarded-Proto`

	// XScheme header
	XScheme = `X-Scheme`

	// XRealIP header
	XRealIP = `X-Real-IP`

	// Origin header
	Origin = `Origin`

	// AccessControlRequestMethod header
	AccessControlRequestMethod = `Access-Control-Request-Method`

	// AccessControlRequestHeaders header
	AccessControlRequestHeaders = `Access-Control-Request-Headers`

	// AccessControlAllowOrigin header
	AccessControlAllowOrigin = `Access-Control-Allow-Origin`

	// AccessControlAllowMethods header
	AccessControlAllowMethods = `Access-Control-Allow-Methods`

	// AccessControlAllowHeaders header
	AccessControlAllowHeaders = `Access-Control-Allow-Headers`

	// AccessControlAllowCredentials header
	AccessControlAllowCredentials = `Access-Control-Allow-Credentials`

	// AccessControlExposeHeaders header
	AccessControlExposeHeaders = `Access-Control-Expose-Headers`

	// AccessControlMaxAge header
	AccessControlMaxAge = `Access-Control-Max-Age`

	// MsEchoRequest Ms-Echo-Request header
	MsEchoRequest = `Ms-Echo-Request`

	// RetryAfter Retry-After header
	RetryAfter = `Retry-After`

	// Referer header
	Referer = `Referer`

	// UserAgent User-Agent header
	UserAgent = `User-Agent`

	// Pragma header
	Pragma = `Pragma`

	// CacheControl Cache-Control header
	CacheControl = `Cache-Control`

	// Expires header
	Expires = `Expires`

	// ETag header
	ETag = `ETag`

	// Range Range header
	Range = `Range`

	// AcceptRanges Accept-Ranges header
	AcceptRanges = `Accept-Ranges`

	// ContentRange Content-Range header
	ContentRange = `Content-Range`

	// Date header
	Date = `Date`

	// Кастомные заголовки

	// XUserAgent X-User-Agent header. Аналог User-Agent
	XUserAgent = `X-User-Agent`

	// XConnectionType X-Connection-Type header. Тип подключения, значения: ["3g", "wifi", "cable", "broadband", ...]
	XConnectionType = `X-Connection-Type`

	// XInternetServiceProvider X-Internet-Service-Provider header. Расширенная сегментация типа подключения по ISP (интернет-провайдер)
	XInternetServiceProvider = `X-Internet-Service-Provider`

	// XCarrier X-Carrier header. Расширенный тип мобильного трафика, значения: ["tele2", "mts", ...]
	XCarrier = `X-Carrier`

	// XTimeZone X-Time-Zone header. Значения: [0, 3, -1, 12, ...]
	XTimeZone = `X-Time-Zone`

	// XDayOfWeek X-Day-Of-Week header. День недели, значения: [0,1,2,3,4,5,6] 0=пнд, -1=не известен
	XDayOfWeek = `X-Day-Of-Week`

	// XGeoContinent X-Geo-Continent header. Географический континент
	XGeoContinent = `X-Geo-Continent`

	// XGeoRegion X-Geo-Region header. Географический регион
	XGeoRegion = `X-Geo-Region`

	// XGeoCountry X-Geo-Country header. Страна
	XGeoCountry = `X-Geo-Country`

	// XGeoCity X-Geo-City header. Город
	XGeoCity = `X-Geo-City`

	// XDeviceManufacturer X-Device-Manufacturer header. Производитель/Марка устройства
	XDeviceManufacturer = `X-Device-Manufacturer`

	// XDeviceModel X-Device-Model header. Модель устройства
	XDeviceModel = `X-Device-Model`

	// XScreenSize X-Screen-Size header. Разрешение экрана
	XScreenSize = `X-Screen-Size`

	// XProxyType X-ProxyType header. Тип прокси, значения: ["vpn", "tor", "dch", "pub", "web"]
	XProxyType = `X-ProxyType`

	// XRedefinitionDeny X-Redefinition-Deny header. Запрет переопределения значений маркеров, значения: ["all", "query", "path", "entrypoint"]
	XRedefinitionDeny = `X-Redefinition-Deny`

	// AutoSubmitted Auto-Submitted header
	AutoSubmitted = `Auto-Submitted`

	// DeliveredTo Delivered-To header
	DeliveredTo = `Delivered-To`

	// MessageID Message-Id header
	MessageID = `Message-Id`

	// Received header
	Received = `Received`

	// ReturnPath Return-Path header
	ReturnPath = `Return-Path`

	// XFailedRecipients X-Failed-Recipients header
	XFailedRecipients = `X-Failed-Recipients`

	// XRequestedWith X-Requested-With header
	XRequestedWith = `X-Requested-With`

	// XProjectID The X-Project-Id header
	XProjectID = `X-Project-Id`

	// XCoreSDToken The X-Coresd-Token header
	XCoreSDToken = `X-Coresd-Token`
)
<|endoftext|>


<commit_before>package henchman

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/flosch/pongo2"
	"github.com/pborman/uuid"
)

type Task struct {
	Id           string
	Debug        bool
	IgnoreErrors bool `yaml:"ignore_errors"`
	Local        bool
	Module       *Module
	Name         string
	Register     string
	Retry        int
	Sudo         bool
	Vars         VarsMap
	When         string
}

type TaskResult struct {
	State  string      `json:"status"`
	Output interface{} `json:"output,omitempty"`
	Msg    string      `json:"msg"`
}

func getTaskResult(buf *bytes.Buffer) (*TaskResult, error) {
	var taskResult TaskResult
	resultInBytes := []byte(buf.String())
	err := json.Unmarshal(resultInBytes, &taskResult)
	if err != nil {
		return &TaskResult{}, HenchErr(err, nil, "While unmarshalling task results")
	}
	return &taskResult, nil
}

func setTaskResult(taskResult *TaskResult, buf *bytes.Buffer) error {
	resultInBytes := []byte(buf.String())
	err := json.Unmarshal(resultInBytes, &taskResult)
	if err != nil {
		return HenchErr(err, nil, "While unmarshalling task results")
	}
	return nil
}

// strings will be evaluated using pongo2 templating with context of
// VarsMap and RegisterMap
func renderValue(value string, varsMap VarsMap, registerMap map[string]interface{}) (string, error) {
	tmpl, err := pongo2.FromString(value)
	if err != nil {
		return "", HenchErr(err, map[string]interface{}{
			"value":    value,
			"solution": "Refer to wiki for proper pongo2 formatting",
		}, "While templating")
	}

	ctxt := pongo2.Context{"vars": varsMap}
	ctxt = ctxt.Update(registerMap)

	out, err := tmpl.Execute(ctxt)
	if err != nil {
		return "", HenchErr(err, map[string]interface{}{
			"value":    value,
			"context":  ctxt,
			"solution": "Refer to wiki for proper pongo2 formatting",
		}, "While executing")
	}
	return out, nil
}

// wrapper for Rendering each task
func (task *Task) Render(vars VarsMap, registerMap RegMap) error {
	var err error
	task.Name, err = renderValue(task.Name, vars, registerMap)
	if err != nil {
		return err
	}

	if task.When != "" {
		task.When, err = renderValue("{{"+task.When+"}}", vars, registerMap)
		if err != nil {
			return err
		}
	}

	for k, v := range task.Module.Params {
		task.Module.Params[k], err = renderValue(v, vars, registerMap)
		if err != nil {
			return err
		}
	}

	return nil
}

// checks and converts when to bool
func (task *Task) ProcessWhen() (bool, error) {
	if task.When == "" {
		return true, nil
	}

	result, err := strconv.ParseBool(task.When)
	if err != nil {
		return false, HenchErr(err, map[string]interface{}{
			"task_when": task.When,
			"solution":  "make sure value is a bool",
		}, "")
	}

	return result, nil
}

func (task *Task) Run(machine *Machine, vars VarsMap, registerMap RegMap) (*TaskResult, error) {
	// Add current host to vars
	task.Id = uuid.New()

	//local copy of module params for each machine
	moduleParams := make(map[string]string)
	for k, v := range task.Module.Params {
		moduleParams[k] = v
	}
	proceed, err := task.ProcessWhen()
	if err != nil {
		return &TaskResult{}, HenchErr(err, nil, "While processing when")
	}

	if proceed == false {
		return &TaskResult{State: "skipped"}, nil
	}

	execOrder, err := task.Module.ExecOrder()
	// currently err will always be nil
	if err != nil {
		return &TaskResult{}, HenchErr(err, nil, "")
	}

	remoteModDir := "${HOME}/.henchman/"
	remoteModPath := filepath.Join(remoteModDir, task.Module.Name)
	// NOTE: Info or Debug level
	Debug(map[string]interface{}{
		"task":   task.Name,
		"host":   task.Vars["current_host"],
		"module": task.Module.Name,
		"order":  execOrder,
	}, "Exec Order")

	var taskResult TaskResult
	for _, execStep := range execOrder {
		switch execStep {
		// Exec Order for Default
		case "exec_module":
			// executes module by calling the copied module remotely
			Info(map[string]interface{}{
				"mod path": remoteModPath,
				"host":     task.Vars["current_host"],
				"task":     task.Name,
				"module":   task.Module.Name,
			}, "Executing Module in Task")

			jsonParams, err := json.Marshal(moduleParams)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "In exec_module while json marshalling")
			}
			buf, err := machine.Transport.Exec(remoteModPath, jsonParams, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_module")
			}
			//This should not be empty
			err = setTaskResult(&taskResult, buf)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_module")
			}
		case "exec_tar_module":
			// executes module by calling the copied module remotely
			// NOTE: may want to just change the way remoteModPath is created
			newModPath := remoteModDir + task.Module.Name + "/exec"
			Info(map[string]interface{}{
				"mod path": newModPath,
				"host":     task.Vars["current_host"],
				"task":     task.Name,
				"module":   task.Module.Name,
			}, "Executing Module in Task")

			jsonParams, err := json.Marshal(moduleParams)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "In exec_tar_module while json marshalling")
			}
			buf, err := machine.Transport.Exec(newModPath, jsonParams, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_tar_module")
			}
			//This should not be empty
			err = setTaskResult(&taskResult, buf)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_tar_module")
			}
		case "put_for_copy":
			//scp's file from local location to remote location
			srcPath, present := moduleParams["src"]
			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'src' parameter"), nil, "")
			}

			info, err := os.Stat(srcPath)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Unable to get info on file/dir")
			}

			if info.IsDir() {
				err = machine.Transport.Put(srcPath, remoteModDir, "dir")
			} else {
				err = machine.Transport.Put(srcPath, remoteModDir, "file")
			}

			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Putting File")
			}
		case "copy_remote":
			//copies file from remote .henchman location to expected location
			remoteSrcPath, present := moduleParams["src"]
			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'src' parameter"), nil, "")
			}

			dstPath, present := moduleParams["dest"]
			dstFldr := filepath.Dir(dstPath)

			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'dest' parameter"), nil, "")
			}

			srcPath := filepath.Join(remoteModDir, filepath.Base(remoteSrcPath))

			cmd := fmt.Sprintf("/bin/mkdir -p %s", dstFldr)
			buf, err := machine.Transport.Exec(cmd, nil, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While copying file")
			}

			cmd = fmt.Sprintf("/bin/cp -r %s %s", srcPath, dstPath)
			buf, err = machine.Transport.Exec(cmd, nil, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While copying file")
			}
			if len(buf.String()) != 0 {
				err = setTaskResult(&taskResult, buf)
				if err != nil {
					return &TaskResult{}, HenchErr(err, nil, "Setting task result from copying file")
				}
			}
		case "process_template":
			srcPath, present := moduleParams["src"]
			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'src' parameter"), nil, "")
			}

			info, err := os.Stat(srcPath)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Unable to get info on file/dir")
			}

			// creates temp directory to store templated files
			tplDir := machine.Hostname + "_templates"
			if err := os.Mkdir(tplDir, 0755); err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Error creating tplDir for templating")
			}

			// if the file(s) to template is a folder
			// create the approriate director in tplDir
			baseDir := ""
			if info.IsDir() {
				baseDir = filepath.Join(tplDir, filepath.Base(srcPath))
				if err := os.Mkdir(baseDir, 0755); err != nil {
					return &TaskResult{}, HenchErr(err, nil, "Error creating baseDir for templating folders")
				}
			}

			err = filepath.Walk(srcPath,
				func(path string, info os.FileInfo, err error) error {
					if err != nil {
						return HenchErr(err, map[string]interface{}{
							"path": path,
						}, "While walking")
					}

					if info.IsDir() {
						// FIXME: make a cleaner work around
						// since first file checked is the dir, can't make dir
						ext := strings.TrimPrefix(path, srcPath)
						if ext != "" {
							return os.Mkdir(filepath.Join(baseDir, ext), 0755)
						}

						return nil
					}

					tpl, err := pongo2.FromFile(path)
					if err != nil {
						return HenchErr(err, map[string]interface{}{
							"file":     srcPath,
							"solution": "Verify if src file has proper pongo2 formatting",
						}, "While processing template file")
					}
					out, err := tpl.Execute(pongo2.Context{"vars": vars})
					if err != nil {
						return HenchErr(err, map[string]interface{}{
							"file":     srcPath,
							"solution": "Verify if src file has proper pongo2 formatting",
						}, "While processing template file")
					}

					renderFilePath := filepath.Join(tplDir, filepath.Base(srcPath))
					if baseDir != "" {
						renderFilePath = filepath.Join(baseDir, strings.TrimPrefix(path, srcPath))
					}

					err = ioutil.WriteFile(renderFilePath, []byte(out), 0644)
					if err != nil {
						return HenchErr(err, nil, "While processing template file")
					}

					return nil
				})
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While walking in process template")
			}

			moduleParams["srcOrig"] = srcPath
			if baseDir != "" {
				moduleParams["src"] = baseDir
			} else {
				moduleParams["src"] = filepath.Join(tplDir, filepath.Base(srcPath))
			}
		case "reset_src":
			// remove the tplDir
			tplDir := machine.Hostname + "_templates"
			if err := os.RemoveAll(tplDir); err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in reset_src")
			}
			moduleParams["src"] = moduleParams["srcOrig"]
		}
	}

	// Set to status to ignored if the result is a failure
	if task.IgnoreErrors && (taskResult.State == "error" || taskResult.State == "failure") {
		taskResult.State = "ignored"
	}

	if task.Register != "" {
		registerMap[task.Register] = taskResult
	}

	return &taskResult, nil
}
<commit_msg>To get a different sys executable.  Will removed println later<commit_after>package henchman

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/flosch/pongo2"
	"github.com/pborman/uuid"
)

type Task struct {
	Id           string
	Debug        bool
	IgnoreErrors bool `yaml:"ignore_errors"`
	Local        bool
	Module       *Module
	Name         string
	Register     string
	Retry        int
	Sudo         bool
	Vars         VarsMap
	When         string
}

type TaskResult struct {
	State  string      `json:"status"`
	Output interface{} `json:"output,omitempty"`
	Msg    string      `json:"msg"`
}

func getTaskResult(buf *bytes.Buffer) (*TaskResult, error) {
	var taskResult TaskResult
	resultInBytes := []byte(buf.String())
	err := json.Unmarshal(resultInBytes, &taskResult)
	if err != nil {
		return &TaskResult{}, HenchErr(err, nil, "While unmarshalling task results")
	}
	return &taskResult, nil
}

func setTaskResult(taskResult *TaskResult, buf *bytes.Buffer) error {
	resultInBytes := []byte(buf.String())
	fmt.Println(buf.String())
	err := json.Unmarshal(resultInBytes, &taskResult)
	if err != nil {
		return HenchErr(err, nil, "While unmarshalling task results")
	}
	return nil
}

// strings will be evaluated using pongo2 templating with context of
// VarsMap and RegisterMap
func renderValue(value string, varsMap VarsMap, registerMap map[string]interface{}) (string, error) {
	tmpl, err := pongo2.FromString(value)
	if err != nil {
		return "", HenchErr(err, map[string]interface{}{
			"value":    value,
			"solution": "Refer to wiki for proper pongo2 formatting",
		}, "While templating")
	}

	ctxt := pongo2.Context{"vars": varsMap}
	ctxt = ctxt.Update(registerMap)

	out, err := tmpl.Execute(ctxt)
	if err != nil {
		return "", HenchErr(err, map[string]interface{}{
			"value":    value,
			"context":  ctxt,
			"solution": "Refer to wiki for proper pongo2 formatting",
		}, "While executing")
	}
	return out, nil
}

// wrapper for Rendering each task
func (task *Task) Render(vars VarsMap, registerMap RegMap) error {
	var err error
	task.Name, err = renderValue(task.Name, vars, registerMap)
	if err != nil {
		return err
	}

	if task.When != "" {
		task.When, err = renderValue("{{"+task.When+"}}", vars, registerMap)
		if err != nil {
			return err
		}
	}

	for k, v := range task.Module.Params {
		task.Module.Params[k], err = renderValue(v, vars, registerMap)
		if err != nil {
			return err
		}
	}

	return nil
}

// checks and converts when to bool
func (task *Task) ProcessWhen() (bool, error) {
	if task.When == "" {
		return true, nil
	}

	result, err := strconv.ParseBool(task.When)
	if err != nil {
		return false, HenchErr(err, map[string]interface{}{
			"task_when": task.When,
			"solution":  "make sure value is a bool",
		}, "")
	}

	return result, nil
}

func (task *Task) Run(machine *Machine, vars VarsMap, registerMap RegMap) (*TaskResult, error) {
	// Add current host to vars
	task.Id = uuid.New()

	//local copy of module params for each machine
	moduleParams := make(map[string]string)
	for k, v := range task.Module.Params {
		moduleParams[k] = v
	}
	proceed, err := task.ProcessWhen()
	if err != nil {
		return &TaskResult{}, HenchErr(err, nil, "While processing when")
	}

	if proceed == false {
		return &TaskResult{State: "skipped"}, nil
	}

	execOrder, err := task.Module.ExecOrder()
	// currently err will always be nil
	if err != nil {
		return &TaskResult{}, HenchErr(err, nil, "")
	}

	remoteModDir := "${HOME}/.henchman/"
	remoteModPath := filepath.Join(remoteModDir, task.Module.Name)
	// NOTE: Info or Debug level
	Debug(map[string]interface{}{
		"task":   task.Name,
		"host":   task.Vars["current_host"],
		"module": task.Module.Name,
		"order":  execOrder,
	}, "Exec Order")

	var taskResult TaskResult
	for _, execStep := range execOrder {
		switch execStep {
		// Exec Order for Default
		case "exec_module":
			// executes module by calling the copied module remotely
			Info(map[string]interface{}{
				"mod path": remoteModPath,
				"host":     task.Vars["current_host"],
				"task":     task.Name,
				"module":   task.Module.Name,
			}, "Executing Module in Task")

			jsonParams, err := json.Marshal(moduleParams)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "In exec_module while json marshalling")
			}
			buf, err := machine.Transport.Exec(remoteModPath, jsonParams, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_module")
			}
			//This should not be empty
			err = setTaskResult(&taskResult, buf)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_module")
			}
		case "exec_tar_module":
			// executes module by calling the copied module remotely
			// NOTE: may want to just change the way remoteModPath is created
			newModPath := remoteModDir + task.Module.Name + "/exec"
			Info(map[string]interface{}{
				"mod path": newModPath,
				"host":     task.Vars["current_host"],
				"task":     task.Name,
				"module":   task.Module.Name,
			}, "Executing Module in Task")

			jsonParams, err := json.Marshal(moduleParams)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "In exec_tar_module while json marshalling")
			}
			buf, err := machine.Transport.Exec(newModPath, jsonParams, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_tar_module")
			}
			//This should not be empty
			err = setTaskResult(&taskResult, buf)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in exec_tar_module")
			}
		case "put_for_copy":
			//scp's file from local location to remote location
			srcPath, present := moduleParams["src"]
			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'src' parameter"), nil, "")
			}

			info, err := os.Stat(srcPath)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Unable to get info on file/dir")
			}

			if info.IsDir() {
				err = machine.Transport.Put(srcPath, remoteModDir, "dir")
			} else {
				err = machine.Transport.Put(srcPath, remoteModDir, "file")
			}

			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Putting File")
			}
		case "copy_remote":
			//copies file from remote .henchman location to expected location
			remoteSrcPath, present := moduleParams["src"]
			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'src' parameter"), nil, "")
			}

			dstPath, present := moduleParams["dest"]
			dstFldr := filepath.Dir(dstPath)

			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'dest' parameter"), nil, "")
			}

			srcPath := filepath.Join(remoteModDir, filepath.Base(remoteSrcPath))

			cmd := fmt.Sprintf("/bin/mkdir -p %s", dstFldr)
			buf, err := machine.Transport.Exec(cmd, nil, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While copying file")
			}

			cmd = fmt.Sprintf("/bin/cp -r %s %s", srcPath, dstPath)
			buf, err = machine.Transport.Exec(cmd, nil, task.Sudo)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While copying file")
			}
			if len(buf.String()) != 0 {
				err = setTaskResult(&taskResult, buf)
				if err != nil {
					return &TaskResult{}, HenchErr(err, nil, "Setting task result from copying file")
				}
			}
		case "process_template":
			srcPath, present := moduleParams["src"]
			if !present {
				return &TaskResult{}, HenchErr(fmt.Errorf("Unable to find 'src' parameter"), nil, "")
			}

			info, err := os.Stat(srcPath)
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Unable to get info on file/dir")
			}

			// creates temp directory to store templated files
			tplDir := machine.Hostname + "_templates"

			// double checks to see if tplDir already exists, if it does remove it
			if _, err := os.Stat(tplDir); os.IsExist(err) {
				if err := os.RemoveAll(tplDir); err != nil {
					return &TaskResult{}, HenchErr(err, nil, "While removing old tplDir")
				}
			}

			if err := os.Mkdir(tplDir, 0755); err != nil {
				return &TaskResult{}, HenchErr(err, nil, "Error creating tplDir for templating")
			}

			// if the file(s) to template is a folder
			// create the approriate director in tplDir
			baseDir := ""
			if info.IsDir() {
				baseDir = filepath.Join(tplDir, filepath.Base(srcPath))
				if err := os.Mkdir(baseDir, 0755); err != nil {
					return &TaskResult{}, HenchErr(err, nil, "Error creating baseDir for templating folders")
				}
			}

			err = filepath.Walk(srcPath,
				func(path string, info os.FileInfo, err error) error {
					if err != nil {
						return HenchErr(err, map[string]interface{}{
							"path": path,
						}, "While walking")
					}

					if info.IsDir() {
						// FIXME: make a cleaner work around
						// since first file checked is the dir, can't make dir
						ext := strings.TrimPrefix(path, srcPath)
						if ext != "" {
							return os.Mkdir(filepath.Join(baseDir, ext), 0755)
						}

						return nil
					}

					tpl, err := pongo2.FromFile(path)
					if err != nil {
						return HenchErr(err, map[string]interface{}{
							"file":     srcPath,
							"solution": "Verify if src file has proper pongo2 formatting",
						}, "While processing template file")
					}
					out, err := tpl.Execute(pongo2.Context{"vars": vars})
					if err != nil {
						return HenchErr(err, map[string]interface{}{
							"file":     srcPath,
							"solution": "Verify if src file has proper pongo2 formatting",
						}, "While processing template file")
					}

					renderFilePath := filepath.Join(tplDir, filepath.Base(srcPath))
					if baseDir != "" {
						renderFilePath = filepath.Join(baseDir, strings.TrimPrefix(path, srcPath))
					}

					err = ioutil.WriteFile(renderFilePath, []byte(out), 0644)
					if err != nil {
						return HenchErr(err, nil, "While processing template file")
					}

					return nil
				})
			if err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While walking in process template")
			}

			moduleParams["srcOrig"] = srcPath
			if baseDir != "" {
				moduleParams["src"] = baseDir
			} else {
				moduleParams["src"] = filepath.Join(tplDir, filepath.Base(srcPath))
			}
		case "reset_src":
			// remove the tplDir
			tplDir := machine.Hostname + "_templates"
			if err := os.RemoveAll(tplDir); err != nil {
				return &TaskResult{}, HenchErr(err, nil, "While in reset_src")
			}
			moduleParams["src"] = moduleParams["srcOrig"]
		}
	}

	// Set to status to ignored if the result is a failure
	if task.IgnoreErrors && (taskResult.State == "error" || taskResult.State == "failure") {
		taskResult.State = "ignored"
	}

	if task.Register != "" {
		registerMap[task.Register] = taskResult
	}

	return &taskResult, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	"fmt"
	"github.com/cooper/quiki/wikiclient"
	"log"
	"net/http"
)

func handlePage(c wikiclient.Client, relPath string, w http.ResponseWriter, r *http.Request) {
	res, err := c.Request(wikiclient.NewMessage("page", map[string]interface{}{
		"name": relPath,
	}))
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(res)
}

func handleImage(c wikiclient.Client, relPath string, w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, relPath, c, r)
}
<commit_msg>fprint<commit_after>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	"fmt"
	"github.com/cooper/quiki/wikiclient"
	"net/http"
)

func handlePage(c wikiclient.Client, relPath string, w http.ResponseWriter, r *http.Request) {
	res, err := c.Request(wikiclient.NewMessage("page", map[string]interface{}{
		"name": relPath,
	}))
	if err != nil {
		fmt.Fprint(w, err)
		return
	}
	fmt.Fprint(w, res)
}

func handleImage(c wikiclient.Client, relPath string, w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, relPath, c, r)
}
<|endoftext|>


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

package main

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func buildObjdump(t *testing.T) (tmp, exe string) {
	testenv.MustHaveGoBuild(t)

	tmp, err := ioutil.TempDir("", "TestObjDump")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}

	exe = filepath.Join(tmp, "testobjdump.exe")
	out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
	if err != nil {
		os.RemoveAll(tmp)
		t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
	}
	return
}

var x86Need = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	"JMP main.main(SB)",
	"CALL fmt.Println(SB)",
	"RET",
}

var armNeed = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	//"B.LS main.main(SB)", // TODO(rsc): restore; golang.org/issue/9021
	"BL fmt.Println(SB)",
	"RET",
}

// objdump is fully cross platform: it can handle binaries
// from any known operating system and architecture.
// We could in principle add binaries to testdata and check
// all the supported systems during this test. However, the
// binaries would be about 1 MB each, and we don't want to
// add that much junk to the hg repository. Instead, build a
// binary for the current system (only) and test that objdump
// can handle that one.

func testDisasm(t *testing.T, flags ...string) {
	tmp, exe := buildObjdump(t)
	defer os.RemoveAll(tmp)

	hello := filepath.Join(tmp, "hello.exe")
	args := []string{"build", "-o", hello}
	args = append(args, flags...)
	args = append(args, "testdata/fmthello.go")
	out, err := exec.Command("go", args...).CombinedOutput()
	if err != nil {
		t.Fatalf("go build fmthello.go: %v\n%s", err, out)
	}
	need := []string{
		"fmthello.go:6",
		"TEXT main.main(SB)",
	}
	switch runtime.GOARCH {
	case "amd64", "386":
		need = append(need, x86Need...)
	case "arm":
		need = append(need, armNeed...)
	}

	out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
	if err != nil {
		t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
	}

	text := string(out)
	ok := true
	for _, s := range need {
		if !strings.Contains(text, s) {
			t.Errorf("disassembly missing '%s'", s)
			ok = false
		}
	}
	if !ok {
		t.Logf("full disassembly:\n%s", text)
	}
}

func TestDisasm(t *testing.T) {
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, issue 9039", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	testDisasm(t)
}

func TestDisasmExtld(t *testing.T) {
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("skipping on %s", runtime.GOOS)
	}
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, no support for external linking, issue 9038", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	// TODO(jsing): Reenable once openbsd/arm has external linking support.
	if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" {
		t.Skip("skipping on openbsd/arm, no support for external linking, issue 10619")
	}
	testDisasm(t, "-ldflags=-linkmode=external")
}
<commit_msg>cmd/objdump: don't run TestDisasmExtld if cgo is not enabled<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"go/build"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func buildObjdump(t *testing.T) (tmp, exe string) {
	testenv.MustHaveGoBuild(t)

	tmp, err := ioutil.TempDir("", "TestObjDump")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}

	exe = filepath.Join(tmp, "testobjdump.exe")
	out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
	if err != nil {
		os.RemoveAll(tmp)
		t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
	}
	return
}

var x86Need = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	"JMP main.main(SB)",
	"CALL fmt.Println(SB)",
	"RET",
}

var armNeed = []string{
	"fmthello.go:6",
	"TEXT main.main(SB)",
	//"B.LS main.main(SB)", // TODO(rsc): restore; golang.org/issue/9021
	"BL fmt.Println(SB)",
	"RET",
}

// objdump is fully cross platform: it can handle binaries
// from any known operating system and architecture.
// We could in principle add binaries to testdata and check
// all the supported systems during this test. However, the
// binaries would be about 1 MB each, and we don't want to
// add that much junk to the hg repository. Instead, build a
// binary for the current system (only) and test that objdump
// can handle that one.

func testDisasm(t *testing.T, flags ...string) {
	tmp, exe := buildObjdump(t)
	defer os.RemoveAll(tmp)

	hello := filepath.Join(tmp, "hello.exe")
	args := []string{"build", "-o", hello}
	args = append(args, flags...)
	args = append(args, "testdata/fmthello.go")
	out, err := exec.Command("go", args...).CombinedOutput()
	if err != nil {
		t.Fatalf("go build fmthello.go: %v\n%s", err, out)
	}
	need := []string{
		"fmthello.go:6",
		"TEXT main.main(SB)",
	}
	switch runtime.GOARCH {
	case "amd64", "386":
		need = append(need, x86Need...)
	case "arm":
		need = append(need, armNeed...)
	}

	out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
	if err != nil {
		t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
	}

	text := string(out)
	ok := true
	for _, s := range need {
		if !strings.Contains(text, s) {
			t.Errorf("disassembly missing '%s'", s)
			ok = false
		}
	}
	if !ok {
		t.Logf("full disassembly:\n%s", text)
	}
}

func TestDisasm(t *testing.T) {
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, issue 9039", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	testDisasm(t)
}

func TestDisasmExtld(t *testing.T) {
	switch runtime.GOOS {
	case "plan9", "windows":
		t.Skipf("skipping on %s", runtime.GOOS)
	}
	switch runtime.GOARCH {
	case "ppc64", "ppc64le":
		t.Skipf("skipping on %s, no support for external linking, issue 9038", runtime.GOARCH)
	case "arm64":
		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
	}
	// TODO(jsing): Reenable once openbsd/arm has external linking support.
	if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" {
		t.Skip("skipping on openbsd/arm, no support for external linking, issue 10619")
	}
	if !build.Default.CgoEnabled {
		t.Skip("skipping because cgo is not enabled")
	}
	testDisasm(t, "-ldflags=-linkmode=external")
}
<|endoftext|>


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

package oidc

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	gooidc "github.com/coreos/go-oidc"
	"github.com/goharbor/harbor/src/common/models"
	"github.com/goharbor/harbor/src/common/utils/log"
	"github.com/goharbor/harbor/src/core/config"
	"golang.org/x/oauth2"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

const googleEndpoint = "https://accounts.google.com"

type providerHelper struct {
	sync.Mutex
	ep       endpoint
	instance atomic.Value
	setting  atomic.Value
}

type endpoint struct {
	url        string
	VerifyCert bool
}

func (p *providerHelper) get() (*gooidc.Provider, error) {
	if p.instance.Load() != nil {
		s := p.setting.Load().(models.OIDCSetting)
		if s.Endpoint != p.ep.url || s.VerifyCert != p.ep.VerifyCert { // relevant settings have changed, need to re-create provider.
			if err := p.create(); err != nil {
				return nil, err
			}
		}
	} else {
		p.Lock()
		defer p.Unlock()
		if p.instance.Load() == nil {
			if err := p.reload(); err != nil {
				return nil, err
			}
			if err := p.create(); err != nil {
				return nil, err
			}
			go func() {
				for {
					if err := p.reload(); err != nil {
						log.Warningf("Failed to refresh configuration, error: %v", err)
					}
					time.Sleep(3 * time.Second)
				}
			}()
		}
	}
	return p.instance.Load().(*gooidc.Provider), nil
}

func (p *providerHelper) reload() error {
	conf, err := config.OIDCSetting()
	if err != nil {
		return fmt.Errorf("failed to load OIDC setting: %v", err)
	}
	p.setting.Store(*conf)
	return nil
}

func (p *providerHelper) create() error {
	if p.setting.Load() == nil {
		return errors.New("the configuration is not loaded")
	}
	s := p.setting.Load().(models.OIDCSetting)
	ctx := clientCtx(context.Background(), s.VerifyCert)
	provider, err := gooidc.NewProvider(ctx, s.Endpoint)
	if err != nil {
		return fmt.Errorf("failed to create OIDC provider, error: %v", err)
	}
	p.instance.Store(provider)
	p.ep = endpoint{
		url:        s.Endpoint,
		VerifyCert: s.VerifyCert,
	}
	return nil
}

var provider = &providerHelper{}

var insecureTransport = &http.Transport{
	TLSClientConfig: &tls.Config{
		InsecureSkipVerify: true,
	},
}

// Token wraps the attributes of a oauth2 token plus the attribute of ID token
type Token struct {
	oauth2.Token
	IDToken string `json:"id_token"`
}

func getOauthConf() (*oauth2.Config, error) {
	p, err := provider.get()
	if err != nil {
		return nil, err
	}
	setting := provider.setting.Load().(models.OIDCSetting)
	scopes := []string{}
	for _, sc := range setting.Scope {
		if strings.HasPrefix(p.Endpoint().AuthURL, googleEndpoint) && sc == gooidc.ScopeOfflineAccess {
			log.Warningf("Dropped unsupported scope: %s ", sc)
			continue
		}
		scopes = append(scopes, sc)
	}
	return &oauth2.Config{
		ClientID:     setting.ClientID,
		ClientSecret: setting.ClientSecret,
		Scopes:       scopes,
		RedirectURL:  setting.RedirectURL,
		Endpoint:     p.Endpoint(),
	}, nil
}

// AuthCodeURL returns the URL for OIDC provider's consent page.  The state should be verified when user is redirected
// back to Harbor.
func AuthCodeURL(state string) (string, error) {
	conf, err := getOauthConf()
	if err != nil {
		log.Errorf("Failed to get OAuth configuration, error: %v", err)
		return "", err
	}
	if strings.HasPrefix(conf.Endpoint.AuthURL, googleEndpoint) { // make sure the refresh token will be returned
		return conf.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent")), nil
	}
	return conf.AuthCodeURL(state), nil
}

// ExchangeToken get the token from token provider via the code
func ExchangeToken(ctx context.Context, code string) (*Token, error) {
	oauth, err := getOauthConf()
	if err != nil {
		log.Errorf("Failed to get OAuth configuration, error: %v", err)
		return nil, err
	}
	setting := provider.setting.Load().(models.OIDCSetting)
	ctx = clientCtx(ctx, setting.VerifyCert)
	oauthToken, err := oauth.Exchange(ctx, code)
	if err != nil {
		return nil, err
	}
	return &Token{Token: *oauthToken, IDToken: oauthToken.Extra("id_token").(string)}, nil
}

// VerifyToken verifies the ID token based on the OIDC settings
func VerifyToken(ctx context.Context, rawIDToken string) (*gooidc.IDToken, error) {
	p, err := provider.get()
	if err != nil {
		return nil, err
	}
	verifier := p.Verifier(&gooidc.Config{ClientID: provider.setting.Load().(models.OIDCSetting).ClientID})
	setting := provider.setting.Load().(models.OIDCSetting)
	ctx = clientCtx(ctx, setting.VerifyCert)
	return verifier.Verify(ctx, rawIDToken)
}

func clientCtx(ctx context.Context, verifyCert bool) context.Context {
	var client *http.Client
	if !verifyCert {
		client = &http.Client{
			Transport: insecureTransport,
		}
	} else {
		client = &http.Client{}
	}
	return gooidc.ClientContext(ctx, client)
}

// RefreshToken refreshes the token passed in parameter, and return the new token.
func RefreshToken(ctx context.Context, token *Token) (*Token, error) {
	oauth, err := getOauthConf()
	if err != nil {
		log.Errorf("Failed to get OAuth configuration, error: %v", err)
		return nil, err
	}
	setting := provider.setting.Load().(models.OIDCSetting)
	ctx = clientCtx(ctx, setting.VerifyCert)
	ts := oauth.TokenSource(ctx, &token.Token)
	t, err := ts.Token()
	if err != nil {
		return nil, err
	}
	return &Token{Token: *t, IDToken: t.Extra("id_token").(string)}, nil
}
<commit_msg>Make sure panic is not thrown when refresh token<commit_after>// Copyright 2018 Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oidc

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	gooidc "github.com/coreos/go-oidc"
	"github.com/goharbor/harbor/src/common/models"
	"github.com/goharbor/harbor/src/common/utils/log"
	"github.com/goharbor/harbor/src/core/config"
	"golang.org/x/oauth2"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

const googleEndpoint = "https://accounts.google.com"

type providerHelper struct {
	sync.Mutex
	ep       endpoint
	instance atomic.Value
	setting  atomic.Value
}

type endpoint struct {
	url        string
	VerifyCert bool
}

func (p *providerHelper) get() (*gooidc.Provider, error) {
	if p.instance.Load() != nil {
		s := p.setting.Load().(models.OIDCSetting)
		if s.Endpoint != p.ep.url || s.VerifyCert != p.ep.VerifyCert { // relevant settings have changed, need to re-create provider.
			if err := p.create(); err != nil {
				return nil, err
			}
		}
	} else {
		p.Lock()
		defer p.Unlock()
		if p.instance.Load() == nil {
			if err := p.reload(); err != nil {
				return nil, err
			}
			if err := p.create(); err != nil {
				return nil, err
			}
			go func() {
				for {
					if err := p.reload(); err != nil {
						log.Warningf("Failed to refresh configuration, error: %v", err)
					}
					time.Sleep(3 * time.Second)
				}
			}()
		}
	}
	return p.instance.Load().(*gooidc.Provider), nil
}

func (p *providerHelper) reload() error {
	conf, err := config.OIDCSetting()
	if err != nil {
		return fmt.Errorf("failed to load OIDC setting: %v", err)
	}
	p.setting.Store(*conf)
	return nil
}

func (p *providerHelper) create() error {
	if p.setting.Load() == nil {
		return errors.New("the configuration is not loaded")
	}
	s := p.setting.Load().(models.OIDCSetting)
	ctx := clientCtx(context.Background(), s.VerifyCert)
	provider, err := gooidc.NewProvider(ctx, s.Endpoint)
	if err != nil {
		return fmt.Errorf("failed to create OIDC provider, error: %v", err)
	}
	p.instance.Store(provider)
	p.ep = endpoint{
		url:        s.Endpoint,
		VerifyCert: s.VerifyCert,
	}
	return nil
}

var provider = &providerHelper{}

var insecureTransport = &http.Transport{
	TLSClientConfig: &tls.Config{
		InsecureSkipVerify: true,
	},
}

// Token wraps the attributes of a oauth2 token plus the attribute of ID token
type Token struct {
	oauth2.Token
	IDToken string `json:"id_token"`
}

func getOauthConf() (*oauth2.Config, error) {
	p, err := provider.get()
	if err != nil {
		return nil, err
	}
	setting := provider.setting.Load().(models.OIDCSetting)
	scopes := []string{}
	for _, sc := range setting.Scope {
		if strings.HasPrefix(p.Endpoint().AuthURL, googleEndpoint) && sc == gooidc.ScopeOfflineAccess {
			log.Warningf("Dropped unsupported scope: %s ", sc)
			continue
		}
		scopes = append(scopes, sc)
	}
	return &oauth2.Config{
		ClientID:     setting.ClientID,
		ClientSecret: setting.ClientSecret,
		Scopes:       scopes,
		RedirectURL:  setting.RedirectURL,
		Endpoint:     p.Endpoint(),
	}, nil
}

// AuthCodeURL returns the URL for OIDC provider's consent page.  The state should be verified when user is redirected
// back to Harbor.
func AuthCodeURL(state string) (string, error) {
	conf, err := getOauthConf()
	if err != nil {
		log.Errorf("Failed to get OAuth configuration, error: %v", err)
		return "", err
	}
	if strings.HasPrefix(conf.Endpoint.AuthURL, googleEndpoint) { // make sure the refresh token will be returned
		return conf.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent")), nil
	}
	return conf.AuthCodeURL(state), nil
}

// ExchangeToken get the token from token provider via the code
func ExchangeToken(ctx context.Context, code string) (*Token, error) {
	oauth, err := getOauthConf()
	if err != nil {
		log.Errorf("Failed to get OAuth configuration, error: %v", err)
		return nil, err
	}
	setting := provider.setting.Load().(models.OIDCSetting)
	ctx = clientCtx(ctx, setting.VerifyCert)
	oauthToken, err := oauth.Exchange(ctx, code)
	if err != nil {
		return nil, err
	}
	return &Token{Token: *oauthToken, IDToken: oauthToken.Extra("id_token").(string)}, nil
}

// VerifyToken verifies the ID token based on the OIDC settings
func VerifyToken(ctx context.Context, rawIDToken string) (*gooidc.IDToken, error) {
	p, err := provider.get()
	if err != nil {
		return nil, err
	}
	verifier := p.Verifier(&gooidc.Config{ClientID: provider.setting.Load().(models.OIDCSetting).ClientID})
	setting := provider.setting.Load().(models.OIDCSetting)
	ctx = clientCtx(ctx, setting.VerifyCert)
	return verifier.Verify(ctx, rawIDToken)
}

func clientCtx(ctx context.Context, verifyCert bool) context.Context {
	var client *http.Client
	if !verifyCert {
		client = &http.Client{
			Transport: insecureTransport,
		}
	} else {
		client = &http.Client{}
	}
	return gooidc.ClientContext(ctx, client)
}

// RefreshToken refreshes the token passed in parameter, and return the new token.
func RefreshToken(ctx context.Context, token *Token) (*Token, error) {
	oauth, err := getOauthConf()
	if err != nil {
		log.Errorf("Failed to get OAuth configuration, error: %v", err)
		return nil, err
	}
	setting := provider.setting.Load().(models.OIDCSetting)
	ctx = clientCtx(ctx, setting.VerifyCert)
	ts := oauth.TokenSource(ctx, &token.Token)
	t, err := ts.Token()
	if err != nil {
		return nil, err
	}
	it, ok := t.Extra("id_token").(string)
	if !ok {
		return nil, fmt.Errorf("failed to get id_token from refresh response")
	}
	return &Token{Token: *t, IDToken: it}, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package activity

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/documize/community/domain"
	"github.com/documize/community/domain/store"
	"github.com/documize/community/model/activity"
	"github.com/pkg/errors"
)

// Store provides data access to user activity information.
type Store struct {
	store.Context
	store.ActivityStorer
}

// RecordUserActivity logs user initiated data changes.
func (s Store) RecordUserActivity(ctx domain.RequestContext, activity activity.UserActivity) (err error) {
	activity.OrgID = ctx.OrgID
	activity.UserID = ctx.UserID
	activity.Created = time.Now().UTC()

	_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_user_activity (c_orgid, c_userid, c_spaceid, c_docid, c_sectionid, c_sourcetype, c_activitytype, c_metadata, c_created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
		activity.OrgID, activity.UserID, activity.SpaceID, activity.DocumentID, activity.SectionID, activity.SourceType, activity.ActivityType, activity.Metadata, activity.Created)

	if err != nil {
		err = errors.Wrap(err, "execute record user activity")
	}

	return
}

// GetDocumentActivity returns the metadata for a specified document.
func (s Store) GetDocumentActivity(ctx domain.RequestContext, id string) (a []activity.DocumentActivity, err error) {
	qry := s.Bind(`SELECT a.id, DATE(a.c_created) AS created, a.c_orgid AS orgid,
        COALESCE(a.c_userid, '') AS userid, a.c_spaceid AS spaceid,
        a.c_docid AS documentid, a.c_sectionid AS sectionid, a.c_activitytype AS activitytype,
        a.c_metadata AS metadata,
		COALESCE(u.c_firstname, 'Anonymous') AS firstname, COALESCE(u.c_lastname, 'Viewer') AS lastname,
		COALESCE(p.c_name, '') AS sectionname
		FROM dmz_user_activity a
		LEFT JOIN dmz_user u ON a.c_userid=u.c_refid
		LEFT JOIN dmz_section p ON a.c_sectionid=p.c_refid
		WHERE a.c_orgid=? AND a.c_docid=?
		AND a.c_userid != '0' AND a.c_userid != ''
		ORDER BY a.c_created DESC`)

	err = s.Runtime.Db.Select(&a, qry, ctx.OrgID, id)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, "select document user activity")
	}

	if len(a) == 0 {
		a = []activity.DocumentActivity{}
	}

	return
}

// DeleteDocumentChangeActivity removes all entries for document changes (add, remove, update).
func (s Store) DeleteDocumentChangeActivity(ctx domain.RequestContext, documentID string) (rows int64, err error) {
	rows, err = s.DeleteWhere(ctx.Transaction,
		fmt.Sprintf("DELETE FROM dmz_user_activity WHERE c_orgid='%s' AND c_docid='%s' AND (c_activitytype=1 OR c_activitytype=2 OR c_activitytype=3 OR c_activitytype=4 OR c_activitytype=7)", ctx.OrgID, documentID))

	return
}
<commit_msg>SQL Server per doc activity report<commit_after>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package activity

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/documize/community/domain"
	"github.com/documize/community/domain/store"
	"github.com/documize/community/model/activity"
	"github.com/pkg/errors"
)

// Store provides data access to user activity information.
type Store struct {
	store.Context
	store.ActivityStorer
}

// RecordUserActivity logs user initiated data changes.
func (s Store) RecordUserActivity(ctx domain.RequestContext, activity activity.UserActivity) (err error) {
	activity.OrgID = ctx.OrgID
	activity.UserID = ctx.UserID
	activity.Created = time.Now().UTC()

	_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_user_activity (c_orgid, c_userid, c_spaceid, c_docid, c_sectionid, c_sourcetype, c_activitytype, c_metadata, c_created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
		activity.OrgID, activity.UserID, activity.SpaceID, activity.DocumentID, activity.SectionID, activity.SourceType, activity.ActivityType, activity.Metadata, activity.Created)

	if err != nil {
		err = errors.Wrap(err, "execute record user activity")
	}

	return
}

// GetDocumentActivity returns the metadata for a specified document.
func (s Store) GetDocumentActivity(ctx domain.RequestContext, id string) (a []activity.DocumentActivity, err error) {
	qry := s.Bind(`SELECT a.id, a.c_created AS created, a.c_orgid AS orgid,
        COALESCE(a.c_userid, '') AS userid, a.c_spaceid AS spaceid,
        a.c_docid AS documentid, a.c_sectionid AS sectionid, a.c_activitytype AS activitytype,
        a.c_metadata AS metadata,
		COALESCE(u.c_firstname, 'Anonymous') AS firstname, COALESCE(u.c_lastname, 'Viewer') AS lastname,
		COALESCE(p.c_name, '') AS sectionname
		FROM dmz_user_activity a
		LEFT JOIN dmz_user u ON a.c_userid=u.c_refid
		LEFT JOIN dmz_section p ON a.c_sectionid=p.c_refid
		WHERE a.c_orgid=? AND a.c_docid=?
		AND a.c_userid != '0' AND a.c_userid != ''
		ORDER BY a.c_created DESC`)

	err = s.Runtime.Db.Select(&a, qry, ctx.OrgID, id)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, "select document user activity")
	}

	if len(a) == 0 {
		a = []activity.DocumentActivity{}
	}

	return
}

// DeleteDocumentChangeActivity removes all entries for document changes (add, remove, update).
func (s Store) DeleteDocumentChangeActivity(ctx domain.RequestContext, documentID string) (rows int64, err error) {
	rows, err = s.DeleteWhere(ctx.Transaction,
		fmt.Sprintf("DELETE FROM dmz_user_activity WHERE c_orgid='%s' AND c_docid='%s' AND (c_activitytype=1 OR c_activitytype=2 OR c_activitytype=3 OR c_activitytype=4 OR c_activitytype=7)", ctx.OrgID, documentID))

	return
}
<|endoftext|>


<commit_before>package gateway

import (
	"errors"
	"net/http"
	"net/http/httputil"
	"time"

	"code.cloudfoundry.org/lager"
)

const (
	DEFAULT_TIMEOUT = 1000 * time.Millisecond
)

var (
	emptyBackendList = errors.New("Backends cannot be empty.")
)

//go:generate counterfeiter . ReverseProxy
//go:generate counterfeiter . ReverseProxyCreator

type ReverseProxyCreator interface {
	Create(lager.Logger, ReverseProxySpec) (ReverseProxy, error)
}

type ReverseProxy interface {
	ServeHTTP(http.ResponseWriter, *http.Request)
}

type ReverseProxySpec struct {
	Host   string
	Backends []string
	// Timeout in Milliseconds
	Timeout time.Duration
}

type reverseProxyCreator struct{}

func NewReverseProxyCreator() *reverseProxyCreator {
	return &reverseProxyCreator{}
}

func (rpc *reverseProxyCreator) Create(logger lager.Logger, spec ReverseProxySpec) (ReverseProxy, error) {
	log := logger.Session("reverse-proxy-creator-create")
	log.Info("start", lager.Data{"spec": spec})
	defer log.Info("end")

	if len(spec.Backends) == 0 {
		return nil, emptyBackendList
	}

	timeout := DEFAULT_TIMEOUT
	if spec.Timeout > 0 {
		timeout = spec.Timeout
	}

	return &reverseProxy{
		spec: spec,
		rp: &httputil.ReverseProxy{
			Director:  director(logger, spec),
			Transport: roundTripper(logger, timeout),
		},
	}, nil
}

type reverseProxy struct {
	spec ReverseProxySpec
	rp   *httputil.ReverseProxy
}

func (n *reverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	n.rp.ServeHTTP(rw, req)
}
<commit_msg>Update default timeout to 10 seconds<commit_after>package gateway

import (
	"errors"
	"net/http"
	"net/http/httputil"
	"time"

	"code.cloudfoundry.org/lager"
)

const (
	DEFAULT_TIMEOUT = 10000 * time.Millisecond
)

var (
	emptyBackendList = errors.New("Backends cannot be empty.")
)

//go:generate counterfeiter . ReverseProxy
//go:generate counterfeiter . ReverseProxyCreator

type ReverseProxyCreator interface {
	Create(lager.Logger, ReverseProxySpec) (ReverseProxy, error)
}

type ReverseProxy interface {
	ServeHTTP(http.ResponseWriter, *http.Request)
}

type ReverseProxySpec struct {
	Host     string
	Backends []string
	// Timeout in Milliseconds
	Timeout time.Duration
}

type reverseProxyCreator struct{}

func NewReverseProxyCreator() *reverseProxyCreator {
	return &reverseProxyCreator{}
}

func (rpc *reverseProxyCreator) Create(logger lager.Logger, spec ReverseProxySpec) (ReverseProxy, error) {
	log := logger.Session("reverse-proxy-creator-create")
	log.Info("start", lager.Data{"spec": spec})
	defer log.Info("end")

	if len(spec.Backends) == 0 {
		return nil, emptyBackendList
	}

	timeout := DEFAULT_TIMEOUT
	if spec.Timeout > 0 {
		timeout = spec.Timeout
	}

	return &reverseProxy{
		spec: spec,
		rp: &httputil.ReverseProxy{
			Director:  director(logger, spec),
			Transport: roundTripper(logger, timeout),
		},
	}, nil
}

type reverseProxy struct {
	spec ReverseProxySpec
	rp   *httputil.ReverseProxy
}

func (n *reverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	n.rp.ServeHTTP(rw, req)
}
<|endoftext|>


<commit_before>package main

import "testing"

// TestGenerate tests that we can still generate the list, to catch
// if anything changes on Chromium side.
func TestGenerate(t *testing.T) {
	sites, err := Get()
	if err != nil {
		t.Fatal(err)
	}
	// 2019-05-01 list was 69567 domains long.
	if len(sites) < 50000 {
		t.Errorf("too few sites: %v", len(sites))
	}
	domains := map[string]bool{}
	for _, e := range sites {
		domains[e.Name] = e.IncludeSubDomains
	}
	// Domains we expect in the list and to include subdomains.
	for _, d := range []string{
		"accounts.google.com",
		"login.yahoo.com",
	} {
		include, ok := domains[d]
		if !ok {
			t.Errorf("not in the list: %v", d)
		} else if !include {
			t.Errorf("does not include subdomains: %v", d)
		}
	}
}
<commit_msg>generate: preload test: fix file perms<commit_after>package main

import "testing"

// TestGenerate tests that we can still generate the list, to catch
// if anything changes on Chromium side.
func TestGenerate(t *testing.T) {
	sites, err := Get()
	if err != nil {
		t.Fatal(err)
	}
	// 2019-05-01 list was 69567 domains long.
	if len(sites) < 50000 {
		t.Errorf("too few sites: %v", len(sites))
	}
	domains := map[string]bool{}
	for _, e := range sites {
		domains[e.Name] = e.IncludeSubDomains
	}
	// Domains we expect in the list and to include subdomains.
	for _, d := range []string{
		"accounts.google.com",
		"login.yahoo.com",
	} {
		include, ok := domains[d]
		if !ok {
			t.Errorf("not in the list: %v", d)
		} else if !include {
			t.Errorf("does not include subdomains: %v", d)
		}
	}
}
<|endoftext|>


<commit_before>package security

import (
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/nu7hatch/gouuid"
)

/** Catches 500 errors and logs them to the display with a UUID while returning
a nice result for the user.
**/
func FiveHundredHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		writerWrapper := NewErrorMaskingResponseWriter(writer)
		h.ServeHTTP(&writerWrapper, request)
	})
}

// NewErrorMaskingResponseWriter creates an error masking response writer that logs
// 500 errors to the log files and returns a reference number to the user so we hide
// internal errors
func NewErrorMaskingResponseWriter(writer http.ResponseWriter) ErrorMaskingResponseWriter {
	u, _ := uuid.NewV4()
	uu := u.String()
	return ErrorMaskingResponseWriter{ResponseWriter: writer, responseCode: 200, uuid: uu}
}

type ErrorMaskingResponseWriter struct {
	http.ResponseWriter
	responseCode  int
	uuid          string
	shouldCapture bool
}

// basic wrapper
func (e *ErrorMaskingResponseWriter) Header() http.Header {
	return e.ResponseWriter.Header()
}

// Catch 500 errors and supress them
func (e *ErrorMaskingResponseWriter) WriteHeader(responseCode int) {
	e.responseCode = responseCode
	e.ResponseWriter.WriteHeader(responseCode)

	if e.responseCode >= 500 && e.responseCode < 600 {
		e.ResponseWriter.Write([]byte("Oops, something messed up, if you have questions contact us with the id: " + e.uuid))
		e.shouldCapture = true
	}
}

func (e *ErrorMaskingResponseWriter) Write(body []byte) (int, error) {
	if e.shouldCapture {
		fields := log.Fields{"uuid": e.uuid, "response code": e.responseCode}
		logger := log.WithFields(fields)
		logger.Warningf(string(body))

		// we "wrote" the whole thing
		return len(body), nil
	} else {
		return e.ResponseWriter.Write(body)
	}
}
<commit_msg>Allow hijacking from the 500 wrapper<commit_after>package security

import (
	"bufio"
	"errors"
	"net"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/nu7hatch/gouuid"
)

/** Catches 500 errors and logs them to the display with a UUID while returning
a nice result for the user.
**/
func FiveHundredHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		writerWrapper := NewErrorMaskingResponseWriter(writer)
		h.ServeHTTP(&writerWrapper, request)
	})
}

// NewErrorMaskingResponseWriter creates an error masking response writer that logs
// 500 errors to the log files and returns a reference number to the user so we hide
// internal errors
func NewErrorMaskingResponseWriter(writer http.ResponseWriter) ErrorMaskingResponseWriter {
	u, _ := uuid.NewV4()
	uu := u.String()
	return ErrorMaskingResponseWriter{ResponseWriter: writer, responseCode: 200, uuid: uu}
}

type ErrorMaskingResponseWriter struct {
	http.ResponseWriter
	responseCode  int
	uuid          string
	shouldCapture bool
}

// basic wrapper
func (e *ErrorMaskingResponseWriter) Header() http.Header {
	return e.ResponseWriter.Header()
}

// Catch 500 errors and supress them
func (e *ErrorMaskingResponseWriter) WriteHeader(responseCode int) {
	e.responseCode = responseCode
	e.ResponseWriter.WriteHeader(responseCode)

	if e.responseCode >= 500 && e.responseCode < 600 {
		e.ResponseWriter.Write([]byte("Oops, something messed up, if you have questions contact us with the id: " + e.uuid))
		e.shouldCapture = true
	}
}

func (e *ErrorMaskingResponseWriter) Write(body []byte) (int, error) {
	if e.shouldCapture {
		fields := log.Fields{"uuid": e.uuid, "response code": e.responseCode}
		logger := log.WithFields(fields)
		logger.Warningf(string(body))

		// we "wrote" the whole thing
		return len(body), nil
	} else {
		return e.ResponseWriter.Write(body)
	}
}

func (e *ErrorMaskingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	hj, ok := e.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, errors.New("Hijacking not supported")
	}
	return hj.Hijack()
}
<|endoftext|>