<commit_before>package gocsv

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"reflect"
)

// Decoder .
type Decoder interface {
	getCSVRows() ([][]string, error)
}

// SimpleDecoder .
type SimpleDecoder interface {
	getCSVRow() ([]string, error)
	getCSVRows() ([][]string, error)
}

type CSVReader interface {
	Read() ([]string, error)
	ReadAll() ([][]string, error)
}

type csvDecoder struct {
	CSVReader
}

func newSimpleDecoderFromReader(r io.Reader) SimpleDecoder {
	return csvDecoder{getCSVReader(r)}
}

// NewSimpleDecoderFromCSVReader creates a SimpleDecoder, which may be passed
// to the UnmarshalDecoder* family of functions, from a CSV reader. Note that
// encoding/csv.Reader implements CSVReader, so you can pass one of those
// directly here.
func NewSimpleDecoderFromCSVReader(r CSVReader) SimpleDecoder {
	return csvDecoder{r}
}

func (c csvDecoder) getCSVRows() ([][]string, error) {
	return c.ReadAll()
}

func (c csvDecoder) getCSVRow() ([]string, error) {
	return c.Read()
}

func mismatchStructFields(structInfo []fieldInfo, headers []string) []string {
	missing := make([]string, 0)
	if len(structInfo) == 0 {
		return missing
	}

	headerMap := make(map[string]struct{}, len(headers))
	for idx := range headers {
		headerMap[headers[idx]] = struct{}{}
	}

	for _, info := range structInfo {
		found := false
		for _, key := range info.keys {
			if _, ok := headerMap[key]; ok {
				found = true
				break
			}
		}
		if !found {
			missing = append(missing, info.keys...)
		}
	}
	return missing
}

func mismatchHeaderFields(structInfo []fieldInfo, headers []string) []string {
	missing := make([]string, 0)
	if len(headers) == 0 {
		return missing
	}

	keyMap := make(map[string]struct{})
	for _, info := range structInfo {
		for _, key := range info.keys {
			keyMap[key] = struct{}{}
		}
	}

	for _, header := range headers {
		if _, ok := keyMap[header]; !ok {
			missing = append(missing, header)
		}
	}
	return missing
}

func maybeMissingStructFields(structInfo []fieldInfo, headers []string) error {
	missing := mismatchStructFields(structInfo, headers)
	if len(missing) != 0 {
		return fmt.Errorf("found unmatched struct field with tags %v", missing)
	}
	return nil
}

// Check that no header name is repeated twice
func maybeDoubleHeaderNames(headers []string) error {
	headerMap := make(map[string]bool, len(headers))
	for _, v := range headers {
		if _, ok := headerMap[v]; ok {
			return fmt.Errorf("repeated header name: %v", v)
		}
		headerMap[v] = true
	}
	return nil
}

// apply normalizer func to headers
func normalizeHeaders(headers []string) []string {
	out := make([]string, len(headers))
	for i, h := range headers {
		out[i] = normalizeName(h)
	}
	return out
}

func readTo(decoder Decoder, out interface{}) error {
	return readToWithErrorHandler(decoder, nil, out)
}

func readToWithErrorHandler(decoder Decoder, errHandler ErrorHandler, out interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	csvRows, err := decoder.getCSVRows() // Get the CSV csvRows
	if err != nil {
		return err
	}
	if len(csvRows) == 0 {
		return errors.New("empty csv file given")
	}
	if err := ensureOutCapacity(&outValue, len(csvRows)); err != nil { // Ensure the container is big enough to hold the CSV content
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}

	headers := normalizeHeaders(csvRows[0])
	body := csvRows[1:]

	csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV

	headerCount := map[string]int{}
	for i, csvColumnHeader := range headers {
		curHeaderCount := headerCount[csvColumnHeader]
		if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
			csvHeadersLabels[i] = fieldInfo
			if ShouldAlignDuplicateHeadersWithStructFieldOrder {
				curHeaderCount++
				headerCount[csvColumnHeader] = curHeaderCount
			}
		}
	}

	if FailIfUnmatchedStructTags {
		if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
			return err
		}
	}
	if FailIfDoubleHeaderNames {
		if err := maybeDoubleHeaderNames(headers); err != nil {
			return err
		}
	}

	for i, csvRow := range body {
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range csvRow {
			if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
				if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
					parseError := csv.ParseError{
						Line:   i + 2, //add 2 to account for the header & 0-indexing of arrays
						Column: j + 1,
						Err:    err,
					}
					if errHandler == nil || !errHandler(&parseError) {
						return &parseError
					}
				}
			}
		}
		outValue.Index(i).Set(outInner)
	}
	return nil
}

func readEach(decoder SimpleDecoder, c interface{}) error {
	headers, err := decoder.getCSVRow()
	if err != nil {
		return err
	}
	headers = normalizeHeaders(headers)

	outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	defer outValue.Close()
	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}
	csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV
	headerCount := map[string]int{}
	for i, csvColumnHeader := range headers {
		curHeaderCount := headerCount[csvColumnHeader]
		if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
			csvHeadersLabels[i] = fieldInfo
			if ShouldAlignDuplicateHeadersWithStructFieldOrder {
				curHeaderCount++
				headerCount[csvColumnHeader] = curHeaderCount
			}
		}
	}
	if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
		if FailIfUnmatchedStructTags {
			return err
		}
	}
	if FailIfDoubleHeaderNames {
		if err := maybeDoubleHeaderNames(headers); err != nil {
			return err
		}
	}
	i := 0
	for {
		line, err := decoder.getCSVRow()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range line {
			if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
				if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
					return &csv.ParseError{
						Line:   i + 2, //add 2 to account for the header & 0-indexing of arrays
						Column: j + 1,
						Err:    err,
					}
				}
			}
		}
		outValue.Send(outInner)
		i++
	}
	return nil
}

func readEachWithoutHeaders(decoder SimpleDecoder, c interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	defer outValue.Close()

	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}

	i := 0
	for {
		line, err := decoder.getCSVRow()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range line {
			fieldInfo := outInnerStructInfo.Fields[j]
			if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
				return &csv.ParseError{
					Line:   i + 2, //add 2 to account for the header & 0-indexing of arrays
					Column: j + 1,
					Err:    err,
				}
			}
		}
		outValue.Send(outInner)
		i++
	}
	return nil
}

func readToWithoutHeaders(decoder Decoder, out interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	csvRows, err := decoder.getCSVRows() // Get the CSV csvRows
	if err != nil {
		return err
	}
	if len(csvRows) == 0 {
		return errors.New("empty csv file given")
	}
	if err := ensureOutCapacity(&outValue, len(csvRows)+1); err != nil { // Ensure the container is big enough to hold the CSV content
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}

	for i, csvRow := range csvRows {
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range csvRow {
			fieldInfo := outInnerStructInfo.Fields[j]
			if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
				return &csv.ParseError{
					Line:   i + 1,
					Column: j + 1,
					Err:    err,
				}
			}
		}
		outValue.Index(i).Set(outInner)
	}

	return nil
}

// Check if the outType is an array or a slice
func ensureOutType(outType reflect.Type) error {
	switch outType.Kind() {
	case reflect.Slice:
		fallthrough
	case reflect.Chan:
		fallthrough
	case reflect.Array:
		return nil
	}
	return fmt.Errorf("cannot use " + outType.String() + ", only slice or array supported")
}

// Check if the outInnerType is of type struct
func ensureOutInnerType(outInnerType reflect.Type) error {
	switch outInnerType.Kind() {
	case reflect.Struct:
		return nil
	}
	return fmt.Errorf("cannot use " + outInnerType.String() + ", only struct supported")
}

func ensureOutCapacity(out *reflect.Value, csvLen int) error {
	switch out.Kind() {
	case reflect.Array:
		if out.Len() < csvLen-1 { // Array is not big enough to hold the CSV content (arrays are not addressable)
			return fmt.Errorf("array capacity problem: cannot store %d %s in %s", csvLen-1, out.Type().Elem().String(), out.Type().String())
		}
	case reflect.Slice:
		if !out.CanAddr() && out.Len() < csvLen-1 { // Slice is not big enough tho hold the CSV content and is not addressable
			return fmt.Errorf("slice capacity problem and is not addressable (did you forget &?)")
		} else if out.CanAddr() && out.Len() < csvLen-1 {
			out.Set(reflect.MakeSlice(out.Type(), csvLen-1, csvLen-1)) // Slice is not big enough, so grows it
		}
	}
	return nil
}

func getCSVFieldPosition(key string, structInfo *structInfo, curHeaderCount int) *fieldInfo {
	matchedFieldCount := 0
	for _, field := range structInfo.Fields {
		if field.matchesKey(key) {
			if matchedFieldCount >= curHeaderCount {
				return &field
			}
			matchedFieldCount++
		}
	}
	return nil
}

func createNewOutInner(outInnerWasPointer bool, outInnerType reflect.Type) reflect.Value {
	if outInnerWasPointer {
		return reflect.New(outInnerType)
	}
	return reflect.New(outInnerType).Elem()
}

func setInnerField(outInner *reflect.Value, outInnerWasPointer bool, index []int, value string, omitEmpty bool) error {
	oi := *outInner
	if outInnerWasPointer {
		// initialize nil pointer
		if oi.IsNil() {
			setField(oi, "", omitEmpty)
		}
		oi = outInner.Elem()
	}
	// because pointers can be nil need to recurse one index at a time and perform nil check
	if len(index) > 1 {
		nextField := oi.Field(index[0])
		return setInnerField(&nextField, nextField.Kind() == reflect.Ptr, index[1:], value, omitEmpty)
	}
	return setField(oi.FieldByIndex(index), value, omitEmpty)
}
<commit_msg>assert chan only for readEach func and defer close early<commit_after>package gocsv

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"reflect"
)

// Decoder .
type Decoder interface {
	getCSVRows() ([][]string, error)
}

// SimpleDecoder .
type SimpleDecoder interface {
	getCSVRow() ([]string, error)
	getCSVRows() ([][]string, error)
}

type CSVReader interface {
	Read() ([]string, error)
	ReadAll() ([][]string, error)
}

type csvDecoder struct {
	CSVReader
}

func newSimpleDecoderFromReader(r io.Reader) SimpleDecoder {
	return csvDecoder{getCSVReader(r)}
}

// NewSimpleDecoderFromCSVReader creates a SimpleDecoder, which may be passed
// to the UnmarshalDecoder* family of functions, from a CSV reader. Note that
// encoding/csv.Reader implements CSVReader, so you can pass one of those
// directly here.
func NewSimpleDecoderFromCSVReader(r CSVReader) SimpleDecoder {
	return csvDecoder{r}
}

func (c csvDecoder) getCSVRows() ([][]string, error) {
	return c.ReadAll()
}

func (c csvDecoder) getCSVRow() ([]string, error) {
	return c.Read()
}

func mismatchStructFields(structInfo []fieldInfo, headers []string) []string {
	missing := make([]string, 0)
	if len(structInfo) == 0 {
		return missing
	}

	headerMap := make(map[string]struct{}, len(headers))
	for idx := range headers {
		headerMap[headers[idx]] = struct{}{}
	}

	for _, info := range structInfo {
		found := false
		for _, key := range info.keys {
			if _, ok := headerMap[key]; ok {
				found = true
				break
			}
		}
		if !found {
			missing = append(missing, info.keys...)
		}
	}
	return missing
}

func mismatchHeaderFields(structInfo []fieldInfo, headers []string) []string {
	missing := make([]string, 0)
	if len(headers) == 0 {
		return missing
	}

	keyMap := make(map[string]struct{})
	for _, info := range structInfo {
		for _, key := range info.keys {
			keyMap[key] = struct{}{}
		}
	}

	for _, header := range headers {
		if _, ok := keyMap[header]; !ok {
			missing = append(missing, header)
		}
	}
	return missing
}

func maybeMissingStructFields(structInfo []fieldInfo, headers []string) error {
	missing := mismatchStructFields(structInfo, headers)
	if len(missing) != 0 {
		return fmt.Errorf("found unmatched struct field with tags %v", missing)
	}
	return nil
}

// Check that no header name is repeated twice
func maybeDoubleHeaderNames(headers []string) error {
	headerMap := make(map[string]bool, len(headers))
	for _, v := range headers {
		if _, ok := headerMap[v]; ok {
			return fmt.Errorf("repeated header name: %v", v)
		}
		headerMap[v] = true
	}
	return nil
}

// apply normalizer func to headers
func normalizeHeaders(headers []string) []string {
	out := make([]string, len(headers))
	for i, h := range headers {
		out[i] = normalizeName(h)
	}
	return out
}

func readTo(decoder Decoder, out interface{}) error {
	return readToWithErrorHandler(decoder, nil, out)
}

func readToWithErrorHandler(decoder Decoder, errHandler ErrorHandler, out interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	csvRows, err := decoder.getCSVRows() // Get the CSV csvRows
	if err != nil {
		return err
	}
	if len(csvRows) == 0 {
		return errors.New("empty csv file given")
	}
	if err := ensureOutCapacity(&outValue, len(csvRows)); err != nil { // Ensure the container is big enough to hold the CSV content
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}

	headers := normalizeHeaders(csvRows[0])
	body := csvRows[1:]

	csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV

	headerCount := map[string]int{}
	for i, csvColumnHeader := range headers {
		curHeaderCount := headerCount[csvColumnHeader]
		if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
			csvHeadersLabels[i] = fieldInfo
			if ShouldAlignDuplicateHeadersWithStructFieldOrder {
				curHeaderCount++
				headerCount[csvColumnHeader] = curHeaderCount
			}
		}
	}

	if FailIfUnmatchedStructTags {
		if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
			return err
		}
	}
	if FailIfDoubleHeaderNames {
		if err := maybeDoubleHeaderNames(headers); err != nil {
			return err
		}
	}

	for i, csvRow := range body {
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range csvRow {
			if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
				if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
					parseError := csv.ParseError{
						Line:   i + 2, //add 2 to account for the header & 0-indexing of arrays
						Column: j + 1,
						Err:    err,
					}
					if errHandler == nil || !errHandler(&parseError) {
						return &parseError
					}
				}
			}
		}
		outValue.Index(i).Set(outInner)
	}
	return nil
}

func readEach(decoder SimpleDecoder, c interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer)
	if outType.Kind() != reflect.Chan {
		return fmt.Errorf("cannot use %v with type %s, only channel supported", c, outType)
	}
	defer outValue.Close()

	headers, err := decoder.getCSVRow()
	if err != nil {
		return err
	}
	headers = normalizeHeaders(headers)

	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}
	csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV
	headerCount := map[string]int{}
	for i, csvColumnHeader := range headers {
		curHeaderCount := headerCount[csvColumnHeader]
		if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
			csvHeadersLabels[i] = fieldInfo
			if ShouldAlignDuplicateHeadersWithStructFieldOrder {
				curHeaderCount++
				headerCount[csvColumnHeader] = curHeaderCount
			}
		}
	}
	if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
		if FailIfUnmatchedStructTags {
			return err
		}
	}
	if FailIfDoubleHeaderNames {
		if err := maybeDoubleHeaderNames(headers); err != nil {
			return err
		}
	}
	i := 0
	for {
		line, err := decoder.getCSVRow()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range line {
			if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
				if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
					return &csv.ParseError{
						Line:   i + 2, //add 2 to account for the header & 0-indexing of arrays
						Column: j + 1,
						Err:    err,
					}
				}
			}
		}
		outValue.Send(outInner)
		i++
	}
	return nil
}

func readEachWithoutHeaders(decoder SimpleDecoder, c interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	defer outValue.Close()

	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}

	i := 0
	for {
		line, err := decoder.getCSVRow()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range line {
			fieldInfo := outInnerStructInfo.Fields[j]
			if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
				return &csv.ParseError{
					Line:   i + 2, //add 2 to account for the header & 0-indexing of arrays
					Column: j + 1,
					Err:    err,
				}
			}
		}
		outValue.Send(outInner)
		i++
	}
	return nil
}

func readToWithoutHeaders(decoder Decoder, out interface{}) error {
	outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
	if err := ensureOutType(outType); err != nil {
		return err
	}
	outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
	if err := ensureOutInnerType(outInnerType); err != nil {
		return err
	}
	csvRows, err := decoder.getCSVRows() // Get the CSV csvRows
	if err != nil {
		return err
	}
	if len(csvRows) == 0 {
		return errors.New("empty csv file given")
	}
	if err := ensureOutCapacity(&outValue, len(csvRows)+1); err != nil { // Ensure the container is big enough to hold the CSV content
		return err
	}
	outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
	if len(outInnerStructInfo.Fields) == 0 {
		return errors.New("no csv struct tags found")
	}

	for i, csvRow := range csvRows {
		outInner := createNewOutInner(outInnerWasPointer, outInnerType)
		for j, csvColumnContent := range csvRow {
			fieldInfo := outInnerStructInfo.Fields[j]
			if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
				return &csv.ParseError{
					Line:   i + 1,
					Column: j + 1,
					Err:    err,
				}
			}
		}
		outValue.Index(i).Set(outInner)
	}

	return nil
}

// Check if the outType is an array or a slice
func ensureOutType(outType reflect.Type) error {
	switch outType.Kind() {
	case reflect.Slice:
		fallthrough
	case reflect.Chan:
		fallthrough
	case reflect.Array:
		return nil
	}
	return fmt.Errorf("cannot use " + outType.String() + ", only slice or array supported")
}

// Check if the outInnerType is of type struct
func ensureOutInnerType(outInnerType reflect.Type) error {
	switch outInnerType.Kind() {
	case reflect.Struct:
		return nil
	}
	return fmt.Errorf("cannot use " + outInnerType.String() + ", only struct supported")
}

func ensureOutCapacity(out *reflect.Value, csvLen int) error {
	switch out.Kind() {
	case reflect.Array:
		if out.Len() < csvLen-1 { // Array is not big enough to hold the CSV content (arrays are not addressable)
			return fmt.Errorf("array capacity problem: cannot store %d %s in %s", csvLen-1, out.Type().Elem().String(), out.Type().String())
		}
	case reflect.Slice:
		if !out.CanAddr() && out.Len() < csvLen-1 { // Slice is not big enough tho hold the CSV content and is not addressable
			return fmt.Errorf("slice capacity problem and is not addressable (did you forget &?)")
		} else if out.CanAddr() && out.Len() < csvLen-1 {
			out.Set(reflect.MakeSlice(out.Type(), csvLen-1, csvLen-1)) // Slice is not big enough, so grows it
		}
	}
	return nil
}

func getCSVFieldPosition(key string, structInfo *structInfo, curHeaderCount int) *fieldInfo {
	matchedFieldCount := 0
	for _, field := range structInfo.Fields {
		if field.matchesKey(key) {
			if matchedFieldCount >= curHeaderCount {
				return &field
			}
			matchedFieldCount++
		}
	}
	return nil
}

func createNewOutInner(outInnerWasPointer bool, outInnerType reflect.Type) reflect.Value {
	if outInnerWasPointer {
		return reflect.New(outInnerType)
	}
	return reflect.New(outInnerType).Elem()
}

func setInnerField(outInner *reflect.Value, outInnerWasPointer bool, index []int, value string, omitEmpty bool) error {
	oi := *outInner
	if outInnerWasPointer {
		// initialize nil pointer
		if oi.IsNil() {
			setField(oi, "", omitEmpty)
		}
		oi = outInner.Elem()
	}
	// because pointers can be nil need to recurse one index at a time and perform nil check
	if len(index) > 1 {
		nextField := oi.Field(index[0])
		return setInnerField(&nextField, nextField.Kind() == reflect.Ptr, index[1:], value, omitEmpty)
	}
	return setField(oi.FieldByIndex(index), value, omitEmpty)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"gopkg.in/olivere/elastic.v3"
	"net/url"
	"strings"
	"time"
)

// SearchResultTiming is used to measure timings at various steps in the request, in microseconds.
type SearchResultTiming struct {

	// Query times reported by ElasticSearch
	DocsQuery uint32 `json:"dq"`
	TextQuery uint32 `json:"tq"`

	// Total request times measured on our side
	DocsRequest uint32 `json:"dr"`
	TextRequest uint32 `json:"tr"`

	// Total processing time on our end
	Total uint32 `json:"o"`
}

// Hit defines a matching document.
type Hit struct {
	ID      string `json:"i"`
	URL     string `json:"u"`
	Title   string `json:"t"`
	Summary string `json:"s"`
}

// SearchResult defines the result for a query, passed to the template.
type SearchResult struct {
	Hits     []Hit              `json:"h,omitempty"`
	Redirect string             `json:"r,omitempty"`
	HasMore  bool               `json:"m,omitempty"`
	Timing   SearchResultTiming `json:"t,omitempty"`
}

// SearchRequest entirely defines a search request.
type SearchRequest struct {
	Query string `json:"q"`
	Page  int    `json:"p"`
	Lang  string `json:"g"`
}

// Href returns the relative URL of this search.
// Same function is implemented on the JavaScript side
func (req SearchRequest) Href() string {

	var components []string

	if req.Lang != "" {
		components = append(components, fmt.Sprintf("g=%s", req.Lang))
	}

	if req.Page > 1 && req.Query != "" {
		components = append(components, fmt.Sprintf("p=%d", req.Page))
	}

	if req.Query != "" {
		components = append(components, "q="+url.QueryEscape(req.Query))
	}

	if len(components) == 0 {
		return "/"
	}

	return "/?" + strings.Join(components, "&")

}

// PreviousPageHref returns the relative URL of the previous page for this search.
func (req SearchRequest) PreviousPageHref() string {
	if req.Page < 2 {
		return req.Href()
	}
	prev := req
	prev.Page--
	return prev.Href()
}

// NextPageHref returns the relative URL of the next page for this search.
func (req SearchRequest) NextPageHref() string {
	next := req
	next.Page++
	if next.Page == 1 {
		next.Page++
	}
	return next.Href()
}

// BuildTextRequest returns a JSON-encoded Elasticsearch query body for the text index.
func (req SearchRequest) BuildTextRequest() (string, error) {

	var scoringFunctions []string

	jsonQuery, err := json.Marshal(req.Query)
	if err != nil {
		return "", err
	}

	scoringFunctions = append(scoringFunctions, `{
	  	"field_value_factor": {
	      "field": "rank",
	      "factor": 1,
	      "missing": 0
	    }
	}`)

	if req.Lang != "all" {
		scoringFunctions = append(scoringFunctions, fmt.Sprintf(`{
		  	"field_value_factor": {
                "field": "lang_%s",
                "missing": 0.002
            }
		}`, req.Lang))
	}

	// TODO: remove whitespace?
	textEsBody := fmt.Sprintf(`{
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query": %s,
              "minimum_should_match": "-25%%",
              "type": "cross_fields",
	          "tie_breaker": 0.5,
	          "fields": ["title^3", "body", "url_words^2", "domain_words^8"]
            }

          },
          "functions": [%s]
        }
      },
      "from": %d,
      "size": %d
    }`, jsonQuery, strings.Join(scoringFunctions, ","), (req.Page-1)*Config.ResultPageSize, Config.ResultPageSize)

	return textEsBody, nil
}

// BuildDocsRequest returns a JSON-encoded Elasticsearch query body for the docs index.
func BuildDocsRequest(textSearchResult *elastic.SearchResult) string {

	// Collect the IDs
	ids := make([]string, len(textSearchResult.Hits.Hits))
	for i, hit := range textSearchResult.Hits.Hits {
		ids[i] = hit.Id
	}

	return fmt.Sprintf(`{
      "query": {
        "filtered": {
          "filter": {
            "bool": {
              "must": [{
                "ids": {
                  "type": "page",
                  "values": ["%s"]
                }
              }]
            }
          }
        }
      }
    }`, strings.Join(ids, `","`))

}

// PerformSearch performs the search itself and returns a SearchResult.
// We are doing 2 Elasticsearch requests:
//  - First to the "Text" server, to get matching docIDs
//  - Then to the "Docs" server with these IDs, to get the document titles/summaries
func (req SearchRequest) PerformSearch() (*SearchResult, error) {

	page := SearchResult{}

	redirect := DetectBang(req.Query, req.Lang)

	if redirect != "" {
		page.Redirect = redirect
		return &page, nil
	}

	if Config.TestData {
		return req.GenerateTestData(), nil
	}

	textEsBody, err := req.BuildTextRequest()
	if err != nil {
		return nil, err
	}

	// fmt.Println(textEsBody)

	textSearchResult, textRequestTime, err := ElasticsearchRequest(
		ElasticsearchTextClient,
		"/text/page/_search",
		textEsBody)

	if err != nil {
		return nil, err
	}

	page.Timing.TextRequest = uint32(textRequestTime.Seconds() * 1000000)
	page.Timing.TextQuery = uint32(textSearchResult.TookInMillis * 1000)

	// No results!
	if textSearchResult.Hits == nil || len(textSearchResult.Hits.Hits) == 0 {
		return &page, nil
	}

	// TODO: use ES count to determine that
	// TODO: also return textSearchResult.Hits.TotalHits
	page.HasMore = (len(textSearchResult.Hits.Hits) >= Config.ResultPageSize)

	docsEsBody := BuildDocsRequest(textSearchResult)

	docsSearchResult, docsRequestTime, err := ElasticsearchRequest(
		ElasticsearchDocsClient,
		"/docs/page/_search?fields=title,summary,url&size=100",
		docsEsBody)

	if err != nil {
		return nil, err
	}

	page.Timing.DocsRequest = uint32(docsRequestTime.Seconds() * 1000000)
	page.Timing.DocsQuery = uint32(docsSearchResult.TookInMillis * 1000)

	// No results! This shouldn't happen, are we missing documents?
	if docsSearchResult.Hits == nil {
		return &page, nil
	}

	hitsByIds := make(map[string]*Hit, len(docsSearchResult.Hits.Hits))

	// Iterate through results and convert them in their final struct
	for _, hit := range docsSearchResult.Hits.Hits {
		hitsByIds[hit.Id] = &Hit{
			ID:      hit.Id,
			URL:     hit.Fields["url"].([]interface{})[0].(string),
			Title:   hit.Fields["title"].([]interface{})[0].(string),
			Summary: hit.Fields["summary"].([]interface{})[0].(string)}
	}

	// Restore the original order of the text results.
	for _, hit := range textSearchResult.Hits.Hits {
		if hitsByIds[hit.Id] != nil {
			page.Hits = append(page.Hits, *hitsByIds[hit.Id])
		}
	}

	return &page, nil
}

// PerformSearchWithTiming adds a Timing.Total to PerformSearch().
func (req SearchRequest) PerformSearchWithTiming() (*SearchResult, error) {

	start := time.Now()

	page, err := req.PerformSearch()

	if page != nil {
		page.Timing.Total = uint32(time.Since(start).Seconds() * 1000000)
	}

	return page, err
}

// GenerateTestData creates a mock data result for tests
func (req SearchRequest) GenerateTestData() *SearchResult {

	return &SearchResult{
		Hits: []Hit{
			Hit{
				ID:      "1",
				Title:   "Page 1",
				URL:     "http://www.example.com/page/1",
				Summary: "summary 1",
			},
			Hit{
				ID:      "2",
				Title:   "Page 2",
				URL:     "http://www.example.com/page/2",
				Summary: "summary 2",
			},
		},
	}
}
<commit_msg>Added Highlighting<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"gopkg.in/olivere/elastic.v3"
	"net/url"
	"strings"
	"time"
)

// SearchResultTiming is used to measure timings at various steps in the request, in microseconds.
type SearchResultTiming struct {

	// Query times reported by ElasticSearch
	DocsQuery uint32 `json:"dq"`
	TextQuery uint32 `json:"tq"`

	// Total request times measured on our side
	DocsRequest uint32 `json:"dr"`
	TextRequest uint32 `json:"tr"`

	// Total processing time on our end
	Total uint32 `json:"o"`
}

// Hit defines a matching document.
type Hit struct {
	ID      string `json:"i"`
	URL     string `json:"u"`
	Title   string `json:"t"`
	Summary string `json:"s"`
}

// SearchResult defines the result for a query, passed to the template.
type SearchResult struct {
	Hits     []Hit              `json:"h,omitempty"`
	Redirect string             `json:"r,omitempty"`
	HasMore  bool               `json:"m,omitempty"`
	Timing   SearchResultTiming `json:"t,omitempty"`
}

// SearchRequest entirely defines a search request.
type SearchRequest struct {
	Query string `json:"q"`
	Page  int    `json:"p"`
	Lang  string `json:"g"`
}

// Href returns the relative URL of this search.
// Same function is implemented on the JavaScript side
func (req SearchRequest) Href() string {

	var components []string

	if req.Lang != "" {
		components = append(components, fmt.Sprintf("g=%s", req.Lang))
	}

	if req.Page > 1 && req.Query != "" {
		components = append(components, fmt.Sprintf("p=%d", req.Page))
	}

	if req.Query != "" {
		components = append(components, "q="+url.QueryEscape(req.Query))
	}

	if len(components) == 0 {
		return "/"
	}

	return "/?" + strings.Join(components, "&")

}

// PreviousPageHref returns the relative URL of the previous page for this search.
func (req SearchRequest) PreviousPageHref() string {
	if req.Page < 2 {
		return req.Href()
	}
	prev := req
	prev.Page--
	return prev.Href()
}

// NextPageHref returns the relative URL of the next page for this search.
func (req SearchRequest) NextPageHref() string {
	next := req
	next.Page++
	if next.Page == 1 {
		next.Page++
	}
	return next.Href()
}

// BuildTextRequest returns a JSON-encoded Elasticsearch query body for the text index.
func (req SearchRequest) BuildTextRequest() (string, error) {

	var scoringFunctions []string

	jsonQuery, err := json.Marshal(req.Query)
	if err != nil {
		return "", err
	}

	scoringFunctions = append(scoringFunctions, `{
	  	"field_value_factor": {
	      "field": "rank",
	      "factor": 1,
	      "missing": 0
	    }
	}`)

	if req.Lang != "all" {
		scoringFunctions = append(scoringFunctions, fmt.Sprintf(`{
		  	"field_value_factor": {
                "field": "lang_%s",
                "missing": 0.002
            }
		}`, req.Lang))
	}

	// TODO: remove whitespace?
	textEsBody := fmt.Sprintf(`{
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query": %s,
              "minimum_should_match": "-25%%",
              "type": "cross_fields",
	          "tie_breaker": 0.5,
	          "fields": ["title^3", "body", "url_words^2", "domain_words^8"]
            }

          },
          "functions": [%s]
        }
      },
      "from": %d,
      "size": %d
    }`, jsonQuery, strings.Join(scoringFunctions, ","), (req.Page-1)*Config.ResultPageSize, Config.ResultPageSize)

	return textEsBody, nil
}

// BuildDocsRequest returns a JSON-encoded Elasticsearch query body for the docs index.
func BuildDocsRequest(textSearchResult *elastic.SearchResult) string {

	// Collect the IDs
	ids := make([]string, len(textSearchResult.Hits.Hits))
	for i, hit := range textSearchResult.Hits.Hits {
		ids[i] = hit.Id
	}

	return fmt.Sprintf(`{
      "query": {
        "filtered": {
          "filter": {
            "bool": {
              "must": [{
                "ids": {
                  "type": "page",
                  "values": ["%s"]
                }
              }]
            }
          }
        }
      }
    }`, strings.Join(ids, `","`))

}

// PerformSearch performs the search itself and returns a SearchResult.
// We are doing 2 Elasticsearch requests:
//  - First to the "Text" server, to get matching docIDs
//  - Then to the "Docs" server with these IDs, to get the document titles/summaries
func (req SearchRequest) PerformSearch() (*SearchResult, error) {

	page := SearchResult{}

	redirect := DetectBang(req.Query, req.Lang)

	if redirect != "" {
		page.Redirect = redirect
		return &page, nil
	}

	if Config.TestData {
		return req.GenerateTestData(), nil
	}

	textEsBody, err := req.BuildTextRequest()
	if err != nil {
		return nil, err
	}

	// fmt.Println(textEsBody)

	textSearchResult, textRequestTime, err := ElasticsearchRequest(
		ElasticsearchTextClient,
		"/text/page/_search",
		textEsBody)

	if err != nil {
		return nil, err
	}

	page.Timing.TextRequest = uint32(textRequestTime.Seconds() * 1000000)
	page.Timing.TextQuery = uint32(textSearchResult.TookInMillis * 1000)

	// No results!
	if textSearchResult.Hits == nil || len(textSearchResult.Hits.Hits) == 0 {
		return &page, nil
	}

	// TODO: use ES count to determine that
	// TODO: also return textSearchResult.Hits.TotalHits
	page.HasMore = (len(textSearchResult.Hits.Hits) >= Config.ResultPageSize)

	docsEsBody := BuildDocsRequest(textSearchResult)

	docsSearchResult, docsRequestTime, err := ElasticsearchRequest(
		ElasticsearchDocsClient,
		"/docs/page/_search?fields=title,summary,url&size=100",
		docsEsBody)

	if err != nil {
		return nil, err
	}

	page.Timing.DocsRequest = uint32(docsRequestTime.Seconds() * 1000000)
	page.Timing.DocsQuery = uint32(docsSearchResult.TookInMillis * 1000)

	// No results! This shouldn't happen, are we missing documents?
	if docsSearchResult.Hits == nil {
		return &page, nil
	}

	hitsByIds := make(map[string]*Hit, len(docsSearchResult.Hits.Hits))

	// Iterate through results and convert them in their final struct
	for _, hit := range docsSearchResult.Hits.Hits {
		hitsByIds[hit.Id] = &Hit{
			ID:      hit.Id,
			URL:     hit.Fields["url"].([]interface{})[0].(string),
			Title:   hit.Fields["title"].([]interface{})[0].(string),
			Summary: hit.Fields["summary"].([]interface{})[0].(string)}

		//Call Highlighung Function
		hitsByIds[hit.Id].AddHighlighting(req.Query)
	}

	// Restore the original order of the text results.
	for _, hit := range textSearchResult.Hits.Hits {
		if hitsByIds[hit.Id] != nil {
			page.Hits = append(page.Hits, *hitsByIds[hit.Id])
		}
	}

	return &page, nil
}

//separate Highlighting function for Title and Summary
func (hit Hit) AddHighlighting(query string){
	hit.Title = strings.Replace(hit.Title, " "+query+" ", " "+query+" ",-1)
	hit.Summary = strings.Replace(hit.Summary, " "+query+" ", " "+query+" ",-1)
}

// PerformSearchWithTiming adds a Timing.Total to PerformSearch().
func (req SearchRequest) PerformSearchWithTiming() (*SearchResult, error) {

	start := time.Now()

	page, err := req.PerformSearch()

	if page != nil {
		page.Timing.Total = uint32(time.Since(start).Seconds() * 1000000)
	}

	return page, err
}

// GenerateTestData creates a mock data result for tests
func (req SearchRequest) GenerateTestData() *SearchResult {

	return &SearchResult{
		Hits: []Hit{
			Hit{
				ID:      "1",
				Title:   "Page 1",
				URL:     "http://www.example.com/page/1",
				Summary: "summary 1",
			},
			Hit{
				ID:      "2",
				Title:   "Page 2",
				URL:     "http://www.example.com/page/2",
				Summary: "summary 2",
			},
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Cutehacks AS. All rights reserved.
// License can be found in the LICENSE file.

package common

import (
	//	"crypto/x509/pkix"
	//	"debug/elf"
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	msg "qpm.io/common/messages"
	"qpm.io/qpm/core"
	"regexp"
	"strings"
	"text/template"
	"path/filepath"
)

const (
	ERR_REQUIRED_FIELD  = "%s is a required field"
	ERR_FORMATTED_FIELD = "%s requires a specific format"
)

var (
	regexPackageName = regexp.MustCompile("^[a-zA-Z]{2,}\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]?$")
	regexVersion     = regexp.MustCompile("[0-9].[0-9].[0-9]*")
	regexAuthorName  = regexp.MustCompile("^[\\p{L}\\s'.-]+$")
	regexAuthorEmail = regexp.MustCompile(".+@.+\\..+")
	regexGitSha1     = regexp.MustCompile("^[a-fA-F0-9]{8,}$")
	regexRevision    = map[msg.RepoType]*regexp.Regexp{
		msg.RepoType_GITHUB: regexGitSha1,
	}
)

func dotSlash(dots string) string {
	return strings.Replace(dots, ".", "/", -1)
}

func dotUnderscore(dots string) string {
	return strings.Replace(dots, ".", "_", -1)
}

type DependencyList map[string]string

// Creates a new DependencyList (which is really a map) which takes a list of package
// names of the form "package@version" and produces a map of "package => "version".
// Passing in multiple versions of the same package will overwrite with the last one.
func NewDependencyList(packages []string) DependencyList {
	deps := DependencyList{}
	for _, dep := range packages {
		parts := strings.Split(dep, "@")
		pName := strings.ToLower(parts[0])

		if len(parts) > 1 {
			deps[pName] = strings.ToLower(parts[1])
		} else {
			deps[pName] = ""
		}
	}
	return deps
}

func NewPackage() *msg.Package {
	return &msg.Package{
		Name:        "",
		Description: "",
		Version: &msg.Package_Version{
			Label: "0.0.1",
		},
		Author: &msg.Package_Author{
			Name:  "",
			Email: "",
		},
		Dependencies: []string{},
		Repository: &msg.Package_Repository{
			Type: msg.RepoType_GITHUB,
			Url:  "",
		},
		License: msg.LicenseType_MIT,
	}
}

type PackageWrapper struct {
	*msg.Package
	ID int // only used on server
	FilePath string
}

func LoadPackage(path string) (*PackageWrapper, error) {
	pw := &PackageWrapper{Package: &msg.Package{}}

	packageFile := filepath.Join(path, core.PackageFile)

	if _, err := os.Stat(packageFile); err == nil {

		file, err := os.Open(packageFile)
		if err != nil {
			return pw, err
		}
		defer file.Close()

		dec := json.NewDecoder(file)

		if err := dec.Decode(pw.Package); err != nil {
			return pw, err
		}

		pw.FilePath, err = filepath.Abs(file.Name())
		if err != nil {
			return pw, err
		}
	}

	return pw, nil
}

func (pw PackageWrapper) Save() error {
	var file *os.File
	var err error

	// re-create the core.PackageFile file
	file, err = os.Create(core.PackageFile)

	if err != nil {
		return err
	}
	defer file.Close()

	b, err := json.Marshal(pw.Package)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	json.Indent(&buf, b, "", "  ")
	buf.WriteTo(file)
	return nil
}

func (pw PackageWrapper) ParseDependencies() DependencyList {
	return NewDependencyList(pw.Dependencies)
}

func (pw PackageWrapper) Validate() error {
	if pw.Name == "" {
		return fmt.Errorf(ERR_REQUIRED_FIELD, "name")
	} else {
		// Validate name
		if !regexPackageName.MatchString(pw.Name) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "name")
		}
	}
	if pw.Version == nil {
		return fmt.Errorf(ERR_REQUIRED_FIELD, "version")
	} else {
		// Validate version label
		if !regexVersion.MatchString(pw.Version.Label) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "version label")
		}
		// Validate version revision
		if pw.Version.Revision == "" {
			return fmt.Errorf(ERR_REQUIRED_FIELD, "version revision")
		} else {
			if !regexRevision[pw.Repository.Type].MatchString(pw.Version.Revision) {
				return fmt.Errorf(ERR_FORMATTED_FIELD, "version revision")
			}
		}
	}
	if pw.Author == nil {
		return fmt.Errorf(ERR_REQUIRED_FIELD, "author")
	} else {
		// Validate author name
		if !regexAuthorName.MatchString(pw.Author.Name) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "author name")
		}
		//Validate author email
		if !regexAuthorEmail.MatchString(pw.Author.Email) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "author email")
		}
	}

	return nil
}

func (pw PackageWrapper) PriFile() string {
	return dotUnderscore(pw.Package.Name) + ".pri"
}

func (pw PackageWrapper) QrcFile() string {
	return dotUnderscore(pw.Package.Name) + ".qrc"
}

func (pw PackageWrapper) QrcPrefix() string {
	return dotSlash(pw.Package.Name)
}

var (
	vendorPri = template.Must(template.New("vendorPri").Parse(`
DEFINES += QPM_INIT\\(E\\)=\"E.addImportPath(QStringLiteral(\\\"qrc:/\\\"));\"
{{range $dep := .}}include($$PWD/{{$dep.Name}}/{{$dep.Name}}.pri){{"\n"}}{{end}}
`))
)

func (pw PackageWrapper) writePri(filename string, tpl *template.Template, data interface{}) error {

	var file *os.File
	var err error

	// create the vendor directory if needed
	if _, err = os.Stat(core.Vendor); err != nil {
		err = os.Mkdir(core.Vendor, 0755)
	}

	// re-create the .pri file
	file, err = os.Create(filename)

	if err != nil {
		return err
	}
	defer file.Close()

	err = tpl.Execute(file, data)
	if err != nil {
		return err
	}

	return nil
}

func (pw PackageWrapper) UpdatePri(dependencies []*msg.Dependency) error {
	return pw.writePri(core.Vendor+"/"+core.Vendor+".pri", vendorPri, dependencies)
}

// FIXME: attach this to Dependency
func (pw PackageWrapper) GetDependencySignature(d *msg.Dependency) string {
	if d != nil {
		if d.Version != nil {
			return d.Name + "@" + d.Version.Label
		}
		return d.Name
	}
	return ""
}
<commit_msg>Ensure we don't ignore errors in LoadPackage.<commit_after>// Copyright 2015 Cutehacks AS. All rights reserved.
// License can be found in the LICENSE file.

package common

import (
	//	"crypto/x509/pkix"
	//	"debug/elf"
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	msg "qpm.io/common/messages"
	"qpm.io/qpm/core"
	"regexp"
	"strings"
	"text/template"
	"path/filepath"
)

const (
	ERR_REQUIRED_FIELD  = "%s is a required field"
	ERR_FORMATTED_FIELD = "%s requires a specific format"
)

var (
	regexPackageName = regexp.MustCompile("^[a-zA-Z]{2,}\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]?$")
	regexVersion     = regexp.MustCompile("[0-9].[0-9].[0-9]*")
	regexAuthorName  = regexp.MustCompile("^[\\p{L}\\s'.-]+$")
	regexAuthorEmail = regexp.MustCompile(".+@.+\\..+")
	regexGitSha1     = regexp.MustCompile("^[a-fA-F0-9]{8,}$")
	regexRevision    = map[msg.RepoType]*regexp.Regexp{
		msg.RepoType_GITHUB: regexGitSha1,
	}
)

func dotSlash(dots string) string {
	return strings.Replace(dots, ".", "/", -1)
}

func dotUnderscore(dots string) string {
	return strings.Replace(dots, ".", "_", -1)
}

type DependencyList map[string]string

// Creates a new DependencyList (which is really a map) which takes a list of package
// names of the form "package@version" and produces a map of "package => "version".
// Passing in multiple versions of the same package will overwrite with the last one.
func NewDependencyList(packages []string) DependencyList {
	deps := DependencyList{}
	for _, dep := range packages {
		parts := strings.Split(dep, "@")
		pName := strings.ToLower(parts[0])

		if len(parts) > 1 {
			deps[pName] = strings.ToLower(parts[1])
		} else {
			deps[pName] = ""
		}
	}
	return deps
}

func NewPackage() *msg.Package {
	return &msg.Package{
		Name:        "",
		Description: "",
		Version: &msg.Package_Version{
			Label: "0.0.1",
		},
		Author: &msg.Package_Author{
			Name:  "",
			Email: "",
		},
		Dependencies: []string{},
		Repository: &msg.Package_Repository{
			Type: msg.RepoType_GITHUB,
			Url:  "",
		},
		License: msg.LicenseType_MIT,
	}
}

type PackageWrapper struct {
	*msg.Package
	ID int // only used on server
	FilePath string
}

func LoadPackage(path string) (*PackageWrapper, error) {
	var err error
	pw := &PackageWrapper{Package: &msg.Package{}}

	packageFile := filepath.Join(path, core.PackageFile)

	if _, err = os.Stat(packageFile); err == nil {
		var file *os.File

		if file, err = os.Open(packageFile); err != nil {
			return pw, err
		}
		defer file.Close()

		dec := json.NewDecoder(file)

		if err = dec.Decode(pw.Package); err != nil {
			return pw, err
		}

		pw.FilePath, err = filepath.Abs(file.Name())
	}

	return pw, err
}

func (pw PackageWrapper) Save() error {
	var file *os.File
	var err error

	// re-create the core.PackageFile file
	file, err = os.Create(core.PackageFile)

	if err != nil {
		return err
	}
	defer file.Close()

	b, err := json.Marshal(pw.Package)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	json.Indent(&buf, b, "", "  ")
	buf.WriteTo(file)
	return nil
}

func (pw PackageWrapper) ParseDependencies() DependencyList {
	return NewDependencyList(pw.Dependencies)
}

func (pw PackageWrapper) Validate() error {
	if pw.Name == "" {
		return fmt.Errorf(ERR_REQUIRED_FIELD, "name")
	} else {
		// Validate name
		if !regexPackageName.MatchString(pw.Name) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "name")
		}
	}
	if pw.Version == nil {
		return fmt.Errorf(ERR_REQUIRED_FIELD, "version")
	} else {
		// Validate version label
		if !regexVersion.MatchString(pw.Version.Label) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "version label")
		}
		// Validate version revision
		if pw.Version.Revision == "" {
			return fmt.Errorf(ERR_REQUIRED_FIELD, "version revision")
		} else {
			if !regexRevision[pw.Repository.Type].MatchString(pw.Version.Revision) {
				return fmt.Errorf(ERR_FORMATTED_FIELD, "version revision")
			}
		}
	}
	if pw.Author == nil {
		return fmt.Errorf(ERR_REQUIRED_FIELD, "author")
	} else {
		// Validate author name
		if !regexAuthorName.MatchString(pw.Author.Name) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "author name")
		}
		//Validate author email
		if !regexAuthorEmail.MatchString(pw.Author.Email) {
			return fmt.Errorf(ERR_FORMATTED_FIELD, "author email")
		}
	}

	return nil
}

func (pw PackageWrapper) PriFile() string {
	return dotUnderscore(pw.Package.Name) + ".pri"
}

func (pw PackageWrapper) QrcFile() string {
	return dotUnderscore(pw.Package.Name) + ".qrc"
}

func (pw PackageWrapper) QrcPrefix() string {
	return dotSlash(pw.Package.Name)
}

var (
	vendorPri = template.Must(template.New("vendorPri").Parse(`
DEFINES += QPM_INIT\\(E\\)=\"E.addImportPath(QStringLiteral(\\\"qrc:/\\\"));\"
{{range $dep := .}}include($$PWD/{{$dep.Name}}/{{$dep.Name}}.pri){{"\n"}}{{end}}
`))
)

func (pw PackageWrapper) writePri(filename string, tpl *template.Template, data interface{}) error {

	var file *os.File
	var err error

	// create the vendor directory if needed
	if _, err = os.Stat(core.Vendor); err != nil {
		err = os.Mkdir(core.Vendor, 0755)
	}

	// re-create the .pri file
	file, err = os.Create(filename)

	if err != nil {
		return err
	}
	defer file.Close()

	err = tpl.Execute(file, data)
	if err != nil {
		return err
	}

	return nil
}

func (pw PackageWrapper) UpdatePri(dependencies []*msg.Dependency) error {
	return pw.writePri(core.Vendor+"/"+core.Vendor+".pri", vendorPri, dependencies)
}

// FIXME: attach this to Dependency
func (pw PackageWrapper) GetDependencySignature(d *msg.Dependency) string {
	if d != nil {
		if d.Version != nil {
			return d.Name + "@" + d.Version.Label
		}
		return d.Name
	}
	return ""
}
<|endoftext|>


<commit_before>package deemon

import (
	"fmt"
	//d "github.com/sevlyar/go-daemon"
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path"
	"runtime/debug"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const (
	MARK_KEY            = "_DEEMON_PROC_TYPE_"
	MARK_CHILD          = "CHILD"
	MARK_WATCHDOG       = "WATCHDOG"
	MARK_STARTER        = "STARTER"
	DefaultMaxKillRetry = 10
)

type Config struct {
	Pidfile string
	Logfile string
	Workdir string
}

var defaultConfig Config

func init() {
	flag.StringVar(&defaultConfig.Pidfile, "pidfile", "", "Location of the pidfile.")
	flag.StringVar(&defaultConfig.Logfile, "logfile", "", "Location of the logfile.")
	flag.StringVar(&defaultConfig.Workdir, "workdir", "", "Location of the working directory to change to.")
}

// The loop is only restarted if none of the Handler implementations return an error != nil.
type StartFunc func() error
type ReturnHandlerFunc func(error) error
type SignalHandlerFunc func(os.Signal) error
type PanicHandlerFunc func(interface{}) error
type ExitHandlerFunc func(*os.ProcessState) error
type AnyHandlerFunc func() error

type Context struct {
	Config

	UseWatchdog  bool
	MaxKillRetry int

	OnExit   ExitHandlerFunc
	OnPanic  PanicHandlerFunc
	OnSignal SignalHandlerFunc
	OnReturn ReturnHandlerFunc
	OnAny    AnyHandlerFunc

	DefaultOnExit   ExitHandlerFunc
	DefaultOnPanic  PanicHandlerFunc
	DefaultOnSignal SignalHandlerFunc
	DefaultOnReturn ReturnHandlerFunc
	DefaultOnAny    AnyHandlerFunc

	Start StartFunc

	Name     string
	MustRun  time.Duration
	ptype    string
	rchild   *os.Process
	watchdog *os.Process
	lf       *os.File
}

func Launch(s StartFunc, args ...interface{}) (ctx *Context, err error) {
	c := NewContext(s, args...)
	err = c.Launch()
	if err != nil {
		c.Logf("error: %s", err)
	}
	return c, err
}

func NewContext(s StartFunc, args ...interface{}) *Context {

	//determin which kind of proc this is.
	ptype := os.Getenv(MARK_KEY)
	if len(ptype) == 0 {
		ptype = MARK_STARTER
	}

	name := path.Base(os.Args[0])

	var ret *Context
	ret = &Context{
		Config:       defaultConfig,
		rchild:       nil,
		ptype:        ptype,
		Name:         name,
		MaxKillRetry: DefaultMaxKillRetry,
		DefaultOnExit: func(state *os.ProcessState) error {
			ret.Logf("onExit %s", state.String())
			return nil
		},
		DefaultOnPanic: func(i interface{}) error {
			ret.Logf("onPanic %s", i)
			debug.PrintStack()
			return nil
		},
		DefaultOnReturn: func(err error) error {
			ret.Logf("onReturn %s", err)
			return nil
		},
		DefaultOnSignal: func(sig os.Signal) error {
			ret.Logf("onSignal %s", sig)
			return ret.Errorf("exiting due to sig %s", sig)
		},
		DefaultOnAny: func() error {
			return nil
		},
		Start:   s,
		MustRun: time.Second * 1,
	}

	if len(ret.Config.Pidfile) <= 0 {
		ret.Config.Pidfile = name + ".pid"
	}

	if len(ret.Config.Logfile) <= 0 {
		ret.Config.Logfile = name + ".log"
	}

	ret.OnExit = ret.DefaultOnExit
	ret.OnPanic = ret.DefaultOnPanic
	ret.OnReturn = ret.DefaultOnReturn
	ret.OnSignal = ret.DefaultOnSignal
	ret.OnAny = ret.DefaultOnAny

	for _, m := range args {
		if v, ok := m.(ReturnHandlerFunc); ok {
			ret.Logf("registering ReturnHandler")
			ret.OnReturn = v
		}

		if v, ok := m.(PanicHandlerFunc); ok {
			ret.Logf("registering PanicHandler")
			ret.OnPanic = v
		}

		if v, ok := m.(SignalHandlerFunc); ok {
			ret.Logf("registering SignalHandler")
			ret.OnSignal = v
		}

		if v, ok := m.(ExitHandlerFunc); ok {
			ret.Logf("registering ExitHandler")
			ret.OnExit = v
		}

		if v, ok := m.(AnyHandlerFunc); ok {
			ret.Logf("registering AnyHandler")
			ret.OnAny = v
		}
	}

	ret.readPidfile()
	return ret
}

func (c *Context) doChild() error {
	c.Logf("doChild")
	ret := make(chan error, 1)

	run := true
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT)

	for run {
		laststart := time.Now()
		go func() {
			var err error
			defer func() {
				if r := recover(); r != nil {
					err = c.OnPanic(r)
				}
				ret <- err
			}()
			c.Logf("starting Start() routine")
			err = c.Start()
		}()

		var err error
		select {
		case err := <-ret: // START returned an error
			err = c.OnReturn(err)

		case sig := <-sc: // A Termination signal was catched
			err = c.OnSignal(sig)
		}

		if err != nil {
			return c.Errorf("exiting due to error in 'onReturn/onSignal' handler: '%s'", err)
		}

		err = c.OnAny()
		if err != nil {
			return c.Errorf("exiting due to error in 'onAny' handler: '%s'", err)
		}

		delta := time.Now().Sub(laststart)
		if delta < c.MustRun {
			log.Fatalf("returned within %.3f seconds. Assuming startup error. Aborted.", delta.Seconds())
		}

		c.Logf("restarting: %s %s", c.Name, c.ptype)
	}
	return nil
}

func (c *Context) doWatchdog() (err error) {
	proc, err := os.FindProcess(os.Getpid())
	if err != nil {
		return
	}
	c.watchdog = proc

	defer c.close()
	run := true

	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT)
	go func() {
		sig := <-sc
		c.Logf("received signal %d", sig)
		run = false
		if c.rchild != nil {
			c.Logf("pass signal %d to PID=%d", sig, c.rchild.Pid)
			c.rchild.Signal(sig) //  Pass the same signal to the child
		}
	}()

	for run {
		laststart := time.Now()

		err = c.restartAs(MARK_CHILD)
		if err != nil {
			return
		}

		err = ioutil.WriteFile(c.Pidfile, []byte(fmt.Sprintf("%d\n%d", c.rchild.Pid, os.Getpid())), 0644)
		if err != nil {
			return
		}

		c.Logf("observing")
		state, err := c.rchild.Wait()
		if err != nil {
			return err
		}

		delta := time.Now().Sub(laststart)
		if delta < c.MustRun {
			return c.Errorf("child returned within %.3f seconds", delta.Seconds)
		}
		err = c.OnExit(state)
		if err != nil {
			err = c.Errorf("exiting due to error in 'onExit' handler: '%s'", err)
			log.Print(err)
			return err
		}

		if run {
			c.Logf("restarting child")
		}
	}
	return nil
}

func (c *Context) restartAs(mark string) (err error) {
	oe := os.Environ()
	ne := make([]string, 0)

	for _, e := range oe {
		if !strings.HasPrefix(e, MARK_KEY) {
			ne = append(ne, e)
		}
	}
	ne = append(ne, fmt.Sprintf("%s=%s", MARK_KEY, mark))

	attr := &os.ProcAttr{
		Dir: c.Config.Workdir,
		Env: ne,
		Files: []*os.File{
			os.Stdin,
			c.lf,
			c.lf,
		},
		Sys: &syscall.SysProcAttr{
			Setsid: true,
		},
	}

	c.Logf("restarting to %s", mark)
	child, err := os.StartProcess(os.Args[0], os.Args, attr)
	if err != nil {
		return err
	}

	if child == nil {
		return c.Errorf("child should never become a same proc type: %s,%s", c.Name, c.ptype)
	}
	c.rchild = child

	return err
}

func (c *Context) doStarter() (err error) {
	//TODO: check for UseWatchdog
	err = c.restartAs(MARK_WATCHDOG)
	if err != nil {
		return
	}

	c.Logf("Starter done. Please find logs at '%s'", c.Logfile)
	return nil
}

func (c *Context) close() error {
	c.lf.Close()
	return os.Remove(c.Pidfile)
}

func (c *Context) Launch() (err error) {
	c.lf, err = os.OpenFile(c.Config.Logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		return err
	}

	/* route proc type */
	switch c.ptype {
	case MARK_CHILD:
		log.SetOutput(c.lf)
		err = c.doChild()
		c.Logf("Exiting: %s", err)
		os.Exit(0)
	case MARK_WATCHDOG:
		c.Logf("Starting. Writing logs to: '%s'", c.Logfile)
		log.SetOutput(c.lf)
		err = c.doWatchdog()
		c.Logf("Exiting: %s", err)
		os.Exit(0)
	case MARK_STARTER:
		return c.doStarter()
	default:
		return c.Errorf("unknown proc type: %s", c.ptype)
	}
	return nil
}

func (c *Context) Logf(pat string, args ...interface{}) {
	log.Printf("[%s|%s|%d] "+pat, append([]interface{}{c.Name, c.ptype, os.Getpid()}, args...)...)
}

func (c *Context) Errorf(pat string, args ...interface{}) error {
	return fmt.Errorf("[%s|%s|%d] "+pat, append([]interface{}{c.Name, c.ptype, os.Getpid()}, args...)...)
}

func (c *Context) readPidfile() error {
	c.rchild = nil
	c.watchdog = nil

	data, err := ioutil.ReadFile(c.Config.Pidfile)
	if err != nil {
		return nil
	}

	sb := bytes.Split(data, []byte{'\n'})
	if len(sb) >= 1 {
		cpid, err := strconv.Atoi(string(sb[0]))
		if err != nil {
			return err
		}

		proc, _ := os.FindProcess(cpid)
		c.rchild = proc
	}

	if len(sb) >= 2 {
		wdpid, err := strconv.Atoi(string(sb[1]))
		if err != nil {
			return err
		}
		proc, _ := os.FindProcess(wdpid)
		c.watchdog = proc
	}

	return nil
}

func (c *Context) Kill() error {
	return c.kill(syscall.SIGKILL)
}

func (c *Context) Stop() error {
	return c.kill(syscall.SIGTERM)
}

func (c *Context) kill(sig os.Signal) error {
	for i := 0; i < c.MaxKillRetry; i++ {
		c.readPidfile()

		if c.IsDown() {
			return nil
		}

		if c.watchdog != nil {
			c.Logf("Sending %s to watchdog PID=%d", sig, c.watchdog.Pid)
			c.watchdog.Signal(syscall.SIGTERM) // Never send the watchdog a kill
		}

		if c.rchild != nil {
			c.Logf("Sending %s to child PID=%d", sig, c.rchild.Pid)
			c.rchild.Signal(sig)
		}
		time.Sleep(time.Second * 1)
	}

	return c.Errorf("Could not stop process '%s' after %d retries.", c.Name, c.MaxKillRetry)
}

func (c *Context) IsRunning() bool {
	return (c.rchild != nil && c.watchdog != nil)
}

func (c *Context) IsDown() bool {
	return (c.rchild == nil && c.watchdog == nil)
}

func (c *Context) amITheChild() bool {
	if c.rchild != nil {
		return c.rchild.Pid == os.Getpid()
	}
	return false
}

func Command(cmd string, s StartFunc, fs ...interface{}) (ctx *Context, err error) {
	ctx = NewContext(s, fs...)
	if ctx == nil {
		return nil, fmt.Errorf("Could not create context.")
	}
	err = ctx.Command(cmd)
	return ctx, err
}

func PrintUsage() {
	fmt.Fprintf(os.Stderr, `
usage: %s [flags...] <target>

Targets:
--------
	start		Start the service.
	stop		Stop the service.
	kill		Kill the service.
	status		Print if service is running.

Flags:
------
`, os.Args[0])
	flag.PrintDefaults()
}

func (c *Context) Command(cmd string) (err error) {
	c.readPidfile()
	cm := strings.ToLower(cmd)
	running := c.IsRunning()
	switch cm {
	case "stop":
		if !running {
			return
		}
		return c.Stop()
	case "kill":
		if !running {
			return
		}
		return c.Kill()
	case "start":
		if !running || c.amITheChild() {
			return c.Launch()
		} else {
			c.Logf("Already Running")
			return c.Errorf("Service already running.")
		}
	case "status":
		if running {
			c.Logf("Service %s is running at PID=%d,%d\n", c.Name, c.watchdog.Pid, c.rchild.Pid)

			/* TODO: Send SIG INFO/STATUS to child process */
			return nil
		} else {
			return c.Errorf("Service %s is not running", c.Name)
		}
		flag.PrintDefaults()
	default:
		return c.Errorf("unknown command: %s", cmd)
	}
	return
}
<commit_msg>Removed double error handling.<commit_after>package deemon

import (
	"fmt"
	//d "github.com/sevlyar/go-daemon"
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path"
	"runtime/debug"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const (
	MARK_KEY            = "_DEEMON_PROC_TYPE_"
	MARK_CHILD          = "CHILD"
	MARK_WATCHDOG       = "WATCHDOG"
	MARK_STARTER        = "STARTER"
	DefaultMaxKillRetry = 10
)

type Config struct {
	Pidfile string
	Logfile string
	Workdir string
}

var defaultConfig Config

func init() {
	flag.StringVar(&defaultConfig.Pidfile, "pidfile", "", "Location of the pidfile.")
	flag.StringVar(&defaultConfig.Logfile, "logfile", "", "Location of the logfile.")
	flag.StringVar(&defaultConfig.Workdir, "workdir", "", "Location of the working directory to change to.")
}

// The loop is only restarted if none of the Handler implementations return an error != nil.
type StartFunc func() error
type ReturnHandlerFunc func(error) error
type SignalHandlerFunc func(os.Signal) error
type PanicHandlerFunc func(interface{}) error
type ExitHandlerFunc func(*os.ProcessState) error
type AnyHandlerFunc func(error) error

type Context struct {
	Config

	UseWatchdog  bool
	MaxKillRetry int

	OnExit   ExitHandlerFunc
	OnPanic  PanicHandlerFunc
	OnSignal SignalHandlerFunc
	OnReturn ReturnHandlerFunc
	OnAny    AnyHandlerFunc

	DefaultOnExit   ExitHandlerFunc
	DefaultOnPanic  PanicHandlerFunc
	DefaultOnSignal SignalHandlerFunc
	DefaultOnReturn ReturnHandlerFunc
	DefaultOnAny    AnyHandlerFunc

	Start StartFunc

	Name     string
	MustRun  time.Duration
	ptype    string
	rchild   *os.Process
	watchdog *os.Process
	lf       *os.File
}

func Launch(s StartFunc, args ...interface{}) (ctx *Context, err error) {
	c := NewContext(s, args...)
	err = c.Launch()
	if err != nil {
		c.Logf("error: %s", err)
	}
	return c, err
}

func NewContext(s StartFunc, args ...interface{}) *Context {

	//determin which kind of proc this is.
	ptype := os.Getenv(MARK_KEY)
	if len(ptype) == 0 {
		ptype = MARK_STARTER
	}

	name := path.Base(os.Args[0])

	var ret *Context
	ret = &Context{
		Config:       defaultConfig,
		rchild:       nil,
		ptype:        ptype,
		Name:         name,
		MaxKillRetry: DefaultMaxKillRetry,
		DefaultOnExit: func(state *os.ProcessState) error {
			ret.Logf("onExit %s", state.String())
			return nil
		},
		DefaultOnPanic: func(i interface{}) error {
			ret.Logf("onPanic %s", i)
			debug.PrintStack()
			return nil
		},
		DefaultOnReturn: func(err error) error {
			ret.Logf("onReturn %s", err)
			return nil
		},
		DefaultOnSignal: func(sig os.Signal) error {
			ret.Logf("onSignal %s", sig)
			return ret.Errorf("exiting due to sig %s", sig)
		},
		DefaultOnAny: func(err error) error {
			return err
		},
		Start:   s,
		MustRun: time.Second * 1,
	}

	if len(ret.Config.Pidfile) <= 0 {
		ret.Config.Pidfile = name + ".pid"
	}

	if len(ret.Config.Logfile) <= 0 {
		ret.Config.Logfile = name + ".log"
	}

	ret.OnExit = ret.DefaultOnExit
	ret.OnPanic = ret.DefaultOnPanic
	ret.OnReturn = ret.DefaultOnReturn
	ret.OnSignal = ret.DefaultOnSignal
	ret.OnAny = ret.DefaultOnAny

	for _, m := range args {
		if v, ok := m.(ReturnHandlerFunc); ok {
			ret.Logf("registering ReturnHandler")
			ret.OnReturn = v
		}

		if v, ok := m.(PanicHandlerFunc); ok {
			ret.Logf("registering PanicHandler")
			ret.OnPanic = v
		}

		if v, ok := m.(SignalHandlerFunc); ok {
			ret.Logf("registering SignalHandler")
			ret.OnSignal = v
		}

		if v, ok := m.(ExitHandlerFunc); ok {
			ret.Logf("registering ExitHandler")
			ret.OnExit = v
		}

		if v, ok := m.(AnyHandlerFunc); ok {
			ret.Logf("registering AnyHandler")
			ret.OnAny = v
		}
	}

	ret.readPidfile()
	return ret
}

func (c *Context) doChild() error {
	c.Logf("doChild")
	ret := make(chan error, 1)

	run := true
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT)

	for run {
		laststart := time.Now()
		go func() {
			var err error
			defer func() {
				if r := recover(); r != nil {
					err = c.OnPanic(r)
				}
				ret <- err
			}()
			c.Logf("starting Start() routine")
			err = c.Start()
		}()

		var err error
		select {
		case err := <-ret: // START returned an error
			err = c.OnReturn(err)

		case sig := <-sc: // A Termination signal was catched
			err = c.OnSignal(sig)
		}
		err = c.OnAny(err)
		if err != nil {
			return c.Errorf("exiting due to error in handler: '%s'", err)
		}

		delta := time.Now().Sub(laststart)
		if delta < c.MustRun {
			log.Fatalf("returned within %.3f seconds. Assuming startup error. Aborted.", delta.Seconds())
		}

		c.Logf("restarting: %s %s", c.Name, c.ptype)
	}
	return nil
}

func (c *Context) doWatchdog() (err error) {
	proc, err := os.FindProcess(os.Getpid())
	if err != nil {
		return
	}
	c.watchdog = proc

	defer c.close()
	run := true

	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT)
	go func() {
		sig := <-sc
		c.Logf("received signal %d", sig)
		run = false
		if c.rchild != nil {
			c.Logf("pass signal %d to PID=%d", sig, c.rchild.Pid)
			c.rchild.Signal(sig) //  Pass the same signal to the child
		}
	}()

	for run {
		laststart := time.Now()

		err = c.restartAs(MARK_CHILD)
		if err != nil {
			return
		}

		err = ioutil.WriteFile(c.Pidfile, []byte(fmt.Sprintf("%d\n%d", c.rchild.Pid, os.Getpid())), 0644)
		if err != nil {
			return
		}

		c.Logf("observing")
		state, err := c.rchild.Wait()
		if err != nil {
			return err
		}

		delta := time.Now().Sub(laststart)
		if delta < c.MustRun {
			return c.Errorf("child returned within %.3f seconds", delta.Seconds)
		}
		err = c.OnExit(state)
		if err != nil {
			err = c.Errorf("exiting due to error in 'onExit' handler: '%s'", err)
			log.Print(err)
			return err
		}

		if run {
			c.Logf("restarting child")
		}
	}
	return nil
}

func (c *Context) restartAs(mark string) (err error) {
	oe := os.Environ()
	ne := make([]string, 0)

	for _, e := range oe {
		if !strings.HasPrefix(e, MARK_KEY) {
			ne = append(ne, e)
		}
	}
	ne = append(ne, fmt.Sprintf("%s=%s", MARK_KEY, mark))

	attr := &os.ProcAttr{
		Dir: c.Config.Workdir,
		Env: ne,
		Files: []*os.File{
			os.Stdin,
			c.lf,
			c.lf,
		},
		Sys: &syscall.SysProcAttr{
			Setsid: true,
		},
	}

	c.Logf("restarting to %s", mark)
	child, err := os.StartProcess(os.Args[0], os.Args, attr)
	if err != nil {
		return err
	}

	if child == nil {
		return c.Errorf("child should never become a same proc type: %s,%s", c.Name, c.ptype)
	}
	c.rchild = child

	return err
}

func (c *Context) doStarter() (err error) {
	//TODO: check for UseWatchdog
	err = c.restartAs(MARK_WATCHDOG)
	if err != nil {
		return
	}

	c.Logf("Starter done. Please find logs at '%s'", c.Logfile)
	return nil
}

func (c *Context) close() error {
	c.lf.Close()
	return os.Remove(c.Pidfile)
}

func (c *Context) Launch() (err error) {
	c.lf, err = os.OpenFile(c.Config.Logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		return err
	}

	/* route proc type */
	switch c.ptype {
	case MARK_CHILD:
		log.SetOutput(c.lf)
		err = c.doChild()
		c.Logf("Exiting: %s", err)
		os.Exit(0)
	case MARK_WATCHDOG:
		c.Logf("Starting. Writing logs to: '%s'", c.Logfile)
		log.SetOutput(c.lf)
		err = c.doWatchdog()
		c.Logf("Exiting: %s", err)
		os.Exit(0)
	case MARK_STARTER:
		return c.doStarter()
	default:
		return c.Errorf("unknown proc type: %s", c.ptype)
	}
	return nil
}

func (c *Context) Logf(pat string, args ...interface{}) {
	log.Printf("[%s|%s|%d] "+pat, append([]interface{}{c.Name, c.ptype, os.Getpid()}, args...)...)
}

func (c *Context) Errorf(pat string, args ...interface{}) error {
	return fmt.Errorf("[%s|%s|%d] "+pat, append([]interface{}{c.Name, c.ptype, os.Getpid()}, args...)...)
}

func (c *Context) readPidfile() error {
	c.rchild = nil
	c.watchdog = nil

	data, err := ioutil.ReadFile(c.Config.Pidfile)
	if err != nil {
		return nil
	}

	sb := bytes.Split(data, []byte{'\n'})
	if len(sb) >= 1 {
		cpid, err := strconv.Atoi(string(sb[0]))
		if err != nil {
			return err
		}

		proc, _ := os.FindProcess(cpid)
		c.rchild = proc
	}

	if len(sb) >= 2 {
		wdpid, err := strconv.Atoi(string(sb[1]))
		if err != nil {
			return err
		}
		proc, _ := os.FindProcess(wdpid)
		c.watchdog = proc
	}

	return nil
}

func (c *Context) Kill() error {
	return c.kill(syscall.SIGKILL)
}

func (c *Context) Stop() error {
	return c.kill(syscall.SIGTERM)
}

func (c *Context) kill(sig os.Signal) error {
	for i := 0; i < c.MaxKillRetry; i++ {
		c.readPidfile()

		if c.IsDown() {
			return nil
		}

		if c.watchdog != nil {
			c.Logf("Sending %s to watchdog PID=%d", sig, c.watchdog.Pid)
			c.watchdog.Signal(syscall.SIGTERM) // Never send the watchdog a kill
		}

		if c.rchild != nil {
			c.Logf("Sending %s to child PID=%d", sig, c.rchild.Pid)
			c.rchild.Signal(sig)
		}
		time.Sleep(time.Second * 1)
	}

	return c.Errorf("Could not stop process '%s' after %d retries.", c.Name, c.MaxKillRetry)
}

func (c *Context) IsRunning() bool {
	return (c.rchild != nil && c.watchdog != nil)
}

func (c *Context) IsDown() bool {
	return (c.rchild == nil && c.watchdog == nil)
}

func (c *Context) amITheChild() bool {
	if c.rchild != nil {
		return c.rchild.Pid == os.Getpid()
	}
	return false
}

func Command(cmd string, s StartFunc, fs ...interface{}) (ctx *Context, err error) {
	ctx = NewContext(s, fs...)
	if ctx == nil {
		return nil, fmt.Errorf("Could not create context.")
	}
	err = ctx.Command(cmd)
	return ctx, err
}

func PrintUsage() {
	fmt.Fprintf(os.Stderr, `
usage: %s [flags...] <target>

Targets:
--------
	start		Start the service.
	stop		Stop the service.
	kill		Kill the service.
	status		Print if service is running.

Flags:
------
`, os.Args[0])
	flag.PrintDefaults()
}

func (c *Context) Command(cmd string) (err error) {
	c.readPidfile()
	cm := strings.ToLower(cmd)
	running := c.IsRunning()
	switch cm {
	case "stop":
		if !running {
			return
		}
		return c.Stop()
	case "kill":
		if !running {
			return
		}
		return c.Kill()
	case "start":
		if !running || c.amITheChild() {
			return c.Launch()
		} else {
			c.Logf("Already Running")
			return c.Errorf("Service already running.")
		}
	case "status":
		if running {
			c.Logf("Service %s is running at PID=%d,%d\n", c.Name, c.watchdog.Pid, c.rchild.Pid)

			/* TODO: Send SIG INFO/STATUS to child process */
			return nil
		} else {
			return c.Errorf("Service %s is not running", c.Name)
		}
		flag.PrintDefaults()
	default:
		return c.Errorf("unknown command: %s", cmd)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"database/sql"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	"github.com/gorilla/securecookie"

	_ "github.com/go-sql-driver/mysql"

	"github.com/AscendTech4H/turtleauth"
)

// Credentials which stores google ids.
type Credentials struct {
	Cid     string `json:"client_id"`
	Csecret string `json:"client_secret"`
}

// User is a retrieved and authentiacted google user.
type User struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified bool `json:"email_verified"`
	Gender        string `json:"gender"`
}

var cred Credentials
var conf *oauth2.Config
var state string

func init() {

}

func main() {
	log.Println("Starting auth server. . . ")
	var dbs string
	var addr string
	var k string
	var credfile string
	flag.StringVar(&dbs, "db", "/", "Database source string")
	flag.StringVar(&addr, "addr", ":9001", "Server port for authentication")
	flag.StringVar(&k, "key", ".authkey", "Key file")
	flag.StringVar(&credfile, "cred", ".cred", "Google oauth2 credentials file")
	flag.Parse()
	log.Println("Loading keys. . . ")
	d, err := ioutil.ReadFile(k)
	if err != nil {
		panic(err)
	}
	var keys struct {
		HashKey  []byte
		BlockKey []byte
	}
	err = json.Unmarshal(d, &keys)
	if err != nil {
		panic(err)
	}
	sc := securecookie.New(keys.HashKey, keys.BlockKey)
	sc.SetSerializer(securecookie.GobEncoder{})
	log.Println("Loading oauth credentials. . . ")
	file, err := ioutil.ReadFile(credfile)
	if err != nil {
		log.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	json.Unmarshal(file, &cred)
	fmt.Println(cred)

	conf = &oauth2.Config{
		ClientID:     cred.Cid,
		ClientSecret: cred.Csecret,
		RedirectURL:  "https://auth.ascendtech4h.org/auth",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email", // You have to select your own scope from here -> https://developers.google.com/identity/protocols/googlescopes#google_sign-in
		},
		Endpoint: google.Endpoint,
	}
	log.Println("Connecting to DB. . . ")
	db, err := sql.Open("mysql", "/")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	log.Println("Connected")
	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("pong"))
	})
	http.HandleFunc("/perm", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
		}
		var ac turtleauth.AuthCheck
		err := gob.NewDecoder(r.Body).Decode(&ac)
		if err != nil {
			http.Error(w, "Decode error", http.StatusInternalServerError)
			return
		}
		if !ac.P.Check() {
			http.Error(w, "Bad PermissionClass", http.StatusBadRequest)
			return
		}
		var uid int
		err = sc.Decode("turtleauth", ac.C.Value, &uid)
		if err != nil {
			http.Error(w, "Cookie error", http.StatusUnauthorized)
			return
		}
		res, err := db.Query(fmt.Sprintf("SELECT * FROM PERM_%s WHERE id=%d", ac.P.Name, uid))
		if err != nil {
			http.Error(w, "SQL Error", http.StatusInternalServerError)
			return
		}
		defer res.Close()
		if res.Next() {
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("Auth successful"))
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Auth rejected"))
		}
	})
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Form parse error", http.StatusBadRequest)
			return
		}
		dest := r.FormValue("dest")
		if dest == "" {
			http.Error(w, "Missing destination", http.StatusBadRequest)
			return
		}
		expire := time.Now().Add(5 * time.Minute) //Delete cookie in 5 min
		http.SetCookie(w, &http.Cookie{
			Name:    "authdest",
			Value:   dest,
			Expires: expire,
		})
		key := make([]byte, 32)
		_, err = rand.Read(key)
		if err != nil {
			http.Error(w, "Random number generator failure", http.StatusInternalServerError)
			return
		}
		kstr := base64.StdEncoding.EncodeToString(key)
		cook := &http.Cookie{
			Name:    "state",
			Value:   kstr,
			Expires: expire,
		}
		http.SetCookie(w, cook)
		authaddr := conf.AuthCodeURL(kstr) + "&access_type=offline"
		http.Redirect(w, r, authaddr, http.StatusMovedPermanently)
	})
	http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		cook, err := r.Cookie("state")
		if err != nil {
			http.Error(w, "Error getting state cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "state",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		dest, err := r.Cookie("authdest")
		if err != nil {
			http.Error(w, "Error retrieving authdest cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "authdest",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		if r.URL.Query().Get("state") != cook.Value {
			http.Error(w, "Unmatched state", http.StatusUnauthorized)
			log.Println(r.URL.Query().Get("state"), cook.Value)
			return
		}
		tok, err := conf.Exchange(oauth2.NoContext, r.URL.Query().Get("code"))
		if err != nil {
			http.Error(w, "Exchange error", http.StatusUnauthorized)
			log.Printf("Exchange error: %s\n", err.Error())
		}
		client := conf.Client(oauth2.NoContext, tok)
		lookup, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
		if err != nil {
			http.Error(w, "User info request error", http.StatusUnauthorized)
			log.Println(err)
			return
		}
		defer lookup.Body.Close()
		var uinfo User
		err = json.NewDecoder(lookup.Body).Decode(&uinfo)
		if err != nil {
			http.Error(w, "User info request decode error", http.StatusInternalServerError)
			log.Println(err)
			return
		}
		res, err := db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		var id int
		if !res.Next() {
			_, err = db.Exec("INSERT INTO users (email) VALUES (?)", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
			res, err = db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
		}
		err = res.Scan(&id)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		cstr, err := sc.Encode("turtleauth", &id)
		if err != nil {
			http.Error(w, "Cookie encryption error", http.StatusInternalServerError)
			log.Printf("Cookie encryption error: %s\n", err.Error())
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:  "turtleauth",
			Value: cstr,
		})
		//All good - redirect
		http.Redirect(w, r, dest.Value, http.StatusMovedPermanently)
	})
	log.Println("Starting http server")
	panic(http.ListenAndServe(addr, nil))
}
<commit_msg>Actually use -db value<commit_after>package main

import (
	"crypto/rand"
	"database/sql"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	"github.com/gorilla/securecookie"

	_ "github.com/go-sql-driver/mysql"

	"github.com/AscendTech4H/turtleauth"
)

// Credentials which stores google ids.
type Credentials struct {
	Cid     string `json:"client_id"`
	Csecret string `json:"client_secret"`
}

// User is a retrieved and authentiacted google user.
type User struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified bool `json:"email_verified"`
	Gender        string `json:"gender"`
}

var cred Credentials
var conf *oauth2.Config
var state string

func init() {

}

func main() {
	log.Println("Starting auth server. . . ")
	var dbs string
	var addr string
	var k string
	var credfile string
	flag.StringVar(&dbs, "db", "/", "Database source string")
	flag.StringVar(&addr, "addr", ":9001", "Server port for authentication")
	flag.StringVar(&k, "key", ".authkey", "Key file")
	flag.StringVar(&credfile, "cred", ".cred", "Google oauth2 credentials file")
	flag.Parse()
	log.Println("Loading keys. . . ")
	d, err := ioutil.ReadFile(k)
	if err != nil {
		panic(err)
	}
	var keys struct {
		HashKey  []byte
		BlockKey []byte
	}
	err = json.Unmarshal(d, &keys)
	if err != nil {
		panic(err)
	}
	sc := securecookie.New(keys.HashKey, keys.BlockKey)
	sc.SetSerializer(securecookie.GobEncoder{})
	log.Println("Loading oauth credentials. . . ")
	file, err := ioutil.ReadFile(credfile)
	if err != nil {
		log.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	json.Unmarshal(file, &cred)
	fmt.Println(cred)

	conf = &oauth2.Config{
		ClientID:     cred.Cid,
		ClientSecret: cred.Csecret,
		RedirectURL:  "https://auth.ascendtech4h.org/auth",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email", // You have to select your own scope from here -> https://developers.google.com/identity/protocols/googlescopes#google_sign-in
		},
		Endpoint: google.Endpoint,
	}
	log.Println("Connecting to DB. . . ")
	db, err := sql.Open("mysql", dbs)
	if err != nil {
		panic(err)
	}
	defer db.Close()
	log.Println("Connected")
	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("pong"))
	})
	http.HandleFunc("/perm", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
		}
		var ac turtleauth.AuthCheck
		err := gob.NewDecoder(r.Body).Decode(&ac)
		if err != nil {
			http.Error(w, "Decode error", http.StatusInternalServerError)
			return
		}
		if !ac.P.Check() {
			http.Error(w, "Bad PermissionClass", http.StatusBadRequest)
			return
		}
		var uid int
		err = sc.Decode("turtleauth", ac.C.Value, &uid)
		if err != nil {
			http.Error(w, "Cookie error", http.StatusUnauthorized)
			return
		}
		res, err := db.Query(fmt.Sprintf("SELECT * FROM PERM_%s WHERE id=%d", ac.P.Name, uid))
		if err != nil {
			http.Error(w, "SQL Error", http.StatusInternalServerError)
			return
		}
		defer res.Close()
		if res.Next() {
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("Auth successful"))
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Auth rejected"))
		}
	})
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Form parse error", http.StatusBadRequest)
			return
		}
		dest := r.FormValue("dest")
		if dest == "" {
			http.Error(w, "Missing destination", http.StatusBadRequest)
			return
		}
		expire := time.Now().Add(5 * time.Minute) //Delete cookie in 5 min
		http.SetCookie(w, &http.Cookie{
			Name:    "authdest",
			Value:   dest,
			Expires: expire,
		})
		key := make([]byte, 32)
		_, err = rand.Read(key)
		if err != nil {
			http.Error(w, "Random number generator failure", http.StatusInternalServerError)
			return
		}
		kstr := base64.StdEncoding.EncodeToString(key)
		cook := &http.Cookie{
			Name:    "state",
			Value:   kstr,
			Expires: expire,
		}
		http.SetCookie(w, cook)
		authaddr := conf.AuthCodeURL(kstr) + "&access_type=offline"
		http.Redirect(w, r, authaddr, http.StatusMovedPermanently)
	})
	http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		cook, err := r.Cookie("state")
		if err != nil {
			http.Error(w, "Error getting state cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "state",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		dest, err := r.Cookie("authdest")
		if err != nil {
			http.Error(w, "Error retrieving authdest cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "authdest",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		if r.URL.Query().Get("state") != cook.Value {
			http.Error(w, "Unmatched state", http.StatusUnauthorized)
			log.Println(r.URL.Query().Get("state"), cook.Value)
			return
		}
		tok, err := conf.Exchange(oauth2.NoContext, r.URL.Query().Get("code"))
		if err != nil {
			http.Error(w, "Exchange error", http.StatusUnauthorized)
			log.Printf("Exchange error: %s\n", err.Error())
		}
		client := conf.Client(oauth2.NoContext, tok)
		lookup, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
		if err != nil {
			http.Error(w, "User info request error", http.StatusUnauthorized)
			log.Println(err)
			return
		}
		defer lookup.Body.Close()
		var uinfo User
		err = json.NewDecoder(lookup.Body).Decode(&uinfo)
		if err != nil {
			http.Error(w, "User info request decode error", http.StatusInternalServerError)
			log.Println(err)
			return
		}
		res, err := db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		var id int
		if !res.Next() {
			_, err = db.Exec("INSERT INTO users (email) VALUES (?)", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
			res, err = db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
		}
		err = res.Scan(&id)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		cstr, err := sc.Encode("turtleauth", &id)
		if err != nil {
			http.Error(w, "Cookie encryption error", http.StatusInternalServerError)
			log.Printf("Cookie encryption error: %s\n", err.Error())
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:  "turtleauth",
			Value: cstr,
		})
		//All good - redirect
		http.Redirect(w, r, dest.Value, http.StatusMovedPermanently)
	})
	log.Println("Starting http server")
	panic(http.ListenAndServe(addr, nil))
}
<|endoftext|>