<commit_before>package structflag

import (
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/ogier/pflag"
	reflection "github.com/ungerik/go-reflection"
)

// Flags is the minimal interface structflag needs to work.
// It is a subset of flag.FlagSet
type Flags interface {
	Args() []string
	Parse(arguments []string) error
	PrintDefaults()

	BoolVar(p *bool, name string, value bool, usage string)
	DurationVar(p *time.Duration, name string, value time.Duration, usage string)
	Float64Var(p *float64, name string, value float64, usage string)
	Int64Var(p *int64, name string, value int64, usage string)
	IntVar(p *int, name string, value int, usage string)
	StringVar(p *string, name string, value string, usage string)
	Uint64Var(p *uint64, name string, value uint64, usage string)
	UintVar(p *uint, name string, value uint, usage string)
	Var(value pflag.Value, name string, usage string)
}

// FlagsP supports github.com/ogier/pflag
type FlagsP interface {
	Flags

	BoolVarP(p *bool, name, shorthand string, value bool, usage string)
	DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string)
	Float64VarP(p *float64, name, shorthand string, value float64, usage string)
	Int64VarP(p *int64, name, shorthand string, value int64, usage string)
	IntVarP(p *int, name, shorthand string, value int, usage string)
	StringVarP(p *string, name, shorthand string, value string, usage string)
	Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string)
	UintVarP(p *uint, name, shorthand string, value uint, usage string)
	VarP(value pflag.Value, name, shorthand string, usage string)
}

type flagSetColorOutputWrapper struct {
}

func (*flagSetColorOutputWrapper) Write(p []byte) (n int, err error) {
	// return Output.Write(p)
	written := 0
	lines := strings.Split(strings.TrimSuffix(string(p), "\n"), "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "  -") {
			n, err = FlagUsageColor.Fprintln(Output, line)
		} else {
			n, err = FlagDescriptionColor.Fprintln(Output, line)
		}
		written += n
	}
	return written, err
}

var (
	flagSetColorOutput flagSetColorOutputWrapper

	// Output used for printing usage
	Output io.Writer = os.Stderr

	// FlagUsageColor is the color in which the
	// flag usage will be printed on the screen.
	FlagUsageColor = color.New(color.FgHiGreen)

	// FlagDescriptionColor is the color in which the
	// flag usage description will be printed on the screen.
	FlagDescriptionColor = color.New(color.FgGreen)

	// AppName is the name of the application, defaults to os.Args[0]
	AppName = os.Args[0]

	PrintUsageIntro = PrintCommandsUsageIntro

	// OnParseError defines the behaviour if there is an
	// error while parsing the flags.
	// See https://golang.org/pkg/flag/#ErrorHandling
	OnParseError = pflag.ExitOnError

	// NewFlags returns new Flags, defaults to flag.NewFlagSet(AppName, OnParseError).
	NewFlags = func() Flags {
		flagSet := pflag.NewFlagSet(AppName, OnParseError)
		flagSet.Usage = PrintUsage
		flagSet.SetOutput(&flagSetColorOutput)
		return flagSet
	}

	flags Flags
)

var (
	// NameTag is the struct tag used to overwrite
	// the struct field name as flag name.
	// Struct fields with NameTag of "-" will be ignored.
	NameTag = "flag"

	// ShorthandTag is the struct tag used to define
	// the possix shorthand command line argument.
	ShorthandTag = "short"

	// UsageTag is the struct tag used to give
	// the usage description of a flag
	UsageTag = "usage"

	// DefaultTag is the struct tag used to
	// define the default value for the field
	// (if that default value is different from the zero value)
	DefaultTag = "default"

	// NameFunc is called as last operation for every flag name
	NameFunc = func(name string) string { return name }
)

var (
	pflagValueType   = reflect.TypeOf((*pflag.Value)(nil)).Elem()
	timeDurationType = reflect.TypeOf(time.Duration(0))
)

func getOrCreateFlags() Flags {
	if flags == nil {
		flags = NewFlags()
	}
	return flags
}

// StructVar defines the fields of a struct as flags.
// structPtr must be a pointer to a struct.
// Anonoymous embedded fields are flattened.
// Struct fields with NameTag of "-" will be ignored.
func StructVar(structPtr interface{}) {
	structVar(structPtr, getOrCreateFlags(), true)
}

func structVar(structPtr interface{}, flags Flags, fieldValuesAsDefault bool) {
	flagsp, _ := flags.(FlagsP)
	var err error
	for _, f := range reflection.FlatExportedStructFields(structPtr) {
		name := f.Field.Tag.Get(NameTag)
		if name == "-" {
			continue
		}
		if name == "" {
			name = f.Field.Name
		}
		name = NameFunc(name)

		shorthand, hasShorthand := f.Field.Tag.Lookup(ShorthandTag)
		hasShorthand = hasShorthand && (flagsp != nil)

		usage := f.Field.Tag.Get(UsageTag)

		if f.Field.Type.Implements(pflagValueType) {
			val := f.Value.Addr().Interface().(pflag.Value)
			if hasShorthand {
				flagsp.VarP(val, name, shorthand, usage)
			} else {
				flags.Var(val, name, usage)
			}
			continue
		}

		defaultStr, hasDefault := f.Field.Tag.Lookup(DefaultTag)

		fieldType := f.Field.Type
		fieldValue := f.Value

		isPtr := fieldType.Kind() == reflect.Ptr
		if isPtr {
			if fieldValue.IsNil() {
				err = fmt.Errorf("pointer struct field '%s' must not be nil", f.Field.Name)
				panic(err)
			}
			fieldType = fieldType.Elem()
			fieldValue = fieldValue.Elem()
			fieldValuesAsDefault = !hasDefault
		}

		if fieldType == timeDurationType {
			var value time.Duration
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(time.Duration)
			} else if hasDefault {
				value, err = time.ParseDuration(defaultStr)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*time.Duration)
			if hasShorthand {
				flagsp.DurationVarP(ptr, name, shorthand, value, usage)
			} else {
				flags.DurationVar(ptr, name, value, usage)
			}
			continue
		}

		switch fieldType.Kind() {
		case reflect.Bool:
			var value bool
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(bool)
			} else if hasDefault {
				value, err = strconv.ParseBool(defaultStr)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*bool)
			if hasShorthand {
				flagsp.BoolVarP(ptr, name, shorthand, value, usage)
			} else {
				flags.BoolVar(ptr, name, value, usage)
			}

		case reflect.Float64:
			var value float64
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(float64)
			} else if hasDefault {
				value, err = strconv.ParseFloat(defaultStr, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*float64)
			if hasShorthand {
				flagsp.Float64VarP(ptr, name, shorthand, value, usage)
			} else {
				flags.Float64Var(ptr, name, value, usage)
			}

		case reflect.Int64:
			var value int64
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(int64)
			} else if hasDefault {
				value, err = strconv.ParseInt(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*int64)
			if hasShorthand {
				flagsp.Int64VarP(ptr, name, shorthand, value, usage)
			} else {
				flags.Int64Var(ptr, name, value, usage)
			}

		case reflect.Int:
			var value int64
			if fieldValuesAsDefault {
				value = int64(fieldValue.Interface().(int))
			} else if hasDefault {
				value, err = strconv.ParseInt(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*int)
			if hasShorthand {
				flagsp.IntVarP(ptr, name, shorthand, int(value), usage)
			} else {
				flags.IntVar(ptr, name, int(value), usage)
			}

		case reflect.String:
			var value string
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(string)
			} else if hasDefault {
				value = defaultStr
			}
			ptr := fieldValue.Addr().Interface().(*string)
			if hasShorthand {
				flagsp.StringVarP(ptr, name, shorthand, value, usage)
			} else {
				flags.StringVar(ptr, name, value, usage)
			}

		case reflect.Uint64:
			var value uint64
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(uint64)
			} else if hasDefault {
				value, err = strconv.ParseUint(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*uint64)
			if hasShorthand {
				flagsp.Uint64VarP(ptr, name, shorthand, value, usage)
			} else {
				flags.Uint64Var(ptr, name, value, usage)
			}

		case reflect.Uint:
			var value uint64
			if fieldValuesAsDefault {
				value = uint64(fieldValue.Interface().(uint))
			} else if hasDefault {
				value, err = strconv.ParseUint(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*uint)
			if hasShorthand {
				flagsp.UintVarP(ptr, name, shorthand, uint(value), usage)
			} else {
				flags.UintVar(ptr, name, uint(value), usage)
			}
		}
	}
}

// Parse parses args, or if no args are given os.Args[1:]
func Parse(args ...string) ([]string, error) {
	return parse(args, getOrCreateFlags())
}

func parse(args []string, flags Flags) ([]string, error) {
	if len(args) == 0 {
		args = os.Args[1:]
	}
	err := flags.Parse(args)
	if err != nil {
		return nil, err
	}
	return flags.Args(), nil
}

func PrintCommandsUsageIntro(output io.Writer) {
	if len(Commands) > 0 {
		fmt.Fprint(Output, "Commands:\n")
		Commands.PrintUsage()
		if flags != nil {
			fmt.Fprint(Output, "Flags:\n")
		}
	}
}

// PrintUsageTo prints a description of all commands and flags of Set and Commands to output
func PrintUsageTo(output io.Writer) {
	if PrintUsageIntro != nil {
		PrintUsageIntro(output)
	}
	if flags != nil {
		flags.PrintDefaults()
	}
}

// PrintUsage prints a description of all commands and flags of Set and Commands to Output
func PrintUsage() {
	PrintUsageTo(Output)
}

// LoadFileAndParseCommandLine loads the configuration from filename
// into structPtr and then parses the command line.
// Every value that is present in command line overwrites the
// value loaded from the configuration file.
// Values not present in the command line won't effect the Values
// loaded from the configuration file.
// If there is an error loading the configuration file,
// then the command line still gets parsed.
// An error where os.IsNotExist(err) == true can be ignored
// if the existence of the configuration file is optional.
func LoadFileAndParseCommandLine(filename string, structPtr interface{}) ([]string, error) {
	// Initialize global variable set with unchanged default values
	// so that a later PrintDefaults() prints the correct default values.
	StructVar(structPtr)

	// Load and unmarshal struct from file
	loadErr := LoadFile(filename, structPtr)

	// Use the existing struct values as defaults for tempSet
	// so that not existing args don't overwrite existing values
	// that have been loaded from the confriguration file
	tempFlags := NewFlags()
	structVar(structPtr, tempFlags, true)
	err := tempFlags.Parse(os.Args[1:])
	if err != nil {
		return nil, err
	}
	return tempFlags.Args(), loadErr
}

// MustLoadFileAndParseCommandLine same as LoadFileAndParseCommandLine but panics on error
func MustLoadFileAndParseCommandLine(filename string, structPtr interface{}) []string {
	args, err := LoadFileAndParseCommandLine(filename, structPtr)
	if err != nil {
		panic(err)
	}
	return args
}

// LoadFileIfExistsAndMustParseCommandLine same as LoadFileAndParseCommandLine but panics on error
func LoadFileIfExistsAndMustParseCommandLine(filename string, structPtr interface{}) []string {
	args, err := LoadFileAndParseCommandLine(filename, structPtr)
	if err != nil && !os.IsNotExist(err) {
		panic(err)
	}
	return args
}

// PrintConfig prints the flattened struct fields from structPtr to Output.
func PrintConfig(structPtr interface{}) {
	for _, f := range reflection.FlatExportedStructFields(structPtr) {
		v := f.Value
		for v.Kind() == reflect.Ptr {
			v = v.Elem()
		}
		fmt.Fprintf(Output, "%s: %v\n", f.Field.Name, v.Interface())
	}
}
<commit_msg>ignore "-test" args<commit_after>package structflag

import (
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/ogier/pflag"

	reflection "github.com/ungerik/go-reflection"
)

// Flags is the minimal interface structflag needs to work.
// It is a subset of flag.FlagSet
type Flags interface {
	Args() []string
	Parse(arguments []string) error
	PrintDefaults()

	BoolVar(p *bool, name string, value bool, usage string)
	DurationVar(p *time.Duration, name string, value time.Duration, usage string)
	Float64Var(p *float64, name string, value float64, usage string)
	Int64Var(p *int64, name string, value int64, usage string)
	IntVar(p *int, name string, value int, usage string)
	StringVar(p *string, name string, value string, usage string)
	Uint64Var(p *uint64, name string, value uint64, usage string)
	UintVar(p *uint, name string, value uint, usage string)
	Var(value pflag.Value, name string, usage string)
}

// FlagsP supports github.com/ogier/pflag
type FlagsP interface {
	Flags

	BoolVarP(p *bool, name, shorthand string, value bool, usage string)
	DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string)
	Float64VarP(p *float64, name, shorthand string, value float64, usage string)
	Int64VarP(p *int64, name, shorthand string, value int64, usage string)
	IntVarP(p *int, name, shorthand string, value int, usage string)
	StringVarP(p *string, name, shorthand string, value string, usage string)
	Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string)
	UintVarP(p *uint, name, shorthand string, value uint, usage string)
	VarP(value pflag.Value, name, shorthand string, usage string)
}

type flagSetColorOutputWrapper struct {
}

func (*flagSetColorOutputWrapper) Write(p []byte) (n int, err error) {
	// return Output.Write(p)
	written := 0
	lines := strings.Split(strings.TrimSuffix(string(p), "\n"), "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "  -") {
			n, err = FlagUsageColor.Fprintln(Output, line)
		} else {
			n, err = FlagDescriptionColor.Fprintln(Output, line)
		}
		written += n
	}
	return written, err
}

var (
	flagSetColorOutput flagSetColorOutputWrapper

	// Output used for printing usage
	Output io.Writer = os.Stderr

	// FlagUsageColor is the color in which the
	// flag usage will be printed on the screen.
	FlagUsageColor = color.New(color.FgHiGreen)

	// FlagDescriptionColor is the color in which the
	// flag usage description will be printed on the screen.
	FlagDescriptionColor = color.New(color.FgGreen)

	// AppName is the name of the application, defaults to os.Args[0]
	AppName = os.Args[0]

	PrintUsageIntro = PrintCommandsUsageIntro

	// OnParseError defines the behaviour if there is an
	// error while parsing the flags.
	// See https://golang.org/pkg/flag/#ErrorHandling
	OnParseError = pflag.ExitOnError

	// NewFlags returns new Flags, defaults to flag.NewFlagSet(AppName, OnParseError).
	NewFlags = func() Flags {
		flagSet := pflag.NewFlagSet(AppName, OnParseError)
		flagSet.Usage = PrintUsage
		flagSet.SetOutput(&flagSetColorOutput)
		return flagSet
	}

	flags Flags
)

var (
	// NameTag is the struct tag used to overwrite
	// the struct field name as flag name.
	// Struct fields with NameTag of "-" will be ignored.
	NameTag = "flag"

	// ShorthandTag is the struct tag used to define
	// the possix shorthand command line argument.
	ShorthandTag = "short"

	// UsageTag is the struct tag used to give
	// the usage description of a flag
	UsageTag = "usage"

	// DefaultTag is the struct tag used to
	// define the default value for the field
	// (if that default value is different from the zero value)
	DefaultTag = "default"

	// NameFunc is called as last operation for every flag name
	NameFunc = func(name string) string { return name }
)

var (
	pflagValueType   = reflect.TypeOf((*pflag.Value)(nil)).Elem()
	timeDurationType = reflect.TypeOf(time.Duration(0))
)

func getOrCreateFlags() Flags {
	if flags == nil {
		flags = NewFlags()
	}
	return flags
}

// StructVar defines the fields of a struct as flags.
// structPtr must be a pointer to a struct.
// Anonoymous embedded fields are flattened.
// Struct fields with NameTag of "-" will be ignored.
func StructVar(structPtr interface{}) {
	structVar(structPtr, getOrCreateFlags(), true)
}

func structVar(structPtr interface{}, flags Flags, fieldValuesAsDefault bool) {
	flagsp, _ := flags.(FlagsP)
	var err error
	for _, f := range reflection.FlatExportedStructFields(structPtr) {
		name := f.Field.Tag.Get(NameTag)
		if name == "-" {
			continue
		}
		if name == "" {
			name = f.Field.Name
		}
		name = NameFunc(name)

		shorthand, hasShorthand := f.Field.Tag.Lookup(ShorthandTag)
		hasShorthand = hasShorthand && (flagsp != nil)

		usage := f.Field.Tag.Get(UsageTag)

		if f.Field.Type.Implements(pflagValueType) {
			val := f.Value.Addr().Interface().(pflag.Value)
			if hasShorthand {
				flagsp.VarP(val, name, shorthand, usage)
			} else {
				flags.Var(val, name, usage)
			}
			continue
		}

		defaultStr, hasDefault := f.Field.Tag.Lookup(DefaultTag)

		fieldType := f.Field.Type
		fieldValue := f.Value

		isPtr := fieldType.Kind() == reflect.Ptr
		if isPtr {
			if fieldValue.IsNil() {
				err = fmt.Errorf("pointer struct field '%s' must not be nil", f.Field.Name)
				panic(err)
			}
			fieldType = fieldType.Elem()
			fieldValue = fieldValue.Elem()
			fieldValuesAsDefault = !hasDefault
		}

		if fieldType == timeDurationType {
			var value time.Duration
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(time.Duration)
			} else if hasDefault {
				value, err = time.ParseDuration(defaultStr)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*time.Duration)
			if hasShorthand {
				flagsp.DurationVarP(ptr, name, shorthand, value, usage)
			} else {
				flags.DurationVar(ptr, name, value, usage)
			}
			continue
		}

		switch fieldType.Kind() {
		case reflect.Bool:
			var value bool
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(bool)
			} else if hasDefault {
				value, err = strconv.ParseBool(defaultStr)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*bool)
			if hasShorthand {
				flagsp.BoolVarP(ptr, name, shorthand, value, usage)
			} else {
				flags.BoolVar(ptr, name, value, usage)
			}

		case reflect.Float64:
			var value float64
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(float64)
			} else if hasDefault {
				value, err = strconv.ParseFloat(defaultStr, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*float64)
			if hasShorthand {
				flagsp.Float64VarP(ptr, name, shorthand, value, usage)
			} else {
				flags.Float64Var(ptr, name, value, usage)
			}

		case reflect.Int64:
			var value int64
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(int64)
			} else if hasDefault {
				value, err = strconv.ParseInt(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*int64)
			if hasShorthand {
				flagsp.Int64VarP(ptr, name, shorthand, value, usage)
			} else {
				flags.Int64Var(ptr, name, value, usage)
			}

		case reflect.Int:
			var value int64
			if fieldValuesAsDefault {
				value = int64(fieldValue.Interface().(int))
			} else if hasDefault {
				value, err = strconv.ParseInt(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*int)
			if hasShorthand {
				flagsp.IntVarP(ptr, name, shorthand, int(value), usage)
			} else {
				flags.IntVar(ptr, name, int(value), usage)
			}

		case reflect.String:
			var value string
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(string)
			} else if hasDefault {
				value = defaultStr
			}
			ptr := fieldValue.Addr().Interface().(*string)
			if hasShorthand {
				flagsp.StringVarP(ptr, name, shorthand, value, usage)
			} else {
				flags.StringVar(ptr, name, value, usage)
			}

		case reflect.Uint64:
			var value uint64
			if fieldValuesAsDefault {
				value = fieldValue.Interface().(uint64)
			} else if hasDefault {
				value, err = strconv.ParseUint(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*uint64)
			if hasShorthand {
				flagsp.Uint64VarP(ptr, name, shorthand, value, usage)
			} else {
				flags.Uint64Var(ptr, name, value, usage)
			}

		case reflect.Uint:
			var value uint64
			if fieldValuesAsDefault {
				value = uint64(fieldValue.Interface().(uint))
			} else if hasDefault {
				value, err = strconv.ParseUint(defaultStr, 0, 64)
				if err != nil {
					panic(err)
				}
			}
			ptr := fieldValue.Addr().Interface().(*uint)
			if hasShorthand {
				flagsp.UintVarP(ptr, name, shorthand, uint(value), usage)
			} else {
				flags.UintVar(ptr, name, uint(value), usage)
			}
		}
	}
}

// Parse parses args, or if no args are given os.Args[1:]
func Parse(args ...string) ([]string, error) {
	return parse(args, getOrCreateFlags())
}

func parse(args []string, flags Flags) ([]string, error) {
	if len(args) == 0 {
		args = os.Args[1:]
	}
	err := flags.Parse(args)
	if err != nil {
		return nil, err
	}
	return flags.Args(), nil
}

func PrintCommandsUsageIntro(output io.Writer) {
	if len(Commands) > 0 {
		fmt.Fprint(Output, "Commands:\n")
		Commands.PrintUsage()
		if flags != nil {
			fmt.Fprint(Output, "Flags:\n")
		}
	}
}

// PrintUsageTo prints a description of all commands and flags of Set and Commands to output
func PrintUsageTo(output io.Writer) {
	if PrintUsageIntro != nil {
		PrintUsageIntro(output)
	}
	if flags != nil {
		flags.PrintDefaults()
	}
}

// PrintUsage prints a description of all commands and flags of Set and Commands to Output
func PrintUsage() {
	PrintUsageTo(Output)
}

// LoadFileAndParseCommandLine loads the configuration from filename
// into structPtr and then parses the command line.
// Every value that is present in command line overwrites the
// value loaded from the configuration file.
// Values not present in the command line won't effect the Values
// loaded from the configuration file.
// If there is an error loading the configuration file,
// then the command line still gets parsed.
// An error where os.IsNotExist(err) == true can be ignored
// if the existence of the configuration file is optional.
func LoadFileAndParseCommandLine(filename string, structPtr interface{}) ([]string, error) {
	// Initialize global variable set with unchanged default values
	// so that a later PrintDefaults() prints the correct default values.
	StructVar(structPtr)

	// Load and unmarshal struct from file
	loadErr := LoadFile(filename, structPtr)

	// Use the existing struct values as defaults for tempSet
	// so that not existing args don't overwrite existing values
	// that have been loaded from the confriguration file
	tempFlags := NewFlags()
	structVar(structPtr, tempFlags, true)

	// If called by a test, then return without parsing args
	// because the "-test" flag syntax is not supported
	if len(os.Args) > 1 && strings.HasPrefix(os.Args[1], "-test") {
		return nil, loadErr
	}

	err := tempFlags.Parse(os.Args[1:])
	if err != nil {
		return nil, err
	}
	return tempFlags.Args(), loadErr
}

// MustLoadFileAndParseCommandLine same as LoadFileAndParseCommandLine but panics on error
func MustLoadFileAndParseCommandLine(filename string, structPtr interface{}) []string {
	args, err := LoadFileAndParseCommandLine(filename, structPtr)
	if err != nil {
		panic(err)
	}
	return args
}

// LoadFileIfExistsAndMustParseCommandLine same as LoadFileAndParseCommandLine but panics on error
func LoadFileIfExistsAndMustParseCommandLine(filename string, structPtr interface{}) []string {
	args, err := LoadFileAndParseCommandLine(filename, structPtr)
	if err != nil && !os.IsNotExist(err) {
		panic(err)
	}
	return args
}

// PrintConfig prints the flattened struct fields from structPtr to Output.
func PrintConfig(structPtr interface{}) {
	for _, f := range reflection.FlatExportedStructFields(structPtr) {
		v := f.Value
		for v.Kind() == reflect.Ptr {
			v = v.Elem()
		}
		fmt.Fprintf(Output, "%s: %v\n", f.Field.Name, v.Interface())
	}
}
<|endoftext|>


<commit_before>package sup

/*
	A `SupervisonFn` is the control code you write to dictate supervisor's behavior.
	This function can spawn tasks, wait around, take orders, spawn more tasks,
	collect task results, etc -- and as long as this function continues, the
	supervisor itself is operational.

	If the `SupervisonFn` panics, the supervisor is in a
	bad state, and all of its children will be killed, and the problem
	reported upwards.
	When the `SupervisonFn` returns, that's the indication that this supervision
	tree will not be assigned any more tasks to babysit, and things will wrap
	up gracefully and the supervisor itself will exit when all children
	have been collected.

	A valid `SupervisonFn` might just spawn one task and return.
	In this case, the supervisor will wait for that child's return, then
	itself return.

	Another valid `SupervisonFn` might spawn a dozen tasks, then select on
	a channel which it responds to by spawning even more tasks.
	In this case, even if all its tasks are done, the supervisor will never
	return until the `SupervisonFn` also returns.  (So, in this scenario,
	you'd probably want to write a "close" channel of sime kind into the
	body of your `SupervisonFn`, so you can tell it when it's time to
	shut down.)

	You should only operate the provided `Supervisor` from within that
	`SupervisonFn` -- there aren't enough mutexes to make that safe, and
	you probably wouldn't like the semantic races and error handling anyway.
	Treat it like another actor: that's what it is.
	(Witnesses are safe to use and pass round anywhere, though.)
*/
type SupervisonFn func(*Supervisor)

func NewSupervisor(superFn SupervisonFn) {
	svr := newSupervisor()
	go svr.actor()
	// TODO more panic-collecting fences around this
	superFn(svr)
	// TODO block for children
	return
}

func (svr *Supervisor) Spawn(fn Task) Witness {
	retCh := make(chan Witness)
	svr.ctrlChan_spawn <- msg_spawn{fn: fn, ret: retCh}
	return <-retCh
}

func (svr *Supervisor) Wait() {
	// TODO svr.doneLatch.Wait()
}
<commit_msg>Pretty sure we declared this just won't be a thing, because we're pushing up blocking behavior ourselves.<commit_after>package sup

/*
	A `SupervisonFn` is the control code you write to dictate supervisor's behavior.
	This function can spawn tasks, wait around, take orders, spawn more tasks,
	collect task results, etc -- and as long as this function continues, the
	supervisor itself is operational.

	If the `SupervisonFn` panics, the supervisor is in a
	bad state, and all of its children will be killed, and the problem
	reported upwards.
	When the `SupervisonFn` returns, that's the indication that this supervision
	tree will not be assigned any more tasks to babysit, and things will wrap
	up gracefully and the supervisor itself will exit when all children
	have been collected.

	A valid `SupervisonFn` might just spawn one task and return.
	In this case, the supervisor will wait for that child's return, then
	itself return.

	Another valid `SupervisonFn` might spawn a dozen tasks, then select on
	a channel which it responds to by spawning even more tasks.
	In this case, even if all its tasks are done, the supervisor will never
	return until the `SupervisonFn` also returns.  (So, in this scenario,
	you'd probably want to write a "close" channel of sime kind into the
	body of your `SupervisonFn`, so you can tell it when it's time to
	shut down.)

	You should only operate the provided `Supervisor` from within that
	`SupervisonFn` -- there aren't enough mutexes to make that safe, and
	you probably wouldn't like the semantic races and error handling anyway.
	Treat it like another actor: that's what it is.
	(Witnesses are safe to use and pass round anywhere, though.)
*/
type SupervisonFn func(*Supervisor)

func NewSupervisor(superFn SupervisonFn) {
	svr := newSupervisor()
	go svr.actor()
	// TODO more panic-collecting fences around this
	superFn(svr)
	// TODO block for children
	return
}

func (svr *Supervisor) Spawn(fn Task) Witness {
	retCh := make(chan Witness)
	svr.ctrlChan_spawn <- msg_spawn{fn: fn, ret: retCh}
	return <-retCh
}
<|endoftext|>


<commit_before>package atbash

import "strings"

const groupSize = 5

func Atbash(str string) (result string) {
	str = strings.ToLower(str)
	offset := 'z' - 'a'
	i := 0
	for _, c := range str {
		switch {
		case c >= 'a' && c < 'z':
			result += string('z' - ((c + 3) % offset))
			i++
		case c == 'z':
			result += string('a')
			i++
		case c >= '0' && c <= '9':
			result += string(c)
			i++
		}
		if i == groupSize {
			result += " "
			i = 0
		}
	}
	result = strings.TrimSpace(result)
	return
}
<commit_msg>simplified atbash-cipher<commit_after>package atbash

import "strings"

const groupSize = 5

func Atbash(str string) (result string) {
	str = strings.ToLower(str)
	offset := 'z' - 'a'
	i := 0
	for _, c := range str {
		switch {
		case c >= 'a' && c <= 'z':
			result += string('a' + offset - (c - 'a'))
			i++
		case c >= '0' && c <= '9':
			result += string(c)
			i++
		}
		if i == groupSize {
			result += " "
			i = 0
		}
	}
	result = strings.TrimSpace(result)
	return
}
<|endoftext|>


<commit_before>// $G $F.go && $L $F.$A  # don't run it - goes forever

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

package Main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch *chan> int) {
  for i := 2; ; i++ {
    >ch = i  // Send 'i' to channel 'ch'.
  }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan< int, out *chan> int, prime int) {
  for {
    i := <in;  // Receive value of new variable 'i' from 'in'.
    if i % prime != 0 {
      >out = i  // Send 'i' to channel 'out'.
    }
  }
}

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
  ch := new(chan int);  // Create a new channel.
  go Generate(ch);  // Start Generate() as a subprocess.
  for {
    prime := <ch;
    printf("%d\n",  prime);
    ch1 := new(chan int);
    go Filter(ch, ch1, prime);
    ch = ch1
  }
}

func Main() {
  Sieve()
}
<commit_msg>- fixed a test - added extra basic type aliases - added select statement<commit_after>// $G $F.go && $L $F.$A  # don't run it - goes forever

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

package Main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch *chan> int) {
  for i := 2; ; i++ {
    >ch = i  // Send 'i' to channel 'ch'.
  }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan< int, out *chan> int, prime int) {
  for {
    i := <in;  // Receive value of new variable 'i' from 'in'.
    if i % prime != 0 {
      >out = i  // Send 'i' to channel 'out'.
    }
  }
}

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
  ch := new(chan int);  // Create a new channel.
  go Generate(ch);  // Start Generate() as a subprocess.
  for {
    prime := <ch;
    print "%d\n",  prime;
    ch1 := new(chan int);
    go Filter(ch, ch1, prime);
    ch = ch1
  }
}

func Main() {
  Sieve()
}
<|endoftext|>


<commit_before>package aws

import "aws-sdk-go/service/s3"
import "github.com/deckarep/golang-set"

type CannedAccessControlType int32
const (
	PublicReadWrite      CannedAccessControlType = 0
	PublicRead      CannedAccessControlType = 1
	Private      CannedAccessControlType = 2
)
const AllUsersUri = "http://acs.amazonaws.com/groups/global/AllUsers"

func GetAcl(resp s3.GetObjectACLOutput) (CannedAccessControlType)  {

	allUsersPermissions := mapset.NewSet()
	for _, value:= range resp.Grants {
		if value.Grantee.URI !=nil && *value.Grantee.URI == AllUsersUri{
			allUsersPermissions.Add(value.Permission)
		}
	}
	read := allUsersPermissions.Contains("READ");
	write := allUsersPermissions.Contains("WRITE");
	if (read && write) {
		return PublicReadWrite;
	} else if (read) {
		return PublicRead;
	} else {
		return Private;
	}
}
<commit_msg>update<commit_after>package aws

import "github.com/ks3sdklib/aws-sdk-go/service/s3"
import "github.com/deckarep/golang-set"

type CannedAccessControlType int32
const (
	PublicReadWrite      CannedAccessControlType = 0
	PublicRead      CannedAccessControlType = 1
	Private      CannedAccessControlType = 2
)
const AllUsersUri = "http://acs.amazonaws.com/groups/global/AllUsers"

func GetAcl(resp s3.GetObjectACLOutput) (CannedAccessControlType)  {

	allUsersPermissions := mapset.NewSet()
	for _, value:= range resp.Grants {
		if value.Grantee.URI !=nil && *value.Grantee.URI == AllUsersUri{
			allUsersPermissions.Add(value.Permission)
		}
	}
	read := allUsersPermissions.Contains("READ");
	write := allUsersPermissions.Contains("WRITE");
	if (read && write) {
		return PublicReadWrite;
	} else if (read) {
		return PublicRead;
	} else {
		return Private;
	}
}
<|endoftext|>


<commit_before>// Copyright ©2018 The go-hep 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 xrootd // import "go-hep.org/x/hep/xrootd"

import (
	"context"
	"fmt"
	"os"
	"sync"

	"go-hep.org/x/hep/xrootd/xrdproto"
	"go-hep.org/x/hep/xrootd/xrdproto/auth"
)

// A Client to xrootd server which allows to send requests and receive responses.
// Concurrent requests are supported.
// Zero value is invalid, Client should be instantiated using NewClient.
type Client struct {
	cancel   context.CancelFunc
	auths    map[string]auth.Auther
	username string
	// initialSessionID is the sessionID of the server which is used as default
	// for all requests that don't specify sessionID explicitly.
	// Any failed request with another sessionID should be redirected to the initialSessionID.
	// See http://xrootd.org/doc/dev45/XRdv310.pdf, page 11 for details.
	initialSessionID string
	mu               sync.RWMutex
	sessions         map[string]*cliSession

	maxRedirections int
}

// Option configures an XRootD client.
type Option func(*Client) error

// WithAuth adds an authentication mechanism to the XRootD client.
// If an authentication mechanism was already registered for that provider,
// it will be silently replaced.
func WithAuth(a auth.Auther) Option {
	return func(client *Client) error {
		return client.addAuth(a)
	}
}

func (client *Client) addAuth(auth auth.Auther) error {
	client.auths[auth.Provider()] = auth
	return nil
}

func (client *Client) initSecurityProviders() {
	for _, provider := range defaultProviders {
		if provider == nil {
			continue
		}
		client.auths[provider.Provider()] = provider
	}
}

// NewClient creates a new xrootd client that connects to the given address using username.
// Options opts configure the client and are applied in the order they were specified.
// When the context expires, a response handling is stopped, however, it is
// necessary to call Cancel to correctly free resources.
func NewClient(ctx context.Context, address string, username string, opts ...Option) (*Client, error) {
	ctx, cancel := context.WithCancel(ctx)

	client := &Client{
		cancel:          cancel,
		auths:           make(map[string]auth.Auther),
		username:        username,
		sessions:        make(map[string]*cliSession),
		maxRedirections: 10,
	}

	client.initSecurityProviders()

	for _, opt := range opts {
		if opt == nil {
			continue
		}
		if err := opt(client); err != nil {
			client.Close()
			return nil, err
		}
	}

	_, err := client.getSession(ctx, address, "")
	if err != nil {
		client.Close()
		return nil, err
	}

	return client, nil
}

// Close closes the connection. Any blocked operation will be unblocked and return error.
func (client *Client) Close() error {
	if client == nil {
		return os.ErrInvalid
	}

	defer client.cancel()
	client.mu.Lock()
	defer client.mu.Unlock()
	var errs []error
	for _, session := range client.sessions {
		err := session.Close()
		if err != nil {
			errs = append(errs, err)
		}
	}
	if errs != nil {
		return fmt.Errorf("xrootd: could not close client: %v", errs)
	}
	return nil
}

// Send sends the request to the server and stores the response inside the resp.
// If the resp is nil, then no response is stored.
// Send returns a session id which identifies the server that provided response.
func (client *Client) Send(ctx context.Context, resp xrdproto.Response, req xrdproto.Request) (string, error) {
	return client.sendSession(ctx, client.initialSessionID, resp, req)
}

func (client *Client) sendSession(ctx context.Context, sessionID string, resp xrdproto.Response, req xrdproto.Request) (string, error) {
	client.mu.RLock()
	session, ok := client.sessions[sessionID]
	client.mu.RUnlock()
	if !ok {
		return "", fmt.Errorf("xrootd: session with id = %q was not found", sessionID)
	}

	redirection, err := session.Send(ctx, resp, req)
	if err != nil {
		return sessionID, err
	}

	for cnt := client.maxRedirections; redirection != nil && cnt > 0; cnt-- {
		sessionID = redirection.Addr
		session, err = client.getSession(ctx, sessionID, redirection.Token)
		if err != nil {
			return sessionID, err
		}
		if fp, ok := req.(xrdproto.FilepathRequest); ok {
			fp.SetOpaque(redirection.Opaque)
		}
		// TODO: we should check if the request contains file handle and re-issue open request in that case.
		redirection, err = session.Send(ctx, resp, req)
		if err != nil {
			return sessionID, err
		}
	}

	if redirection != nil {
		err = fmt.Errorf("xrootd: received %d redirections in a row, aborting request", client.maxRedirections)
	}

	return sessionID, err
}

func (client *Client) getSession(ctx context.Context, address, token string) (*cliSession, error) {
	client.mu.RLock()
	v, ok := client.sessions[address]
	client.mu.RUnlock()
	if ok {
		return v, nil
	}
	client.mu.Lock()
	defer client.mu.Unlock()
	session, err := newSession(ctx, address, client.username, token, client)
	if err != nil {
		return nil, err
	}
	client.sessions[address] = session

	if len(client.initialSessionID) == 0 {
		client.initialSessionID = address
	}
	// TODO: check if initial sessionID should be changed.
	// See http://xrootd.org/doc/dev45/XRdv310.pdf, p. 11 for details.

	return session, nil
}
<commit_msg>xrootd: do not segfault sending data with nil clients<commit_after>// Copyright ©2018 The go-hep 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 xrootd // import "go-hep.org/x/hep/xrootd"

import (
	"context"
	"fmt"
	"os"
	"sync"

	"go-hep.org/x/hep/xrootd/xrdproto"
	"go-hep.org/x/hep/xrootd/xrdproto/auth"
)

// A Client to xrootd server which allows to send requests and receive responses.
// Concurrent requests are supported.
// Zero value is invalid, Client should be instantiated using NewClient.
type Client struct {
	cancel   context.CancelFunc
	auths    map[string]auth.Auther
	username string
	// initialSessionID is the sessionID of the server which is used as default
	// for all requests that don't specify sessionID explicitly.
	// Any failed request with another sessionID should be redirected to the initialSessionID.
	// See http://xrootd.org/doc/dev45/XRdv310.pdf, page 11 for details.
	initialSessionID string
	mu               sync.RWMutex
	sessions         map[string]*cliSession

	maxRedirections int
}

// Option configures an XRootD client.
type Option func(*Client) error

// WithAuth adds an authentication mechanism to the XRootD client.
// If an authentication mechanism was already registered for that provider,
// it will be silently replaced.
func WithAuth(a auth.Auther) Option {
	return func(client *Client) error {
		return client.addAuth(a)
	}
}

func (client *Client) addAuth(auth auth.Auther) error {
	client.auths[auth.Provider()] = auth
	return nil
}

func (client *Client) initSecurityProviders() {
	for _, provider := range defaultProviders {
		if provider == nil {
			continue
		}
		client.auths[provider.Provider()] = provider
	}
}

// NewClient creates a new xrootd client that connects to the given address using username.
// Options opts configure the client and are applied in the order they were specified.
// When the context expires, a response handling is stopped, however, it is
// necessary to call Cancel to correctly free resources.
func NewClient(ctx context.Context, address string, username string, opts ...Option) (*Client, error) {
	ctx, cancel := context.WithCancel(ctx)

	client := &Client{
		cancel:          cancel,
		auths:           make(map[string]auth.Auther),
		username:        username,
		sessions:        make(map[string]*cliSession),
		maxRedirections: 10,
	}

	client.initSecurityProviders()

	for _, opt := range opts {
		if opt == nil {
			continue
		}
		if err := opt(client); err != nil {
			client.Close()
			return nil, err
		}
	}

	_, err := client.getSession(ctx, address, "")
	if err != nil {
		client.Close()
		return nil, err
	}

	return client, nil
}

// Close closes the connection. Any blocked operation will be unblocked and return error.
func (client *Client) Close() error {
	if client == nil {
		return os.ErrInvalid
	}
	defer client.cancel()

	client.mu.Lock()
	defer client.mu.Unlock()

	var errs []error
	for _, session := range client.sessions {
		err := session.Close()
		if err != nil {
			errs = append(errs, err)
		}
	}
	if errs != nil {
		return fmt.Errorf("xrootd: could not close client: %v", errs)
	}
	return nil
}

// Send sends the request to the server and stores the response inside the resp.
// If the resp is nil, then no response is stored.
// Send returns a session id which identifies the server that provided response.
func (client *Client) Send(ctx context.Context, resp xrdproto.Response, req xrdproto.Request) (string, error) {
	if client == nil {
		return "", os.ErrInvalid
	}

	return client.sendSession(ctx, client.initialSessionID, resp, req)
}

func (client *Client) sendSession(ctx context.Context, sessionID string, resp xrdproto.Response, req xrdproto.Request) (string, error) {
	client.mu.RLock()
	session, ok := client.sessions[sessionID]
	client.mu.RUnlock()
	if !ok {
		return "", fmt.Errorf("xrootd: session with id = %q was not found", sessionID)
	}

	redirection, err := session.Send(ctx, resp, req)
	if err != nil {
		return sessionID, err
	}

	for cnt := client.maxRedirections; redirection != nil && cnt > 0; cnt-- {
		sessionID = redirection.Addr
		session, err = client.getSession(ctx, sessionID, redirection.Token)
		if err != nil {
			return sessionID, err
		}
		if fp, ok := req.(xrdproto.FilepathRequest); ok {
			fp.SetOpaque(redirection.Opaque)
		}
		// TODO: we should check if the request contains file handle and re-issue open request in that case.
		redirection, err = session.Send(ctx, resp, req)
		if err != nil {
			return sessionID, err
		}
	}

	if redirection != nil {
		err = fmt.Errorf("xrootd: received %d redirections in a row, aborting request", client.maxRedirections)
	}

	return sessionID, err
}

func (client *Client) getSession(ctx context.Context, address, token string) (*cliSession, error) {
	client.mu.RLock()
	v, ok := client.sessions[address]
	client.mu.RUnlock()
	if ok {
		return v, nil
	}
	client.mu.Lock()
	defer client.mu.Unlock()
	session, err := newSession(ctx, address, client.username, token, client)
	if err != nil {
		return nil, err
	}
	client.sessions[address] = session

	if len(client.initialSessionID) == 0 {
		client.initialSessionID = address
	}
	// TODO: check if initial sessionID should be changed.
	// See http://xrootd.org/doc/dev45/XRdv310.pdf, p. 11 for details.

	return session, nil
}
<|endoftext|>


<commit_before>// A Go interface to ZeroMQ version 2.
//
// For ZeroMQ version 4, see: http://github.com/pebbe/zmq4
//
// For ZeroMQ version 3, see: http://github.com/pebbe/zmq3
//
// Requires ZeroMQ version 2.1 or 2.2
//
// The following functions return ErrorNotImplemented in 0MQ version 2.1:
//
// (*Socket)GetRcvtimeo, (*Socket)GetSndtimeo, (*Socket)SetRcvtimeo, (*Socket)SetSndtimeo
//
// http://www.zeromq.org/
package zmq2

/*
#cgo !windows pkg-config: libzmq
#cgo windows CFLAGS: -I/usr/local/include
#cgo windows LDFLAGS: -L/usr/local/lib -lzmq
#include <zmq.h>
#include "zmq2.h"
#include <stdlib.h>
#include <string.h>
void my_free (void *data, void *hint) {
    free (data);
}
int my_msg_init_data (zmq_msg_t *msg, void *data, size_t size) {
    return zmq_msg_init_data (msg, data, size, my_free, NULL);
}
*/
import "C"

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"syscall"
	"unsafe"
)

var (
	ErrorNotImplemented = errors.New("Not implemented, requires 0MQ version 2.2")
)

var (
	ctx           unsafe.Pointer
	old           []unsafe.Pointer
	nr_of_threads int
)

func init() {
	var err error
	nr_of_threads = 1
	ctx, err = C.zmq_init(C.int(nr_of_threads))
	if ctx == nil {
		panic("Init of ZeroMQ context failed: " + errget(err).Error())
	}
}

//. Util

func errget(err error) error {
	errno, ok := err.(syscall.Errno)
	if ok && errno >= C.ZMQ_HAUSNUMERO {
		return errors.New(C.GoString(C.zmq_strerror(C.int(errno))))
	}
	return err
}

// Report 0MQ library version.
func Version() (major, minor, patch int) {
	var maj, min, pat C.int
	C.zmq_version(&maj, &min, &pat)
	return int(maj), int(min), int(pat)
}

// Get 0MQ error message string.
func Error(e int) string {
	return C.GoString(C.zmq_strerror(C.int(e)))
}

//. Context

// Returns the size of the 0MQ thread pool.
func GetIoThreads() (int, error) {
	return nr_of_threads, nil
}

/*
This function specifies the size of the ØMQ thread pool to handle I/O operations.
If your application is using only the inproc transport for messaging you may set
this to zero, otherwise set it to at least one.

This function creates a new context without closing the old one. Use it before
creating any sockets.

Default value   1
*/
func SetIoThreads(n int) error {
	if n != nr_of_threads {
		c, err := C.zmq_init(C.int(n))
		if c == nil {
			return errget(err)
		}
		old = append(old, ctx) // keep a reference, to prevent garbage collection
		ctx = c
		nr_of_threads = n
	}
	return nil
}

/*
Terminates the current and all old contexts.

For linger behavior, see: http://api.zeromq.org/2-2:zmq-term
*/
func Term() error {
	n, err := C.zmq_term(ctx)
	if n != 0 {
		return errget(err)
	}

	for _, oldCtx := range old {
		n, err := C.zmq_term(oldCtx)
		if n != 0 {
			return errget(err)
		}
	}

	return nil
}

//. Sockets

// Specifies the type of a socket, used by NewSocket()
type Type int

const (
	// Constants for NewSocket()
	// See: http://api.zeromq.org/2-2:zmq-socket#toc3
	REQ    = Type(C.ZMQ_REQ)
	REP    = Type(C.ZMQ_REP)
	DEALER = Type(C.ZMQ_DEALER)
	ROUTER = Type(C.ZMQ_ROUTER)
	PUB    = Type(C.ZMQ_PUB)
	SUB    = Type(C.ZMQ_SUB)
	XPUB   = Type(C.ZMQ_XPUB)
	XSUB   = Type(C.ZMQ_XSUB)
	PUSH   = Type(C.ZMQ_PUSH)
	PULL   = Type(C.ZMQ_PULL)
	PAIR   = Type(C.ZMQ_PAIR)
)

/*
Socket type as string.
*/
func (t Type) String() string {
	switch t {
	case REQ:
		return "REQ"
	case REP:
		return "REP"
	case DEALER:
		return "DEALER"
	case ROUTER:
		return "ROUTER"
	case PUB:
		return "PUB"
	case SUB:
		return "SUB"
	case XPUB:
		return "XPUB"
	case XSUB:
		return "XSUB"
	case PUSH:
		return "PUSH"
	case PULL:
		return "PULL"
	case PAIR:
		return "PAIR"
	}
	return "<INVALID>"
}

// Used by  (*Socket)Send() and (*Socket)Recv()
type Flag int

const (
	// Flags for (*Socket)Send(), (*Socket)Recv()
	// For Send, see: http://api.zeromq.org/2-2:zmq-send#toc2
	// For Recv, see: http://api.zeromq.org/2-2:zmq-recv#toc2
	NOBLOCK = Flag(C.ZMQ_NOBLOCK)
	SNDMORE = Flag(C.ZMQ_SNDMORE)
)

/*
Socket flag as string.
*/
func (f Flag) String() string {
	ff := make([]string, 0)
	if f&NOBLOCK != 0 {
		ff = append(ff, "NOBLOCK")
	}
	if f&SNDMORE != 0 {
		ff = append(ff, "SNDMORE")
	}
	if len(ff) == 0 {
		return "<NONE>"
	}
	return strings.Join(ff, "|")
}

// Used by (soc *Socket)GetEvents()
type State uint32

const (
	// Flags for (*Socket)GetEvents()
	// See: http://api.zeromq.org/2-2:zmq-getsockopt#toc22
	POLLIN  = State(C.ZMQ_POLLIN)
	POLLOUT = State(C.ZMQ_POLLOUT)
)

// Uses by Device()
type Dev int

const (
	// Constants for Device()
	// See: http://api.zeromq.org/2-2:zmq-device#toc2
	QUEUE     = Dev(C.ZMQ_QUEUE)
	FORWARDER = Dev(C.ZMQ_FORWARDER)
	STREAMER  = Dev(C.ZMQ_STREAMER)
)

/*
Dev as string
*/
func (d Dev) String() string {
	switch d {
	case QUEUE:
		return "QUEUE"
	case FORWARDER:
		return "FORWARDER"
	case STREAMER:
		return "STREAMER"
	}
	return "<INVALID>"
}

/*
Socket state as string.
*/
func (s State) String() string {
	ss := make([]string, 0)
	if s&POLLIN != 0 {
		ss = append(ss, "POLLIN")
	}
	if s&POLLOUT != 0 {
		ss = append(ss, "POLLOUT")
	}
	if len(ss) == 0 {
		return "<NONE>"
	}
	return strings.Join(ss, "|")
}

/*
Socket functions starting with `Set` or `Get` are used for setting and
getting socket options.
*/
type Socket struct {
	soc unsafe.Pointer
}

/*
Socket as string.
*/
func (soc Socket) String() string {
	t, _ := soc.GetType()
	i, err := soc.GetIdentity()
	if err == nil && i != "" {
		return fmt.Sprintf("Socket(%v,%q)", t, i)
	}
	return fmt.Sprintf("Socket(%v,%p)", t, soc.soc)
}

/*
Create 0MQ socket.

WARNING:
The Socket is not thread safe. This means that you cannot access the same Socket
from different goroutines without using something like a mutex.

For a description of socket types, see: http://api.zeromq.org/2-2:zmq-socket#toc3
*/
func NewSocket(t Type) (soc *Socket, err error) {
	soc = &Socket{}
	s, e := C.zmq_socket(ctx, C.int(t))
	if s == nil {
		err = errget(e)
	} else {
		soc.soc = s
		runtime.SetFinalizer(soc, (*Socket).Close)
	}
	return
}

// If not called explicitly, the socket will be closed on garbage collection
func (soc *Socket) Close() error {
	if i, err := C.zmq_close(soc.soc); int(i) != 0 {
		return errget(err)
	}
	soc.soc = unsafe.Pointer(nil)
	return nil
}

/*
Accept incoming connections on a socket.

For a description of endpoint, see: http://api.zeromq.org/2-2:zmq-bind#toc2
*/
func (soc *Socket) Bind(endpoint string) error {
	s := C.CString(endpoint)
	defer C.free(unsafe.Pointer(s))
	if i, err := C.zmq_bind(soc.soc, s); int(i) != 0 {
		return errget(err)
	}
	return nil
}

/*
Create outgoing connection from socket.

For a description of endpoint, see: http://api.zeromq.org/2-2:zmq-connect#toc2
*/
func (soc *Socket) Connect(endpoint string) error {
	s := C.CString(endpoint)
	defer C.free(unsafe.Pointer(s))
	if i, err := C.zmq_connect(soc.soc, s); int(i) != 0 {
		return errget(err)
	}
	return nil
}

/*
Receive a message part from a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-recv#toc2
*/
func (soc *Socket) Recv(flags Flag) (string, error) {
	b, err := soc.RecvBytes(flags)
	return string(b), err
}

/*
Receive a message part from a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-recv#toc2
*/
func (soc *Socket) RecvBytes(flags Flag) ([]byte, error) {
	var msg C.zmq_msg_t
	if i, err := C.zmq_msg_init(&msg); i != 0 {
		return []byte{}, errget(err)
	}
	defer C.zmq_msg_close(&msg)

	var size C.int
	var err error

	var i C.int
	i, err = C.zmq_recv(soc.soc, &msg, C.int(flags))
	if i == 0 {
		size = C.int(C.zmq_msg_size(&msg))
	} else {
		size = -1
	}

	if size < 0 {
		return []byte{}, errget(err)
	}
	if size == 0 {
		return []byte{}, nil
	}
	data := make([]byte, int(size))
	C.memcpy(unsafe.Pointer(&data[0]), C.zmq_msg_data(&msg), C.size_t(size))
	return data, nil
}

/*
Send a message part on a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-send#toc2
*/
func (soc *Socket) Send(data string, flags Flag) (int, error) {
	return soc.SendBytes([]byte(data), flags)
}

/*
Send a message part on a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-send#toc2
*/
func (soc *Socket) SendBytes(data []byte, flags Flag) (int, error) {
	datac := C.CString(string(data))
	var msg C.zmq_msg_t
	if i, err := C.my_msg_init_data(&msg, unsafe.Pointer(datac), C.size_t(len(data))); i != 0 {
		return -1, errget(err)
	}
	defer C.zmq_msg_close(&msg)
	n, err := C.zmq_send(soc.soc, &msg, C.int(flags))
	if n != 0 {
		return -1, errget(err)
	}
	return int(n), nil
}

/*
Start built-in ØMQ device

see: http://api.zeromq.org/2-2:zmq-device#toc2
*/
func Device(device Dev, frontend, backend *Socket) error {
	_, err := C.zmq_device(C.int(device), frontend.soc, backend.soc)
	return errget(err)
}

/*
Emulate the proxy that will be built-in in 0MQ version 3

See: http://api.zeromq.org/3-2:zmq-proxy
*/
func Proxy(frontend, backend, capture *Socket) error {
	items := NewPoller()
	items.Add(frontend, POLLIN)
	items.Add(backend, POLLIN)
	for {
		sockets, err := items.Poll(-1)
		if err != nil {
			return err
		}
		for _, socket := range sockets {
			for more := true; more; {
				msg, err := socket.Socket.RecvBytes(0)
				if err != nil {
					return err
				}
				more, err = socket.Socket.GetRcvmore()
				if err != nil {
					return err
				}
				fl := SNDMORE
				if !more {
					fl = 0
				}

				if capture != nil {
					_, err = capture.SendBytes(msg, fl)
					if err != nil {
						return err
					}
				}

				switch socket.Socket {
				case frontend:
					_, err = backend.SendBytes(msg, fl)
				case backend:
					_, err = frontend.SendBytes(msg, fl)
				}
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
<commit_msg>3rd arg of memcpy need not be size_t.<commit_after>// A Go interface to ZeroMQ version 2.
//
// For ZeroMQ version 4, see: http://github.com/pebbe/zmq4
//
// For ZeroMQ version 3, see: http://github.com/pebbe/zmq3
//
// Requires ZeroMQ version 2.1 or 2.2
//
// The following functions return ErrorNotImplemented in 0MQ version 2.1:
//
// (*Socket)GetRcvtimeo, (*Socket)GetSndtimeo, (*Socket)SetRcvtimeo, (*Socket)SetSndtimeo
//
// http://www.zeromq.org/
package zmq2

/*
#cgo !windows pkg-config: libzmq
#cgo windows CFLAGS: -I/usr/local/include
#cgo windows LDFLAGS: -L/usr/local/lib -lzmq
#include <zmq.h>
#include "zmq2.h"
#include <stdlib.h>
#include <string.h>
void my_free (void *data, void *hint) {
    free (data);
}
int my_msg_init_data (zmq_msg_t *msg, void *data, size_t size) {
    return zmq_msg_init_data (msg, data, size, my_free, NULL);
}
void *my_memcpy(void *dest, const void *src, size_t n) {
	memcpy(dest, src, n);
}
*/
import "C"

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"syscall"
	"unsafe"
)

var (
	ErrorNotImplemented = errors.New("Not implemented, requires 0MQ version 2.2")
)

var (
	ctx           unsafe.Pointer
	old           []unsafe.Pointer
	nr_of_threads int
)

func init() {
	var err error
	nr_of_threads = 1
	ctx, err = C.zmq_init(C.int(nr_of_threads))
	if ctx == nil {
		panic("Init of ZeroMQ context failed: " + errget(err).Error())
	}
}

//. Util

func errget(err error) error {
	errno, ok := err.(syscall.Errno)
	if ok && errno >= C.ZMQ_HAUSNUMERO {
		return errors.New(C.GoString(C.zmq_strerror(C.int(errno))))
	}
	return err
}

// Report 0MQ library version.
func Version() (major, minor, patch int) {
	var maj, min, pat C.int
	C.zmq_version(&maj, &min, &pat)
	return int(maj), int(min), int(pat)
}

// Get 0MQ error message string.
func Error(e int) string {
	return C.GoString(C.zmq_strerror(C.int(e)))
}

//. Context

// Returns the size of the 0MQ thread pool.
func GetIoThreads() (int, error) {
	return nr_of_threads, nil
}

/*
This function specifies the size of the ØMQ thread pool to handle I/O operations.
If your application is using only the inproc transport for messaging you may set
this to zero, otherwise set it to at least one.

This function creates a new context without closing the old one. Use it before
creating any sockets.

Default value   1
*/
func SetIoThreads(n int) error {
	if n != nr_of_threads {
		c, err := C.zmq_init(C.int(n))
		if c == nil {
			return errget(err)
		}
		old = append(old, ctx) // keep a reference, to prevent garbage collection
		ctx = c
		nr_of_threads = n
	}
	return nil
}

/*
Terminates the current and all old contexts.

For linger behavior, see: http://api.zeromq.org/2-2:zmq-term
*/
func Term() error {
	n, err := C.zmq_term(ctx)
	if n != 0 {
		return errget(err)
	}

	for _, oldCtx := range old {
		n, err := C.zmq_term(oldCtx)
		if n != 0 {
			return errget(err)
		}
	}

	return nil
}

//. Sockets

// Specifies the type of a socket, used by NewSocket()
type Type int

const (
	// Constants for NewSocket()
	// See: http://api.zeromq.org/2-2:zmq-socket#toc3
	REQ    = Type(C.ZMQ_REQ)
	REP    = Type(C.ZMQ_REP)
	DEALER = Type(C.ZMQ_DEALER)
	ROUTER = Type(C.ZMQ_ROUTER)
	PUB    = Type(C.ZMQ_PUB)
	SUB    = Type(C.ZMQ_SUB)
	XPUB   = Type(C.ZMQ_XPUB)
	XSUB   = Type(C.ZMQ_XSUB)
	PUSH   = Type(C.ZMQ_PUSH)
	PULL   = Type(C.ZMQ_PULL)
	PAIR   = Type(C.ZMQ_PAIR)
)

/*
Socket type as string.
*/
func (t Type) String() string {
	switch t {
	case REQ:
		return "REQ"
	case REP:
		return "REP"
	case DEALER:
		return "DEALER"
	case ROUTER:
		return "ROUTER"
	case PUB:
		return "PUB"
	case SUB:
		return "SUB"
	case XPUB:
		return "XPUB"
	case XSUB:
		return "XSUB"
	case PUSH:
		return "PUSH"
	case PULL:
		return "PULL"
	case PAIR:
		return "PAIR"
	}
	return "<INVALID>"
}

// Used by  (*Socket)Send() and (*Socket)Recv()
type Flag int

const (
	// Flags for (*Socket)Send(), (*Socket)Recv()
	// For Send, see: http://api.zeromq.org/2-2:zmq-send#toc2
	// For Recv, see: http://api.zeromq.org/2-2:zmq-recv#toc2
	NOBLOCK = Flag(C.ZMQ_NOBLOCK)
	SNDMORE = Flag(C.ZMQ_SNDMORE)
)

/*
Socket flag as string.
*/
func (f Flag) String() string {
	ff := make([]string, 0)
	if f&NOBLOCK != 0 {
		ff = append(ff, "NOBLOCK")
	}
	if f&SNDMORE != 0 {
		ff = append(ff, "SNDMORE")
	}
	if len(ff) == 0 {
		return "<NONE>"
	}
	return strings.Join(ff, "|")
}

// Used by (soc *Socket)GetEvents()
type State uint32

const (
	// Flags for (*Socket)GetEvents()
	// See: http://api.zeromq.org/2-2:zmq-getsockopt#toc22
	POLLIN  = State(C.ZMQ_POLLIN)
	POLLOUT = State(C.ZMQ_POLLOUT)
)

// Uses by Device()
type Dev int

const (
	// Constants for Device()
	// See: http://api.zeromq.org/2-2:zmq-device#toc2
	QUEUE     = Dev(C.ZMQ_QUEUE)
	FORWARDER = Dev(C.ZMQ_FORWARDER)
	STREAMER  = Dev(C.ZMQ_STREAMER)
)

/*
Dev as string
*/
func (d Dev) String() string {
	switch d {
	case QUEUE:
		return "QUEUE"
	case FORWARDER:
		return "FORWARDER"
	case STREAMER:
		return "STREAMER"
	}
	return "<INVALID>"
}

/*
Socket state as string.
*/
func (s State) String() string {
	ss := make([]string, 0)
	if s&POLLIN != 0 {
		ss = append(ss, "POLLIN")
	}
	if s&POLLOUT != 0 {
		ss = append(ss, "POLLOUT")
	}
	if len(ss) == 0 {
		return "<NONE>"
	}
	return strings.Join(ss, "|")
}

/*
Socket functions starting with `Set` or `Get` are used for setting and
getting socket options.
*/
type Socket struct {
	soc unsafe.Pointer
}

/*
Socket as string.
*/
func (soc Socket) String() string {
	t, _ := soc.GetType()
	i, err := soc.GetIdentity()
	if err == nil && i != "" {
		return fmt.Sprintf("Socket(%v,%q)", t, i)
	}
	return fmt.Sprintf("Socket(%v,%p)", t, soc.soc)
}

/*
Create 0MQ socket.

WARNING:
The Socket is not thread safe. This means that you cannot access the same Socket
from different goroutines without using something like a mutex.

For a description of socket types, see: http://api.zeromq.org/2-2:zmq-socket#toc3
*/
func NewSocket(t Type) (soc *Socket, err error) {
	soc = &Socket{}
	s, e := C.zmq_socket(ctx, C.int(t))
	if s == nil {
		err = errget(e)
	} else {
		soc.soc = s
		runtime.SetFinalizer(soc, (*Socket).Close)
	}
	return
}

// If not called explicitly, the socket will be closed on garbage collection
func (soc *Socket) Close() error {
	if i, err := C.zmq_close(soc.soc); int(i) != 0 {
		return errget(err)
	}
	soc.soc = unsafe.Pointer(nil)
	return nil
}

/*
Accept incoming connections on a socket.

For a description of endpoint, see: http://api.zeromq.org/2-2:zmq-bind#toc2
*/
func (soc *Socket) Bind(endpoint string) error {
	s := C.CString(endpoint)
	defer C.free(unsafe.Pointer(s))
	if i, err := C.zmq_bind(soc.soc, s); int(i) != 0 {
		return errget(err)
	}
	return nil
}

/*
Create outgoing connection from socket.

For a description of endpoint, see: http://api.zeromq.org/2-2:zmq-connect#toc2
*/
func (soc *Socket) Connect(endpoint string) error {
	s := C.CString(endpoint)
	defer C.free(unsafe.Pointer(s))
	if i, err := C.zmq_connect(soc.soc, s); int(i) != 0 {
		return errget(err)
	}
	return nil
}

/*
Receive a message part from a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-recv#toc2
*/
func (soc *Socket) Recv(flags Flag) (string, error) {
	b, err := soc.RecvBytes(flags)
	return string(b), err
}

/*
Receive a message part from a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-recv#toc2
*/
func (soc *Socket) RecvBytes(flags Flag) ([]byte, error) {
	var msg C.zmq_msg_t
	if i, err := C.zmq_msg_init(&msg); i != 0 {
		return []byte{}, errget(err)
	}
	defer C.zmq_msg_close(&msg)

	var size C.int
	var err error

	var i C.int
	i, err = C.zmq_recv(soc.soc, &msg, C.int(flags))
	if i == 0 {
		size = C.int(C.zmq_msg_size(&msg))
	} else {
		size = -1
	}

	if size < 0 {
		return []byte{}, errget(err)
	}
	if size == 0 {
		return []byte{}, nil
	}
	data := make([]byte, int(size))
	C.my_memcpy(unsafe.Pointer(&data[0]), C.zmq_msg_data(&msg), C.size_t(size))
	return data, nil
}

/*
Send a message part on a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-send#toc2
*/
func (soc *Socket) Send(data string, flags Flag) (int, error) {
	return soc.SendBytes([]byte(data), flags)
}

/*
Send a message part on a socket.

For a description of flags, see: http://api.zeromq.org/2-2:zmq-send#toc2
*/
func (soc *Socket) SendBytes(data []byte, flags Flag) (int, error) {
	datac := C.CString(string(data))
	var msg C.zmq_msg_t
	if i, err := C.my_msg_init_data(&msg, unsafe.Pointer(datac), C.size_t(len(data))); i != 0 {
		return -1, errget(err)
	}
	defer C.zmq_msg_close(&msg)
	n, err := C.zmq_send(soc.soc, &msg, C.int(flags))
	if n != 0 {
		return -1, errget(err)
	}
	return int(n), nil
}

/*
Start built-in ØMQ device

see: http://api.zeromq.org/2-2:zmq-device#toc2
*/
func Device(device Dev, frontend, backend *Socket) error {
	_, err := C.zmq_device(C.int(device), frontend.soc, backend.soc)
	return errget(err)
}

/*
Emulate the proxy that will be built-in in 0MQ version 3

See: http://api.zeromq.org/3-2:zmq-proxy
*/
func Proxy(frontend, backend, capture *Socket) error {
	items := NewPoller()
	items.Add(frontend, POLLIN)
	items.Add(backend, POLLIN)
	for {
		sockets, err := items.Poll(-1)
		if err != nil {
			return err
		}
		for _, socket := range sockets {
			for more := true; more; {
				msg, err := socket.Socket.RecvBytes(0)
				if err != nil {
					return err
				}
				more, err = socket.Socket.GetRcvmore()
				if err != nil {
					return err
				}
				fl := SNDMORE
				if !more {
					fl = 0
				}

				if capture != nil {
					_, err = capture.SendBytes(msg, fl)
					if err != nil {
						return err
					}
				}

				switch socket.Socket {
				case frontend:
					_, err = backend.SendBytes(msg, fl)
				case backend:
					_, err = frontend.SendBytes(msg, fl)
				}
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/robarchibald/configReader"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

type authConf struct {
	AuthServerListenPort                     int
	StoragePrefix                            string
	DbType                                   string
	DbServer                                 string
	DbPort                                   int
	DbUser                                   string
	DbDatabase                               string
	DbPassword                               string
	LdapServer                               string
	LdapPort                                 int
	LdapBindDn                               string
	LdapPassword                             string
	LdapBaseDn                               string
	LdapUserFilter                           string
	GetSessionQuery                          string
	RenewSessionQuery                        string
	GetRememberMeQuery                       string
	RenewRememberMeQuery                     string
	AddUserQuery                             string
	GetUserQuery                             string
	UpdateUserQuery                          string
	CreateLoginQuery                         string
	UpdateEmailAndInvalidateSessionsQuery    string
	UpdatePasswordAndInvalidateSessionsQuery string
	InvalidateUserSessionsQuery              string

	RedisServer         string
	RedisPort           int
	RedisPassword       string
	RedisMaxIdle        int
	RedisMaxConnections int
	ConcurrentDownloads int

	CookieBase64Key string

	SMTPServer              string
	SMTPPort                int
	SMTPFromEmail           string
	SMTPPassword            string
	EmailFromDisplayName    string
	VerifyEmailTemplate     string
	VerifyEmailSubject      string
	WelcomeTemplate         string
	WelcomeSubject          string
	NewLoginTemplate        string
	NewLoginSubject         string
	LockedOutTemplate       string
	LockedOutSubject        string
	EmailChangedTemplate    string
	EmailChangedSubject     string
	PasswordChangedTemplate string
	PasswordChangedSubject  string
}

type nginxauth struct {
	backend   backender
	mailer    mailer
	cookieKey []byte
	conf      authConf
}

func main() {
	server, err := newNginxAuth()
	if err != nil {
		log.Fatal(err)
	}
	defer server.backend.Close()

	server.serve(server.conf.AuthServerListenPort)
}

func newNginxAuth() (*nginxauth, error) {
	config := authConf{}
	err := configReader.ReadFile("nginxauth.conf", &config)
	if err != nil {
		log.Fatal(err)
	}

	s := newBackendRedisSession(config.RedisServer, config.RedisPort, config.RedisPassword, config.RedisMaxIdle, config.RedisMaxConnections, config.StoragePrefix)
	l, err := newBackendLDAPLogin(config.LdapServer, config.LdapPort, config.LdapBindDn, config.LdapPassword, config.LdapBaseDn, config.LdapUserFilter)
	if err != nil {
		return nil, err
	}
	u, err := newBackendDbUser(config.DbServer, config.DbPort, config.DbUser, config.DbPassword, config.DbDatabase, config.AddUserQuery, config.GetUserQuery, config.UpdateUserQuery)
	if err != nil {
		return nil, err
	}
	b := &backend{u: u, l: l, s: s}

	mailer, err := config.NewEmailer()
	if err != nil {
		return nil, err
	}

	cookieKey, err := decodeFromString(config.CookieBase64Key)
	if err != nil {
		return nil, err
	}

	return &nginxauth{b, mailer, cookieKey, config}, nil
}

func (n *authConf) NewEmailer() (*emailer, error) {
	sender := &smtpSender{n.SMTPServer, n.SMTPPort, n.SMTPFromEmail, n.SMTPPassword, n.EmailFromDisplayName}
	templateCache, err := template.ParseFiles(n.VerifyEmailTemplate, n.WelcomeTemplate,
		n.NewLoginTemplate, n.LockedOutTemplate, n.EmailChangedTemplate, n.PasswordChangedTemplate)
	if err != nil {
		return nil, err
	}
	return &emailer{
		templateCache:           templateCache,
		sender:                  sender,
		VerifyEmailTemplate:     n.VerifyEmailTemplate,
		VerifyEmailSubject:      n.VerifyEmailSubject,
		WelcomeTemplate:         n.WelcomeTemplate,
		WelcomeSubject:          n.WelcomeSubject,
		NewLoginTemplate:        n.NewLoginTemplate,
		NewLoginSubject:         n.NewLoginSubject,
		LockedOutTemplate:       n.LockedOutTemplate,
		LockedOutSubject:        n.LockedOutSubject,
		EmailChangedTemplate:    n.EmailChangedTemplate,
		EmailChangedSubject:     n.EmailChangedSubject,
		PasswordChangedTemplate: n.PasswordChangedTemplate,
		PasswordChangedSubject:  n.PasswordChangedSubject,
	}, nil
}

func (s *nginxauth) serve(port int) {
	http.HandleFunc("/auth", s.method("GET", auth))
	http.HandleFunc("/authBasic", s.method("GET", authBasic))
	http.HandleFunc("/createProfile", s.method("POST", createProfile))
	http.HandleFunc("/login", s.method("POST", login))
	http.HandleFunc("/register", s.method("POST", register))
	http.HandleFunc("/verifyEmail", s.method("POST", verifyEmail))
	http.HandleFunc("/updateEmail", s.method("POST", updateEmail))
	http.HandleFunc("/updatePassword", s.method("POST", updatePassword))

	http.ListenAndServe(fmt.Sprintf(":%d", port), fileLoggerHandler(handlers.CompressHandler(http.DefaultServeMux)))
}

func fileLoggerHandler(h http.Handler) http.Handler {
	logFile, err := os.OpenFile("nginxauth.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	return handlers.CombinedLoggingHandler(logFile, h)
}

func (s *nginxauth) method(name string, handler func(authStore authStorer, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != name {
			http.Error(w, "Unsupported method", http.StatusInternalServerError)
			return
		}
		secureOnly := strings.HasPrefix(r.Referer(), "https") // proxy to back-end so if referer is secure connection, we can use secureOnly cookies
		authStore := newAuthStore(s.backend, s.mailer, &cryptoHashStore{}, w, r, s.conf.StoragePrefix, s.cookieKey, secureOnly)
		handler(authStore, w, r)
	}
}

func auth(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	log.Println("auth begin:")
	session, err := authStore.GetSession()
	if err != nil {
		authErr(w, r, err)
		log.Println("auth end:   session error", time.Since(startTime))
		return
	}

	user, err := json.Marshal(&userLogin{Email: session.Email, UserID: session.UserID, FullName: session.FullName})
	if err != nil {
		authErr(w, r, err)
		log.Println("auth end:   json error", time.Since(startTime))
		return
	}

	addUserHeader(string(user), w)
	log.Println("auth end:   success", time.Since(startTime))
}

func authErr(w http.ResponseWriter, r *http.Request, err error) {
	http.Error(w, "Authentication required: "+err.Error(), http.StatusUnauthorized)
	if a, ok := err.(*authError); ok {
		fmt.Println(a.Trace())
	} else {
		fmt.Println(err)
	}
}

func authBasic(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	log.Println("authBasic begin:")
	session, err := authStore.GetBasicAuth()
	if err != nil {
		basicErr(w, r, err)
		log.Println("authBasic end:   session error", time.Since(startTime))
		return
	}

	user, err := json.Marshal(&userLogin{Email: session.Email, UserID: session.UserID, FullName: session.FullName})
	if err != nil {
		basicErr(w, r, err)
		log.Println("authBasic end:   json error", time.Since(startTime))
		return
	}

	addUserHeader(string(user), w)
	log.Println("authBasic end:   success", time.Since(startTime))
}

func basicErr(w http.ResponseWriter, r *http.Request, err error) {
	w.Header().Set("WWW-Authenticate", "Basic realm='Endfirst.com'")
	http.Error(w, "Authentication required: "+err.Error(), http.StatusUnauthorized)
}

func login(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("login", authStore.Login, w)
}

func register(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("register", authStore.Register, w)
}

func createProfile(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("createProfile", authStore.CreateProfile, w)
}

func updateEmail(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("updateEmail", authStore.UpdateEmail, w)
}

func updatePassword(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("updatePassword", authStore.UpdatePassword, w)
}

func verifyEmail(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("verifyEmail", authStore.VerifyEmail, w)
}

func run(name string, method func() error, w http.ResponseWriter) {
	startTime := time.Now()
	log.Println(name, "begin:")
	err := method()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		if a, ok := err.(*authError); ok {
			fmt.Println(a.Trace())
		}
		log.Println(name, "end:   error", time.Since(startTime))
	} else {
		w.Header().Add("Access-Control-Allow-Origin", "*")
		w.Header().Add("Content-Type", "application/javascript")
		fmt.Fprint(w, "{ \"result\": \"Success\" }")
		log.Println(name, "end:   success", time.Since(startTime))
	}
}

func addUserHeader(userJSON string, w http.ResponseWriter) {
	w.Header().Add("X-User", userJSON)
}
<commit_msg>use command-line specified config file<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/robarchibald/configReader"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

type authConf struct {
	AuthServerListenPort                     int
	StoragePrefix                            string
	DbType                                   string
	DbServer                                 string
	DbPort                                   int
	DbUser                                   string
	DbDatabase                               string
	DbPassword                               string
	LdapServer                               string
	LdapPort                                 int
	LdapBindDn                               string
	LdapPassword                             string
	LdapBaseDn                               string
	LdapUserFilter                           string
	GetSessionQuery                          string
	RenewSessionQuery                        string
	GetRememberMeQuery                       string
	RenewRememberMeQuery                     string
	AddUserQuery                             string
	GetUserQuery                             string
	UpdateUserQuery                          string
	CreateLoginQuery                         string
	UpdateEmailAndInvalidateSessionsQuery    string
	UpdatePasswordAndInvalidateSessionsQuery string
	InvalidateUserSessionsQuery              string

	RedisServer         string
	RedisPort           int
	RedisPassword       string
	RedisMaxIdle        int
	RedisMaxConnections int
	ConcurrentDownloads int

	CookieBase64Key string

	SMTPServer              string
	SMTPPort                int
	SMTPFromEmail           string
	SMTPPassword            string
	EmailFromDisplayName    string
	VerifyEmailTemplate     string
	VerifyEmailSubject      string
	WelcomeTemplate         string
	WelcomeSubject          string
	NewLoginTemplate        string
	NewLoginSubject         string
	LockedOutTemplate       string
	LockedOutSubject        string
	EmailChangedTemplate    string
	EmailChangedSubject     string
	PasswordChangedTemplate string
	PasswordChangedSubject  string
}

type nginxauth struct {
	backend   backender
	mailer    mailer
	cookieKey []byte
	conf      authConf
}

func main() {
	configFile := flag.String("c", "nginxauth.conf", "config file location")
	flag.Parse()
	server, err := newNginxAuth(*configFile)
	if err != nil {
		log.Fatal(err)
	}
	defer server.backend.Close()

	server.serve(server.conf.AuthServerListenPort)
}

func newNginxAuth(configFle string) (*nginxauth, error) {
	config := authConf{}
	err := configReader.ReadFile(configFle, &config)
	if err != nil {
		log.Fatal(err)
	}

	s := newBackendRedisSession(config.RedisServer, config.RedisPort, config.RedisPassword, config.RedisMaxIdle, config.RedisMaxConnections, config.StoragePrefix)
	l, err := newBackendLDAPLogin(config.LdapServer, config.LdapPort, config.LdapBindDn, config.LdapPassword, config.LdapBaseDn, config.LdapUserFilter)
	if err != nil {
		return nil, err
	}
	u, err := newBackendDbUser(config.DbServer, config.DbPort, config.DbUser, config.DbPassword, config.DbDatabase, config.AddUserQuery, config.GetUserQuery, config.UpdateUserQuery)
	if err != nil {
		return nil, err
	}
	b := &backend{u: u, l: l, s: s}

	mailer, err := config.NewEmailer()
	if err != nil {
		return nil, err
	}

	cookieKey, err := decodeFromString(config.CookieBase64Key)
	if err != nil {
		return nil, err
	}

	return &nginxauth{b, mailer, cookieKey, config}, nil
}

func (n *authConf) NewEmailer() (*emailer, error) {
	sender := &smtpSender{n.SMTPServer, n.SMTPPort, n.SMTPFromEmail, n.SMTPPassword, n.EmailFromDisplayName}
	templateCache, err := template.ParseFiles(n.VerifyEmailTemplate, n.WelcomeTemplate,
		n.NewLoginTemplate, n.LockedOutTemplate, n.EmailChangedTemplate, n.PasswordChangedTemplate)
	if err != nil {
		return nil, err
	}
	return &emailer{
		templateCache:           templateCache,
		sender:                  sender,
		VerifyEmailTemplate:     n.VerifyEmailTemplate,
		VerifyEmailSubject:      n.VerifyEmailSubject,
		WelcomeTemplate:         n.WelcomeTemplate,
		WelcomeSubject:          n.WelcomeSubject,
		NewLoginTemplate:        n.NewLoginTemplate,
		NewLoginSubject:         n.NewLoginSubject,
		LockedOutTemplate:       n.LockedOutTemplate,
		LockedOutSubject:        n.LockedOutSubject,
		EmailChangedTemplate:    n.EmailChangedTemplate,
		EmailChangedSubject:     n.EmailChangedSubject,
		PasswordChangedTemplate: n.PasswordChangedTemplate,
		PasswordChangedSubject:  n.PasswordChangedSubject,
	}, nil
}

func (s *nginxauth) serve(port int) {
	http.HandleFunc("/auth", s.method("GET", auth))
	http.HandleFunc("/authBasic", s.method("GET", authBasic))
	http.HandleFunc("/createProfile", s.method("POST", createProfile))
	http.HandleFunc("/login", s.method("POST", login))
	http.HandleFunc("/register", s.method("POST", register))
	http.HandleFunc("/verifyEmail", s.method("POST", verifyEmail))
	http.HandleFunc("/updateEmail", s.method("POST", updateEmail))
	http.HandleFunc("/updatePassword", s.method("POST", updatePassword))

	http.ListenAndServe(fmt.Sprintf(":%d", port), fileLoggerHandler(handlers.CompressHandler(http.DefaultServeMux)))
}

func fileLoggerHandler(h http.Handler) http.Handler {
	logFile, err := os.OpenFile("nginxauth.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	return handlers.CombinedLoggingHandler(logFile, h)
}

func (s *nginxauth) method(name string, handler func(authStore authStorer, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != name {
			http.Error(w, "Unsupported method", http.StatusInternalServerError)
			return
		}
		secureOnly := strings.HasPrefix(r.Referer(), "https") // proxy to back-end so if referer is secure connection, we can use secureOnly cookies
		authStore := newAuthStore(s.backend, s.mailer, &cryptoHashStore{}, w, r, s.conf.StoragePrefix, s.cookieKey, secureOnly)
		handler(authStore, w, r)
	}
}

func auth(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	log.Println("auth begin:")
	session, err := authStore.GetSession()
	if err != nil {
		authErr(w, r, err)
		log.Println("auth end:   session error", time.Since(startTime))
		return
	}

	user, err := json.Marshal(&userLogin{Email: session.Email, UserID: session.UserID, FullName: session.FullName})
	if err != nil {
		authErr(w, r, err)
		log.Println("auth end:   json error", time.Since(startTime))
		return
	}

	addUserHeader(string(user), w)
	log.Println("auth end:   success", time.Since(startTime))
}

func authErr(w http.ResponseWriter, r *http.Request, err error) {
	http.Error(w, "Authentication required: "+err.Error(), http.StatusUnauthorized)
	if a, ok := err.(*authError); ok {
		fmt.Println(a.Trace())
	} else {
		fmt.Println(err)
	}
}

func authBasic(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	log.Println("authBasic begin:")
	session, err := authStore.GetBasicAuth()
	if err != nil {
		basicErr(w, r, err)
		log.Println("authBasic end:   session error", time.Since(startTime))
		return
	}

	user, err := json.Marshal(&userLogin{Email: session.Email, UserID: session.UserID, FullName: session.FullName})
	if err != nil {
		basicErr(w, r, err)
		log.Println("authBasic end:   json error", time.Since(startTime))
		return
	}

	addUserHeader(string(user), w)
	log.Println("authBasic end:   success", time.Since(startTime))
}

func basicErr(w http.ResponseWriter, r *http.Request, err error) {
	w.Header().Set("WWW-Authenticate", "Basic realm='Endfirst.com'")
	http.Error(w, "Authentication required: "+err.Error(), http.StatusUnauthorized)
}

func login(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("login", authStore.Login, w)
}

func register(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("register", authStore.Register, w)
}

func createProfile(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("createProfile", authStore.CreateProfile, w)
}

func updateEmail(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("updateEmail", authStore.UpdateEmail, w)
}

func updatePassword(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("updatePassword", authStore.UpdatePassword, w)
}

func verifyEmail(authStore authStorer, w http.ResponseWriter, r *http.Request) {
	run("verifyEmail", authStore.VerifyEmail, w)
}

func run(name string, method func() error, w http.ResponseWriter) {
	startTime := time.Now()
	log.Println(name, "begin:")
	err := method()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		if a, ok := err.(*authError); ok {
			fmt.Println(a.Trace())
		}
		log.Println(name, "end:   error", time.Since(startTime))
	} else {
		w.Header().Add("Access-Control-Allow-Origin", "*")
		w.Header().Add("Content-Type", "application/javascript")
		fmt.Fprint(w, "{ \"result\": \"Success\" }")
		log.Println(name, "end:   success", time.Since(startTime))
	}
}

func addUserHeader(userJSON string, w http.ResponseWriter) {
	w.Header().Add("X-User", userJSON)
}
<|endoftext|>


<commit_before>package of10

import (
	"bytes"
	"encoding/binary"
)

const MaxPortNameLength = 16
const EthernetAddressLength = 6

type PortNumber uint16
type PortConfig uint16
type PortState uint16
type PortFeature uint16

type PhysicalPort struct {
	PortNumber         PortNumber
	HardwareAddress    [EthernetAddressLength]uint8
	Name               [MaxPortNameLength]uint8
	Config             PortConfig
	State              PortState
	CurrentFeatures    PortFeature
	AdvertisedFeatures PortFeature
	SupportedFeatures  PortFeature
	PeerFeatures       PortFeature
}

func readPhysicalPort(b []byte) ([]PhysicalPort, error) {
	var port PhysicalPort
	count := len(b) / binary.Size(port)
	ports := make([]PhysicalPort, count)

	buf := bytes.NewBuffer(b)
	if err := binary.Read(buf, binary.BigEndian, port); err != nil {
		return nil, err
	}

	return ports, nil
}
<commit_msg>Fix wrong slice length in initialization<commit_after>package of10

import (
	"bytes"
	"encoding/binary"
)

const MaxPortNameLength = 16
const EthernetAddressLength = 6

type PortNumber uint16
type PortConfig uint16
type PortState uint16
type PortFeature uint16

type PhysicalPort struct {
	PortNumber         PortNumber
	HardwareAddress    [EthernetAddressLength]uint8
	Name               [MaxPortNameLength]uint8
	Config             PortConfig
	State              PortState
	CurrentFeatures    PortFeature
	AdvertisedFeatures PortFeature
	SupportedFeatures  PortFeature
	PeerFeatures       PortFeature
}

func readPhysicalPort(b []byte) ([]PhysicalPort, error) {
	var port PhysicalPort
	count := len(b) / binary.Size(port)
	ports := make([]PhysicalPort, 0, count)

	buf := bytes.NewBuffer(b)
	if err := binary.Read(buf, binary.BigEndian, port); err != nil {
		return nil, err
	}

	return ports, nil
}
<|endoftext|>


<commit_before>package php

import (
	"stephensearles.com/php/ast"
	"stephensearles.com/php/token"
)

type operationType int

const (
	nilOperation operationType = 1 << iota
	unaryOperation
	binaryOperation
	ternaryOperation
	assignmentOperation
	subexpressionBeginOperation
	subexpressionEndOperation
	ignoreErrorOperation
)

func operationTypeForToken(t token.Token) operationType {
	switch t {
	case token.IgnoreErrorOperator:
		return ignoreErrorOperation
	case token.UnaryOperator, token.BitwiseNotOperator:
		return unaryOperation
	case token.AdditionOperator,
		token.SubtractionOperator,
		token.ConcatenationOperator,
		token.ComparisonOperator,
		token.MultOperator,
		token.AndOperator,
		token.OrOperator,
		token.AmpersandOperator,
		token.BitwiseXorOperator,
		token.BitwiseOrOperator,
		token.BitwiseShiftOperator,
		token.WrittenAndOperator,
		token.WrittenXorOperator,
		token.WrittenOrOperator,
		token.InstanceofOperator:
		return binaryOperation
	case token.TernaryOperator1:
		return ternaryOperation
	case token.AssignmentOperator:
		return assignmentOperation
	case token.OpenParen:
		return subexpressionBeginOperation
	case token.CloseParen:
		return subexpressionEndOperation
	}
	return nilOperation
}

func newUnaryOperation(operator Item, expr ast.Expression) ast.OperatorExpression {
	t := ast.Numeric
	if operator.val == "!" {
		t = ast.Boolean
	}
	return ast.OperatorExpression{
		Type:     t,
		Operand1: expr,
		Operator: operator.val,
	}
}

func (p *Parser) newBinaryOperation(operator Item, expr1, expr2 ast.Expression) ast.Expression {
	t := ast.Numeric
	switch operator.typ {
	case token.AssignmentOperator:
		return p.parseAssignmentOperation(expr1, expr2, operator)
	case token.ComparisonOperator, token.AndOperator, token.OrOperator, token.WrittenAndOperator, token.WrittenOrOperator, token.WrittenXorOperator:
		t = ast.Boolean
	case token.ConcatenationOperator:
		t = ast.String
	case token.AmpersandOperator, token.BitwiseXorOperator, token.BitwiseOrOperator, token.BitwiseShiftOperator:
		t = ast.AnyType
	}
	return ast.OperatorExpression{
		Type:     t,
		Operand1: expr1,
		Operand2: expr2,
		Operator: operator.val,
	}
}

func (p *Parser) parseBinaryOperation(lhs ast.Expression, operator Item, originalParenLevel int) ast.Expression {
	p.next()
	rhs := p.parseOperand()
	for {
		nextOperator := p.peek()
		nextOperatorPrecedence, ok := operatorPrecedence[nextOperator.typ]
		if ok && nextOperatorPrecedence > operatorPrecedence[operator.typ] {
			rhs = p.parseOperation(originalParenLevel, rhs)
		} else {
			break
		}
	}
	return p.newBinaryOperation(operator, lhs, rhs)
}

func (p *Parser) parseTernaryOperation(lhs ast.Expression) ast.Expression {
	var truthy ast.Expression
	if p.peek().typ == token.TernaryOperator2 {
		truthy = lhs
	} else {
		truthy = p.parseNextExpression()
	}
	p.expect(token.TernaryOperator2)
	falsy := p.parseNextExpression()
	return &ast.OperatorExpression{
		Operand1: lhs,
		Operand2: truthy,
		Operand3: falsy,
		Type:     truthy.EvaluatesTo() | falsy.EvaluatesTo(),
		Operator: "?:",
	}
}

func (p *Parser) parseUnaryExpressionRight(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}

func (p *Parser) parseUnaryExpressionLeft(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}
<commit_msg>Cleaned up parseBinaryOperation a little bit<commit_after>package php

import (
	"stephensearles.com/php/ast"
	"stephensearles.com/php/token"
)

type operationType int

const (
	nilOperation operationType = 1 << iota
	unaryOperation
	binaryOperation
	ternaryOperation
	assignmentOperation
	subexpressionBeginOperation
	subexpressionEndOperation
	ignoreErrorOperation
)

func operationTypeForToken(t token.Token) operationType {
	switch t {
	case token.IgnoreErrorOperator:
		return ignoreErrorOperation
	case token.UnaryOperator, token.BitwiseNotOperator:
		return unaryOperation
	case token.AdditionOperator,
		token.SubtractionOperator,
		token.ConcatenationOperator,
		token.ComparisonOperator,
		token.MultOperator,
		token.AndOperator,
		token.OrOperator,
		token.AmpersandOperator,
		token.BitwiseXorOperator,
		token.BitwiseOrOperator,
		token.BitwiseShiftOperator,
		token.WrittenAndOperator,
		token.WrittenXorOperator,
		token.WrittenOrOperator,
		token.InstanceofOperator:
		return binaryOperation
	case token.TernaryOperator1:
		return ternaryOperation
	case token.AssignmentOperator:
		return assignmentOperation
	case token.OpenParen:
		return subexpressionBeginOperation
	case token.CloseParen:
		return subexpressionEndOperation
	}
	return nilOperation
}

func newUnaryOperation(operator Item, expr ast.Expression) ast.OperatorExpression {
	t := ast.Numeric
	if operator.val == "!" {
		t = ast.Boolean
	}
	return ast.OperatorExpression{
		Type:     t,
		Operand1: expr,
		Operator: operator.val,
	}
}

func (p *Parser) newBinaryOperation(operator Item, expr1, expr2 ast.Expression) ast.Expression {
	t := ast.Numeric
	switch operator.typ {
	case token.AssignmentOperator:
		return p.parseAssignmentOperation(expr1, expr2, operator)
	case token.ComparisonOperator, token.AndOperator, token.OrOperator, token.WrittenAndOperator, token.WrittenOrOperator, token.WrittenXorOperator:
		t = ast.Boolean
	case token.ConcatenationOperator:
		t = ast.String
	case token.AmpersandOperator, token.BitwiseXorOperator, token.BitwiseOrOperator, token.BitwiseShiftOperator:
		t = ast.AnyType
	}
	return ast.OperatorExpression{
		Type:     t,
		Operand1: expr1,
		Operand2: expr2,
		Operator: operator.val,
	}
}

func (p *Parser) parseBinaryOperation(lhs ast.Expression, operator Item, originalParenLevel int) ast.Expression {
	p.next()
	rhs := p.parseOperand()
	currentPrecedence := operatorPrecedence[operator.typ]
	for {
		nextOperator := p.peek()
		nextPrecedence, ok := operatorPrecedence[nextOperator.typ]
		if !ok || nextPrecedence <= currentPrecedence {
			break
		}
		rhs = p.parseOperation(originalParenLevel, rhs)
	}
	return p.newBinaryOperation(operator, lhs, rhs)
}

func (p *Parser) parseTernaryOperation(lhs ast.Expression) ast.Expression {
	var truthy ast.Expression
	if p.peek().typ == token.TernaryOperator2 {
		truthy = lhs
	} else {
		truthy = p.parseNextExpression()
	}
	p.expect(token.TernaryOperator2)
	falsy := p.parseNextExpression()
	return &ast.OperatorExpression{
		Operand1: lhs,
		Operand2: truthy,
		Operand3: falsy,
		Type:     truthy.EvaluatesTo() | falsy.EvaluatesTo(),
		Operator: "?:",
	}
}

func (p *Parser) parseUnaryExpressionRight(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}

func (p *Parser) parseUnaryExpressionLeft(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 internal

import (
	"fmt"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/kube-openapi/pkg/schemaconv"
	"k8s.io/kube-openapi/pkg/util/proto"
	smdschema "sigs.k8s.io/structured-merge-diff/schema"
	"sigs.k8s.io/structured-merge-diff/typed"
)

// groupVersionKindExtensionKey is the key used to lookup the
// GroupVersionKind value for an object definition from the
// definition's "extensions" map.
const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"

type gvkParser struct {
	gvks   map[schema.GroupVersionKind]string
	parser typed.Parser
}

func (p *gvkParser) Type(gvk schema.GroupVersionKind) typed.ParseableType {
	typeName, ok := p.gvks[gvk]
	if !ok {
		return nil
	}
	return p.parser.Type(typeName)
}

func newGVKParser(models proto.Models) (*gvkParser, error) {
	typeSchema, err := schemaconv.ToSchema(models)
	if err != nil {
		return nil, fmt.Errorf("failed to convert models to schema: %v", err)
	}
	typeSchema = makeRawExtensionUntyped(typeSchema)
	parser := gvkParser{
		gvks: map[schema.GroupVersionKind]string{},
	}
	parser.parser = typed.Parser{Schema: *typeSchema}
	for _, modelName := range models.ListModels() {
		model := models.LookupModel(modelName)
		if model == nil {
			panic("ListModels returns a model that can't be looked-up.")
		}
		gvkList := parseGroupVersionKind(model)
		for _, gvk := range gvkList {
			if len(gvk.Kind) > 0 {
				parser.gvks[gvk] = modelName
			}
		}
	}
	return &parser, nil
}

// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
	extensions := s.GetExtensions()

	gvkListResult := []schema.GroupVersionKind{}

	// Get the extensions
	gvkExtension, ok := extensions[groupVersionKindExtensionKey]
	if !ok {
		return []schema.GroupVersionKind{}
	}

	// gvk extension must be a list of at least 1 element.
	gvkList, ok := gvkExtension.([]interface{})
	if !ok {
		return []schema.GroupVersionKind{}
	}

	for _, gvk := range gvkList {
		// gvk extension list must be a map with group, version, and
		// kind fields
		gvkMap, ok := gvk.(map[interface{}]interface{})
		if !ok {
			continue
		}
		group, ok := gvkMap["group"].(string)
		if !ok {
			continue
		}
		version, ok := gvkMap["version"].(string)
		if !ok {
			continue
		}
		kind, ok := gvkMap["kind"].(string)
		if !ok {
			continue
		}

		gvkListResult = append(gvkListResult, schema.GroupVersionKind{
			Group:   group,
			Version: version,
			Kind:    kind,
		})
	}

	return gvkListResult
}

// makeRawExtensionUntyped explicitly sets RawExtension's type in the schema to Untyped atomic
// TODO: remove this once kube-openapi is updated to include
// https://github.com/kubernetes/kube-openapi/pull/133
func makeRawExtensionUntyped(s *smdschema.Schema) *smdschema.Schema {
	s2 := &smdschema.Schema{}
	for _, t := range s.Types {
		t2 := t
		if t2.Name == "io.k8s.apimachinery.pkg.runtime.RawExtension" {
			t2.Atom = smdschema.Atom{
				Untyped: &smdschema.Untyped{},
			}
		}
		s2.Types = append(s2.Types, t2)
	}
	return s2
}
<commit_msg>Misc fix for feature-serverside-apply<commit_after>/*
Copyright 2018 The Kubernetes 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 internal

import (
	"fmt"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/kube-openapi/pkg/schemaconv"
	"k8s.io/kube-openapi/pkg/util/proto"
	smdschema "sigs.k8s.io/structured-merge-diff/schema"
	"sigs.k8s.io/structured-merge-diff/typed"
)

// groupVersionKindExtensionKey is the key used to lookup the
// GroupVersionKind value for an object definition from the
// definition's "extensions" map.
const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"

type gvkParser struct {
	gvks   map[schema.GroupVersionKind]string
	parser typed.Parser
}

func (p *gvkParser) Type(gvk schema.GroupVersionKind) typed.ParseableType {
	typeName, ok := p.gvks[gvk]
	if !ok {
		return nil
	}
	return p.parser.Type(typeName)
}

func newGVKParser(models proto.Models) (*gvkParser, error) {
	typeSchema, err := schemaconv.ToSchema(models)
	if err != nil {
		return nil, fmt.Errorf("failed to convert models to schema: %v", err)
	}
	typeSchema = makeRawExtensionUntyped(typeSchema)
	parser := gvkParser{
		gvks: map[schema.GroupVersionKind]string{},
	}
	parser.parser = typed.Parser{Schema: *typeSchema}
	for _, modelName := range models.ListModels() {
		model := models.LookupModel(modelName)
		if model == nil {
			panic(fmt.Sprintf("ListModels returns a model that can't be looked-up for: %v", modelName))
		}
		gvkList := parseGroupVersionKind(model)
		for _, gvk := range gvkList {
			if len(gvk.Kind) > 0 {
	                        _, ok := parser.gvks[gvk]
                                if ok {
                                    return nil, fmt.Errorf("Duplicate entry for %v", gvk)
                                }
				parser.gvks[gvk] = modelName
			}
		}
	}
	return &parser, nil
}

// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
	extensions := s.GetExtensions()

	gvkListResult := []schema.GroupVersionKind{}

	// Get the extensions
	gvkExtension, ok := extensions[groupVersionKindExtensionKey]
	if !ok {
		return []schema.GroupVersionKind{}
	}

	// gvk extension must be a list of at least 1 element.
	gvkList, ok := gvkExtension.([]interface{})
	if !ok {
		return []schema.GroupVersionKind{}
	}

	for _, gvk := range gvkList {
		// gvk extension list must be a map with group, version, and
		// kind fields
		gvkMap, ok := gvk.(map[interface{}]interface{})
		if !ok {
			continue
		}
		group, ok := gvkMap["group"].(string)
		if !ok {
			continue
		}
		version, ok := gvkMap["version"].(string)
		if !ok {
			continue
		}
		kind, ok := gvkMap["kind"].(string)
		if !ok {
			continue
		}

		gvkListResult = append(gvkListResult, schema.GroupVersionKind{
			Group:   group,
			Version: version,
			Kind:    kind,
		})
	}

	return gvkListResult
}

// makeRawExtensionUntyped explicitly sets RawExtension's type in the schema to Untyped atomic
// TODO: remove this once kube-openapi is updated to include
// https://github.com/kubernetes/kube-openapi/pull/133
func makeRawExtensionUntyped(s *smdschema.Schema) *smdschema.Schema {
	s2 := &smdschema.Schema{}
	for _, t := range s.Types {
		t2 := t
		if t2.Name == "io.k8s.apimachinery.pkg.runtime.RawExtension" {
			t2.Atom = smdschema.Atom{
				Untyped: &smdschema.Untyped{},
			}
		}
		s2.Types = append(s2.Types, t2)
	}
	return s2
}
<|endoftext|>