<commit_before>package tiff

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math/big"
	"strings"
	"unicode"
	"unicode/utf8"
)

type FormatType int

const (
	IntVal FormatType = iota
	FloatVal
	RatVal
	StringVal
	UndefVal
	OtherVal
)

var fmtSize = map[uint16]uint32{
	1:  1,
	2:  1,
	3:  2,
	4:  4,
	5:  8,
	6:  1,
	7:  1,
	8:  2,
	9:  4,
	10: 8,
	11: 4,
	12: 8,
}

// Tag reflects the parsed content of a tiff IFD tag.
type Tag struct {
	// Id is the 2-byte tiff tag identifier
	Id uint16
	// Fmt is an integer (1 through 12) indicating the tag value's format.
	Fmt uint16
	// Ncomp is the number of type Fmt stored in the tag's value (i.e. the tag's
	// value is an array of type Fmt and length Ncomp).
	Ncomp uint32
	// Val holds the bytes that represent the tag's value.
	Val []byte

	order binary.ByteOrder

	intVals   []int64
	floatVals []float64
	ratVals   [][]int64
	strVal    string
}

// DecodeTag parses a tiff-encoded IFD tag from r and returns Tag object. The
// first read from r should be the first byte of the tag. ReadAt offsets should
// be relative to the beginning of the tiff structure (not relative to the
// beginning of the tag).
func DecodeTag(r ReadAtReader, order binary.ByteOrder) (*Tag, error) {
	t := new(Tag)
	t.order = order

	err := binary.Read(r, order, &t.Id)
	if err != nil {
		return nil, errors.New("tiff: tag id read failed: " + err.Error())
	}

	err = binary.Read(r, order, &t.Fmt)
	if err != nil {
		return nil, errors.New("tiff: tag format read failed: " + err.Error())
	}

	err = binary.Read(r, order, &t.Ncomp)
	if err != nil {
		return nil, errors.New("tiff: tag component count read failed: " + err.Error())
	}

	valLen := fmtSize[t.Fmt] * t.Ncomp
	var offset uint32
	if valLen > 4 {
		binary.Read(r, order, &offset)
		t.Val = make([]byte, valLen)
		n, err := r.ReadAt(t.Val, int64(offset))
		if n != int(valLen) || err != nil {
			return nil, errors.New("tiff: tag value read failed: " + err.Error())
		}
	} else {
		val := make([]byte, valLen)
		n, err := r.Read(val)
		if err != nil || n != int(valLen) {
			return nil, errors.New("tiff: tag offset read failed: " + err.Error())
		}

		n, err = r.Read(make([]byte, 4-valLen))
		if err != nil || n != 4-int(valLen) {
			return nil, errors.New("tiff: tag offset read failed: " + err.Error())
		}

		t.Val = val
	}

	t.convertVals()

	return t, nil
}

func (t *Tag) convertVals() {
	r := bytes.NewReader(t.Val)

	switch t.Fmt {
	case 2: // ascii string
		t.strVal = string(t.Val[:len(t.Val)-1])
	case 1:
		var v uint8
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 3:
		var v uint16
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 4:
		var v uint32
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 6:
		var v int8
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 8:
		var v int16
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 9:
		var v int32
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 5: // unsigned rational
		t.ratVals = make([][]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			var n, d uint32
			err := binary.Read(r, t.order, &n)
			panicOn(err)
			err = binary.Read(r, t.order, &d)
			panicOn(err)
			t.ratVals[i] = []int64{int64(n), int64(d)}
		}
	case 10: // signed rational
		t.ratVals = make([][]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			var n, d int32
			err := binary.Read(r, t.order, &n)
			panicOn(err)
			err = binary.Read(r, t.order, &d)
			panicOn(err)
			t.ratVals[i] = []int64{int64(n), int64(d)}
		}
	case 11: // float32
		for i := 0; i < int(t.Ncomp); i++ {
			t.floatVals = make([]float64, int(t.Ncomp))
			var v float32
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.floatVals[i] = float64(v)
		}
	case 12: // float64 (double)
		for i := 0; i < int(t.Ncomp); i++ {
			t.floatVals = make([]float64, int(t.Ncomp))
			var u float64
			err := binary.Read(r, t.order, &u)
			panicOn(err)
			t.floatVals[i] = u
		}
	}
}

// Format returns a value indicating which method can be called to retrieve the
// tag's value properly typed (e.g. integer, rational, etc.).
func (t *Tag) Format() FormatType {
	switch t.Fmt {
	case 1, 3, 4, 6, 8, 9:
		return IntVal
	case 5, 10:
		return RatVal
	case 11, 12:
		return FloatVal
	case 2:
		return StringVal
	case 7:
		return UndefVal
	}
	return OtherVal
}

// Rat returns the tag's i'th value as a rational number. It panics if the tag format
// is not RatVal, if the denominator is zero, or if the tag has no i'th
// component. If a denominator could be zero, use Rat2.
func (t *Tag) Rat(i int) *big.Rat {
	n, d := t.Rat2(i)
	return big.NewRat(n, d)
}

// Rat2 returns the tag's i'th value as a rational number represented by a
// numerator-denominator pair. It panics if the tag format is not RatVal
// or if the tag value has no i'th component.
func (t *Tag) Rat2(i int) (num, den int64) {
	if t.Format() != RatVal {
		panic("Tag format is not 'rational'")
	}
	return t.ratVals[i][0], t.ratVals[i][1]
}

// Int returns the tag's i'th value as an integer. It panics if the tag format is not
// IntVal or if the tag value has no i'th component.
func (t *Tag) Int(i int) int64 {
	if t.Format() != IntVal {
		panic("Tag format is not 'int'")
	}
	return t.intVals[i]
}

// Float returns the tag's i'th value as a float. It panics if the tag format is not
// FloatVal or if the tag value has no i'th component.
func (t *Tag) Float(i int) float64 {
	if t.Format() != FloatVal {
		panic("Tag format is not 'float'")
	}
	return t.floatVals[i]
}

// StringVal returns the tag's value as a string. It panics if the tag
// format is not StringVal
func (t *Tag) StringVal() string {
	if t.Format() != StringVal {
		panic("Tag format is not 'ascii string'")
	}
	return t.strVal
}

// String returns a nicely formatted version of the tag.
func (t *Tag) String() string {
	data, err := t.MarshalJSON()
	panicOn(err)
	val := string(data)
	return fmt.Sprintf("{Id: %X, Val: %v}", t.Id, val)
}

func (t *Tag) MarshalJSON() ([]byte, error) {
	f := t.Format()

	switch f {
	case StringVal, UndefVal:
		return nullString(t.Val), nil
	case OtherVal:
		panic(fmt.Sprintf("Unhandled type Fmt=%v", t.Fmt))
	}

	rv := []string{}
	for i := 0; i < int(t.Ncomp); i++ {
		switch f {
		case RatVal:
			n, d := t.Rat2(i)
			rv = append(rv, fmt.Sprintf(`"%v/%v"`, n, d))
		case FloatVal:
			rv = append(rv, fmt.Sprintf("%v", t.Float(i)))
		case IntVal:
			rv = append(rv, fmt.Sprintf("%v", t.Int(i)))
		}
	}
	return []byte(fmt.Sprintf(`[%s]`, strings.Join(rv, ","))), nil
}

func nullString(in []byte) []byte {
	rv := bytes.Buffer{}
	rv.WriteByte('"')
	for _, b := range in {
		if unicode.IsPrint(rune(b)) {
			rv.WriteByte(b)
		}
	}
	rv.WriteByte('"')
	rvb := rv.Bytes()
	if utf8.Valid(rvb) {
		return rvb
	}
	return []byte(`""`)
}

func panicOn(err error) {
	if err != nil {
		panic("unexpected error: " + err.Error())
	}
}
<commit_msg>tiff: Fix bug in convertVals, where floatVals was allocated more than once.<commit_after>package tiff

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math/big"
	"strings"
	"unicode"
	"unicode/utf8"
)

type FormatType int

const (
	IntVal FormatType = iota
	FloatVal
	RatVal
	StringVal
	UndefVal
	OtherVal
)

var fmtSize = map[uint16]uint32{
	1:  1,
	2:  1,
	3:  2,
	4:  4,
	5:  8,
	6:  1,
	7:  1,
	8:  2,
	9:  4,
	10: 8,
	11: 4,
	12: 8,
}

// Tag reflects the parsed content of a tiff IFD tag.
type Tag struct {
	// Id is the 2-byte tiff tag identifier
	Id uint16
	// Fmt is an integer (1 through 12) indicating the tag value's format.
	Fmt uint16
	// Ncomp is the number of type Fmt stored in the tag's value (i.e. the tag's
	// value is an array of type Fmt and length Ncomp).
	Ncomp uint32
	// Val holds the bytes that represent the tag's value.
	Val []byte

	order binary.ByteOrder

	intVals   []int64
	floatVals []float64
	ratVals   [][]int64
	strVal    string
}

// DecodeTag parses a tiff-encoded IFD tag from r and returns Tag object. The
// first read from r should be the first byte of the tag. ReadAt offsets should
// be relative to the beginning of the tiff structure (not relative to the
// beginning of the tag).
func DecodeTag(r ReadAtReader, order binary.ByteOrder) (*Tag, error) {
	t := new(Tag)
	t.order = order

	err := binary.Read(r, order, &t.Id)
	if err != nil {
		return nil, errors.New("tiff: tag id read failed: " + err.Error())
	}

	err = binary.Read(r, order, &t.Fmt)
	if err != nil {
		return nil, errors.New("tiff: tag format read failed: " + err.Error())
	}

	err = binary.Read(r, order, &t.Ncomp)
	if err != nil {
		return nil, errors.New("tiff: tag component count read failed: " + err.Error())
	}

	valLen := fmtSize[t.Fmt] * t.Ncomp
	var offset uint32
	if valLen > 4 {
		binary.Read(r, order, &offset)
		t.Val = make([]byte, valLen)
		n, err := r.ReadAt(t.Val, int64(offset))
		if n != int(valLen) || err != nil {
			return nil, errors.New("tiff: tag value read failed: " + err.Error())
		}
	} else {
		val := make([]byte, valLen)
		n, err := r.Read(val)
		if err != nil || n != int(valLen) {
			return nil, errors.New("tiff: tag offset read failed: " + err.Error())
		}

		n, err = r.Read(make([]byte, 4-valLen))
		if err != nil || n != 4-int(valLen) {
			return nil, errors.New("tiff: tag offset read failed: " + err.Error())
		}

		t.Val = val
	}

	t.convertVals()

	return t, nil
}

func (t *Tag) convertVals() {
	r := bytes.NewReader(t.Val)

	switch t.Fmt {
	case 2: // ascii string
		t.strVal = string(t.Val[:len(t.Val)-1])
	case 1:
		var v uint8
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 3:
		var v uint16
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 4:
		var v uint32
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 6:
		var v int8
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 8:
		var v int16
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 9:
		var v int32
		t.intVals = make([]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.intVals[i] = int64(v)
		}
	case 5: // unsigned rational
		t.ratVals = make([][]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			var n, d uint32
			err := binary.Read(r, t.order, &n)
			panicOn(err)
			err = binary.Read(r, t.order, &d)
			panicOn(err)
			t.ratVals[i] = []int64{int64(n), int64(d)}
		}
	case 10: // signed rational
		t.ratVals = make([][]int64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			var n, d int32
			err := binary.Read(r, t.order, &n)
			panicOn(err)
			err = binary.Read(r, t.order, &d)
			panicOn(err)
			t.ratVals[i] = []int64{int64(n), int64(d)}
		}
	case 11: // float32
		t.floatVals = make([]float64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			var v float32
			err := binary.Read(r, t.order, &v)
			panicOn(err)
			t.floatVals[i] = float64(v)
		}
	case 12: // float64 (double)
		t.floatVals = make([]float64, int(t.Ncomp))
		for i := 0; i < int(t.Ncomp); i++ {
			var u float64
			err := binary.Read(r, t.order, &u)
			panicOn(err)
			t.floatVals[i] = u
		}
	}
}

// Format returns a value indicating which method can be called to retrieve the
// tag's value properly typed (e.g. integer, rational, etc.).
func (t *Tag) Format() FormatType {
	switch t.Fmt {
	case 1, 3, 4, 6, 8, 9:
		return IntVal
	case 5, 10:
		return RatVal
	case 11, 12:
		return FloatVal
	case 2:
		return StringVal
	case 7:
		return UndefVal
	}
	return OtherVal
}

// Rat returns the tag's i'th value as a rational number. It panics if the tag format
// is not RatVal, if the denominator is zero, or if the tag has no i'th
// component. If a denominator could be zero, use Rat2.
func (t *Tag) Rat(i int) *big.Rat {
	n, d := t.Rat2(i)
	return big.NewRat(n, d)
}

// Rat2 returns the tag's i'th value as a rational number represented by a
// numerator-denominator pair. It panics if the tag format is not RatVal
// or if the tag value has no i'th component.
func (t *Tag) Rat2(i int) (num, den int64) {
	if t.Format() != RatVal {
		panic("Tag format is not 'rational'")
	}
	return t.ratVals[i][0], t.ratVals[i][1]
}

// Int returns the tag's i'th value as an integer. It panics if the tag format is not
// IntVal or if the tag value has no i'th component.
func (t *Tag) Int(i int) int64 {
	if t.Format() != IntVal {
		panic("Tag format is not 'int'")
	}
	return t.intVals[i]
}

// Float returns the tag's i'th value as a float. It panics if the tag format is not
// FloatVal or if the tag value has no i'th component.
func (t *Tag) Float(i int) float64 {
	if t.Format() != FloatVal {
		panic("Tag format is not 'float'")
	}
	return t.floatVals[i]
}

// StringVal returns the tag's value as a string. It panics if the tag
// format is not StringVal
func (t *Tag) StringVal() string {
	if t.Format() != StringVal {
		panic("Tag format is not 'ascii string'")
	}
	return t.strVal
}

// String returns a nicely formatted version of the tag.
func (t *Tag) String() string {
	data, err := t.MarshalJSON()
	panicOn(err)
	val := string(data)
	return fmt.Sprintf("{Id: %X, Val: %v}", t.Id, val)
}

func (t *Tag) MarshalJSON() ([]byte, error) {
	f := t.Format()

	switch f {
	case StringVal, UndefVal:
		return nullString(t.Val), nil
	case OtherVal:
		panic(fmt.Sprintf("Unhandled type Fmt=%v", t.Fmt))
	}

	rv := []string{}
	for i := 0; i < int(t.Ncomp); i++ {
		switch f {
		case RatVal:
			n, d := t.Rat2(i)
			rv = append(rv, fmt.Sprintf(`"%v/%v"`, n, d))
		case FloatVal:
			rv = append(rv, fmt.Sprintf("%v", t.Float(i)))
		case IntVal:
			rv = append(rv, fmt.Sprintf("%v", t.Int(i)))
		}
	}
	return []byte(fmt.Sprintf(`[%s]`, strings.Join(rv, ","))), nil
}

func nullString(in []byte) []byte {
	rv := bytes.Buffer{}
	rv.WriteByte('"')
	for _, b := range in {
		if unicode.IsPrint(rune(b)) {
			rv.WriteByte(b)
		}
	}
	rv.WriteByte('"')
	rvb := rv.Bytes()
	if utf8.Valid(rvb) {
		return rvb
	}
	return []byte(`""`)
}

func panicOn(err error) {
	if err != nil {
		panic("unexpected error: " + err.Error())
	}
}
<|endoftext|>


<commit_before>package crawler

import (
	"bytes"
	"net/url"

	"github.com/ReanGD/go-web-search/content"
	"github.com/ReanGD/go-web-search/werrors"
	"github.com/uber-go/zap"

	"golang.org/x/net/html"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"
)

func isHTML(content []byte) bool {
	isHTML := false
	if len(content) == 0 {
		return isHTML
	}
	if len(content) > 1024 {
		content = content[:1024]
	}

	z := html.NewTokenizer(bytes.NewReader(content))
	isFinish := false
	for !isFinish {
		switch z.Next() {
		case html.ErrorToken:
			isFinish = true
		case html.StartTagToken:
			tagName, _ := z.TagName()
			if bytes.Equal(tagName, []byte("html")) {
				isHTML = true
				isFinish = true
			}
		}
	}

	return isHTML
}

func bodyToUTF8(body []byte, contentType []string) (*transform.Reader, error) {
	contentTypeStr := ""
	if len(contentType) != 0 {
		contentTypeStr = contentType[0]
	}
	enc, _, _ := charset.DetermineEncoding(body, contentTypeStr)
	if enc == encoding.Nop {
		return nil, werrors.New(ErrEncodingNotFound)
	}

	return transform.NewReader(bytes.NewReader(body), enc.NewDecoder()), nil
}

// ProcessBody - check and minimize request body
func ProcessBody(logger zap.Logger, body []byte, contentType []string, u *url.URL) (*HTMLMetadata, content.State, error) {
	state := content.StateSuccess

	if !isHTML(body) {
		return nil, content.StateParseError, werrors.New(ErrBodyNotHTML)
	}

	bodyReader, err := bodyToUTF8(body, contentType)
	if err != nil {
		return nil, content.StateEncodingError, err
	}

	node, err := html.Parse(bodyReader)
	if err != nil {
		return nil, content.StateParseError, werrors.NewDetails(ErrHTMLParse, err)
	}

	parser, err := RunDataExtrator(node, u)
	if err != nil {
		return parser, content.StateParseError, err
	}
	if !parser.MetaTagIndex {
		return parser, content.StateNoFollow, werrors.NewLevel(WarnPageNotIndexed, werrors.WarningLevel)
	}

	for url, error := range parser.WrongURLs {
		logger.Warn("Error parse URL",
			zap.String("err_url", url),
			zap.String("error", error),
		)
	}

	return parser, state, nil
}
<commit_msg>добавил минификацию<commit_after>package crawler

import (
	"bytes"
	"net/url"

	"github.com/ReanGD/go-web-search/content"
	"github.com/ReanGD/go-web-search/werrors"
	"github.com/uber-go/zap"

	"golang.org/x/net/html"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"
)

func isHTML(content []byte) bool {
	isHTML := false
	if len(content) == 0 {
		return isHTML
	}
	if len(content) > 1024 {
		content = content[:1024]
	}

	z := html.NewTokenizer(bytes.NewReader(content))
	isFinish := false
	for !isFinish {
		switch z.Next() {
		case html.ErrorToken:
			isFinish = true
		case html.StartTagToken:
			tagName, _ := z.TagName()
			if bytes.Equal(tagName, []byte("html")) {
				isHTML = true
				isFinish = true
			}
		}
	}

	return isHTML
}

func bodyToUTF8(body []byte, contentType []string) (*transform.Reader, error) {
	contentTypeStr := ""
	if len(contentType) != 0 {
		contentTypeStr = contentType[0]
	}
	enc, _, _ := charset.DetermineEncoding(body, contentTypeStr)
	if enc == encoding.Nop {
		return nil, werrors.New(ErrEncodingNotFound)
	}

	return transform.NewReader(bytes.NewReader(body), enc.NewDecoder()), nil
}

// ProcessBody - check and minimize request body
func ProcessBody(logger zap.Logger, body []byte, contentType []string, u *url.URL) (*HTMLMetadata, content.State, error) {
	state := content.StateSuccess

	if !isHTML(body) {
		return nil, content.StateParseError, werrors.New(ErrBodyNotHTML)
	}

	bodyReader, err := bodyToUTF8(body, contentType)
	if err != nil {
		return nil, content.StateEncodingError, err
	}

	node, err := html.Parse(bodyReader)
	if err != nil {
		return nil, content.StateParseError, werrors.NewDetails(ErrHTMLParse, err)
	}

	parser, err := RunDataExtrator(node, u)
	if err != nil {
		return nil, content.StateParseError, err
	}
	if !parser.MetaTagIndex {
		return nil, content.StateNoFollow, werrors.NewLevel(WarnPageNotIndexed, werrors.WarningLevel)
	}

	for url, error := range parser.WrongURLs {
		logger.Warn("Error parse URL",
			zap.String("err_url", url),
			zap.String("error", error),
		)
	}

	err = RunMinificationHTML(node)
	if err != nil {
		return nil, content.StateParseError, err
	}

	err = RunMinificationText(node)
	if err != nil {
		return nil, content.StateParseError, err
	}

	return parser, state, nil
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// 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.

// make sure that even if a file imports runtime,
// it cannot get at the low-level runtime definitions
// known to the compiler.  for normal packages
// the compiler doesn't even record the lower case
// functions in its symbol table, but some functions
// in runtime are hard-coded into the compiler.

package main

import "runtime"

func main() {
	runtime.printbool(true);	// ERROR "cannot refer"
}
<commit_msg>Recognize gccgo error message.<commit_after>// errchk $G $D/$F.go

// 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.

// make sure that even if a file imports runtime,
// it cannot get at the low-level runtime definitions
// known to the compiler.  for normal packages
// the compiler doesn't even record the lower case
// functions in its symbol table, but some functions
// in runtime are hard-coded into the compiler.

package main

import "runtime"

func main() {
	runtime.printbool(true);	// ERROR "cannot refer|undefined identifier"
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"log"
	"os"
	"path/filepath"
	"strconv"
)

func importKeys(t *Golog, uid int, logDir string) {
	tx, err := t.Db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := tx.Prepare("insert into keyLogs (uid, time, count) values (?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	files, err := filepath.Glob(logDir + "/keyfreq*.txt")
	for _, file := range files {
		handle, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		defer handle.Close()

		scanner := bufio.NewScanner(handle)
		for scanner.Scan() {
			t := scanner.Text()
			if len(t) > 11 {
				unixtime := t[0:10]
				count := t[10:]
				i, err := strconv.ParseInt(count, 10, 64)
				if len(unixtime) > 0 && err == nil {
					_, err := stmt.Exec(uid, unixtime, i)
					if err != nil {
						log.Fatal(err)
					}
				}
			}
		}
	}
	tx.Commit()
}

func importWindows(t *Golog, uid int, logDir string) {
	tx, err := t.Db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := tx.Prepare("insert into windowLogs (uid, time, name) values (?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	files, err := filepath.Glob(logDir + "/window*.txt")
	for _, file := range files {
		handle, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		defer handle.Close()

		scanner := bufio.NewScanner(handle)
		for scanner.Scan() {
			t := scanner.Text()
			if len(t) > 12 {
				unixtime := t[0:10]
				title := t[11:]
				_, err := stmt.Exec(uid, unixtime, title)
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}
	tx.Commit()
}
<commit_msg>Correct int parsing<commit_after>package main

import (
	"bufio"
	"log"
	"os"
	"path/filepath"
	"strconv"
)

func importKeys(t *Golog, uid int, logDir string) {
	tx, err := t.Db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := tx.Prepare("insert into keyLogs (uid, time, count) values (?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	files, err := filepath.Glob(logDir + "/keyfreq*.txt")
	for _, file := range files {
		handle, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		defer handle.Close()

		scanner := bufio.NewScanner(handle)
		for scanner.Scan() {
			t := scanner.Text()
			if len(t) > 11 {
				unixtime := t[0:10]
				count := t[11:]
				i, err := strconv.ParseInt(count, 10, 64)
				if len(unixtime) > 0 && err == nil {
					_, err := stmt.Exec(uid, unixtime, i)
					if err != nil {
						log.Fatal(err)
					}
				}
			}
		}
	}
	tx.Commit()
}

func importWindows(t *Golog, uid int, logDir string) {
	tx, err := t.Db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := tx.Prepare("insert into windowLogs (uid, time, name) values (?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	files, err := filepath.Glob(logDir + "/window*.txt")
	for _, file := range files {
		handle, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		defer handle.Close()

		scanner := bufio.NewScanner(handle)
		for scanner.Scan() {
			t := scanner.Text()
			if len(t) > 12 {
				unixtime := t[0:10]
				title := t[11:]
				_, err := stmt.Exec(uid, unixtime, title)
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}
	tx.Commit()
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package process

import (
	"reflect"

	"github.com/juju/errors"
	"gopkg.in/juju/charm.v5"
)

// Info holds information about a process that Juju needs.
type Info struct {
	charm.Process

	// Status is the overall Juju status of the workload process.
	Status Status

	// Details is the information about the process which the plugin provided.
	Details LaunchDetails
}

// NewInfo builds a new Info object with the provided values.
func NewInfo(name, procType string) *Info {
	return &Info{
		Process: charm.Process{
			Name: name,
			Type: procType,
		},
	}
}

// Validate checks the process info to ensure it is correct.
func (info Info) Validate() error {
	if err := info.Process.Validate(); err != nil {
		return errors.Trace(err)
	}

	if info.Status.IsUnknown() {
		return errors.Errorf("bad status %#v", info.Status)
	}

	return nil
}

// IsRegistered indicates whether the represented process has been
// registered already.
func (info Info) IsRegistered() bool {
	return !reflect.DeepEqual(info, Info{Process: info.Process})
}
<commit_msg>Clarify a doc comment.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package process

import (
	"reflect"

	"github.com/juju/errors"
	"gopkg.in/juju/charm.v5"
)

// Info holds information about a process that Juju needs. Iff the
// process has not been registered with Juju then the Status and
// Details fields will be zero values.
//
// A registered process is one which has been defined in Juju (e.g. in
// charm metadata) and subsequently was launched by Juju (e.g. in a
// unit hook context).
type Info struct {
	charm.Process

	// Status is the overall Juju status of the workload process.
	Status Status

	// Details is the information about the process which the plugin provided.
	Details LaunchDetails
}

// NewInfo builds a new Info object with the provided values.
func NewInfo(name, procType string) *Info {
	return &Info{
		Process: charm.Process{
			Name: name,
			Type: procType,
		},
	}
}

// Validate checks the process info to ensure it is correct.
func (info Info) Validate() error {
	if err := info.Process.Validate(); err != nil {
		return errors.Trace(err)
	}

	if info.Status.IsUnknown() {
		return errors.Errorf("bad status %#v", info.Status)
	}

	return nil
}

// IsRegistered indicates whether the represented process has already
// been registered with Juju.
func (info Info) IsRegistered() bool {
	// An unregistered process will not have the Status and Details
	// fields set (they will be zero values). Thus a registered
	// process can be identified by non-zero values in those fields.
	// We use that fact here.
	return !reflect.DeepEqual(info, Info{Process: info.Process})
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"
	"github.com/jingweno/octokat"
	"github.com/octokit/go-octokit"
)

const (
	GitHubHost  string = "github.com"
	OAuthAppURL string = "http://owenou.com/gh"
)

type GitHub struct {
	Project *Project
	Config  *Config
}

func (gh *GitHub) PullRequest(id string) (pr *octokit.PullRequest, err error) {
	client := gh.octokit()
	prService, err := client.PullRequests(&octokit.PullRequestsURL, octokit.M{"owner": gh.Project.Owner, "repo": gh.Project.Name, "number": id})
	if err != nil {
		return
	}

	pr, result := prService.Get()
	if result.HasError() {
		err = result.Err
	}

	return
}

func (gh *GitHub) CreatePullRequest(base, head, title, body string) (url string, err error) {
	client := gh.octokit()
	prService, err := client.PullRequests(&octokit.PullRequestsURL, octokit.M{"owner": gh.Project.Owner, "repo": gh.Project.Name})
	if err != nil {
		return
	}

	params := octokit.PullRequestParams{Base: base, Head: head, Title: title, Body: body}
	pr, result := prService.Create(params)
	if result.HasError() {
		err = result.Err
	}

	url = pr.HTMLURL

	return
}

func (gh *GitHub) CreatePullRequestForIssue(base, head, issue string) (url string, err error) {
	client := gh.octokit()
	prService, err := client.PullRequests(&octokit.PullRequestsURL, octokit.M{"owner": gh.Project.Owner, "repo": gh.Project.Name})
	if err != nil {
		return
	}

	params := octokit.PullRequestForIssueParams{Base: base, Head: head, Issue: issue}
	pr, result := prService.Create(params)
	if result.HasError() {
		err = result.Err
	}

	url = pr.HTMLURL

	return
}

func (gh *GitHub) Repository(project Project) (repo *octokit.Repository, err error) {
	client := gh.octokit()
	repoService, err := client.Repositories(&octokit.RepositoryURL, octokit.M{"owner": project.Owner, "repo": project.Name})
	if err != nil {
		return
	}

	repo, result := repoService.Get()
	if result.HasError() {
		err = result.Err
	}

	return
}

// TODO: detach GitHub from Project
func (gh *GitHub) IsRepositoryExist(project Project) bool {
	repo, err := gh.Repository(project)

	return err == nil && repo != nil
}

func (gh *GitHub) CreateRepository(project Project, description, homepage string, isPrivate bool) (repo *octokit.Repository, err error) {
	var repoURL octokit.Hyperlink
	if project.Owner != gh.Config.FetchUser() {
		repoURL = octokit.OrgRepositoriesURL
	} else {
		repoURL = octokit.UserRepositoriesURL
	}

	client := gh.octokit()
	repoService, err := client.Repositories(&repoURL, octokit.M{"org": project.Owner})
	if err != nil {
		return
	}

	params := octokat.Repository{Name: project.Name, Description: description, Homepage: homepage, Private: isPrivate}
	repo, result := repoService.Create(params)
	if result.HasError() {
		err = result.Err
	}

	return
}

func (gh *GitHub) Releases() ([]octokat.Release, error) {
	client := gh.client()
	releases, err := client.Releases(gh.repo(), nil)
	if err != nil {
		return nil, err
	}

	return releases, nil
}

func (gh *GitHub) CiStatus(sha string) (*octokat.Status, error) {
	client := gh.client()
	statuses, err := client.Statuses(gh.repo(), sha, nil)
	if err != nil {
		return nil, err
	}

	if len(statuses) == 0 {
		return nil, nil
	}

	return &statuses[0], nil
}

func (gh *GitHub) ForkRepository(name, owner string, noRemote bool) (repo *octokit.Repository, err error) {
	config := gh.Config
	project := Project{Name: name, Owner: config.User}
	r, err := gh.Repository(project)
	if err == nil && r != nil {
		err = fmt.Errorf("Error creating fork: %s exists on %s", r.FullName, GitHubHost)
		return
	}

	client := gh.octokit()
	repoService, err := client.Repositories(&octokit.ForksURL, octokit.M{"owner": owner, "repo": name})
	repo, result := repoService.Create(nil)
	if result.HasError() {
		err = result.Err
	}

	return
}

func (gh *GitHub) ExpandRemoteUrl(owner, name string, isSSH bool) (url string) {
	project := gh.Project
	if owner == "origin" {
		config := gh.Config
		owner = config.FetchUser()
	}

	return project.GitURL(name, owner, isSSH)
}

func (gh *GitHub) repo() octokat.Repo {
	project := gh.Project
	return octokat.Repo{Name: project.Name, UserName: project.Owner}
}

func findOrCreateToken(user, password, twoFactorCode string) (string, error) {
	client := octokat.NewClient().WithLogin(user, password)
	options := &octokat.Options{}
	if twoFactorCode != "" {
		headers := octokat.Headers{"X-GitHub-OTP": twoFactorCode}
		options.Headers = headers
	}

	auths, err := client.Authorizations(options)
	if err != nil {
		return "", err
	}

	var token string
	for _, auth := range auths {
		if auth.NoteURL == OAuthAppURL {
			token = auth.Token
			break
		}
	}

	if token == "" {
		authParam := octokat.AuthorizationParams{}
		authParam.Scopes = append(authParam.Scopes, "repo")
		authParam.Note = "gh"
		authParam.NoteURL = OAuthAppURL
		options.Params = authParam

		auth, err := client.CreateAuthorization(options)
		if err != nil {
			return "", err
		}

		token = auth.Token
	}

	return token, nil
}

func (gh *GitHub) client() *octokat.Client {
	config := gh.Config
	config.FetchCredentials()

	return octokat.NewClient().WithToken(config.Token)
}

func (gh *GitHub) octokit() *octokit.Client {
	config := gh.Config
	config.FetchCredentials()
	tokenAuth := octokit.TokenAuth{AccessToken: config.Token}

	return octokit.NewClient(tokenAuth)
}

func New() *GitHub {
	project := CurrentProject()
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{project, c}
}

// TODO: detach project from GitHub
func NewWithoutProject() *GitHub {
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{nil, c}
}

func (gh *GitHub) Issues() ([]octokat.Issue, error) {
	client := gh.client()
	issues, err := client.Issues(gh.repo(), nil)
	if err != nil {
		return nil, err
	}

	return issues, nil
}
<commit_msg>Fix wrong reference to octokit<commit_after>package github

import (
	"fmt"
	"github.com/jingweno/octokat"
	"github.com/octokit/go-octokit"
)

const (
	GitHubHost  string = "github.com"
	OAuthAppURL string = "http://owenou.com/gh"
)

type GitHub struct {
	Project *Project
	Config  *Config
}

func (gh *GitHub) PullRequest(id string) (pr *octokit.PullRequest, err error) {
	client := gh.octokit()
	prService, err := client.PullRequests(&octokit.PullRequestsURL, octokit.M{"owner": gh.Project.Owner, "repo": gh.Project.Name, "number": id})
	if err != nil {
		return
	}

	pr, result := prService.Get()
	if result.HasError() {
		err = result.Err
	}

	return
}

func (gh *GitHub) CreatePullRequest(base, head, title, body string) (url string, err error) {
	client := gh.octokit()
	prService, err := client.PullRequests(&octokit.PullRequestsURL, octokit.M{"owner": gh.Project.Owner, "repo": gh.Project.Name})
	if err != nil {
		return
	}

	params := octokit.PullRequestParams{Base: base, Head: head, Title: title, Body: body}
	pr, result := prService.Create(params)
	if result.HasError() {
		err = result.Err
	}

	url = pr.HTMLURL

	return
}

func (gh *GitHub) CreatePullRequestForIssue(base, head, issue string) (url string, err error) {
	client := gh.octokit()
	prService, err := client.PullRequests(&octokit.PullRequestsURL, octokit.M{"owner": gh.Project.Owner, "repo": gh.Project.Name})
	if err != nil {
		return
	}

	params := octokit.PullRequestForIssueParams{Base: base, Head: head, Issue: issue}
	pr, result := prService.Create(params)
	if result.HasError() {
		err = result.Err
	}

	url = pr.HTMLURL

	return
}

func (gh *GitHub) Repository(project Project) (repo *octokit.Repository, err error) {
	client := gh.octokit()
	repoService, err := client.Repositories(&octokit.RepositoryURL, octokit.M{"owner": project.Owner, "repo": project.Name})
	if err != nil {
		return
	}

	repo, result := repoService.Get()
	if result.HasError() {
		err = result.Err
	}

	return
}

// TODO: detach GitHub from Project
func (gh *GitHub) IsRepositoryExist(project Project) bool {
	repo, err := gh.Repository(project)

	return err == nil && repo != nil
}

func (gh *GitHub) CreateRepository(project Project, description, homepage string, isPrivate bool) (repo *octokit.Repository, err error) {
	var repoURL octokit.Hyperlink
	if project.Owner != gh.Config.FetchUser() {
		repoURL = octokit.OrgRepositoriesURL
	} else {
		repoURL = octokit.UserRepositoriesURL
	}

	client := gh.octokit()
	repoService, err := client.Repositories(&repoURL, octokit.M{"org": project.Owner})
	if err != nil {
		return
	}

	params := octokit.Repository{Name: project.Name, Description: description, Homepage: homepage, Private: isPrivate}
	repo, result := repoService.Create(params)
	if result.HasError() {
		err = result.Err
	}

	return
}

func (gh *GitHub) Releases() ([]octokat.Release, error) {
	client := gh.client()
	releases, err := client.Releases(gh.repo(), nil)
	if err != nil {
		return nil, err
	}

	return releases, nil
}

func (gh *GitHub) CiStatus(sha string) (*octokat.Status, error) {
	client := gh.client()
	statuses, err := client.Statuses(gh.repo(), sha, nil)
	if err != nil {
		return nil, err
	}

	if len(statuses) == 0 {
		return nil, nil
	}

	return &statuses[0], nil
}

func (gh *GitHub) ForkRepository(name, owner string, noRemote bool) (repo *octokit.Repository, err error) {
	config := gh.Config
	project := Project{Name: name, Owner: config.User}
	r, err := gh.Repository(project)
	if err == nil && r != nil {
		err = fmt.Errorf("Error creating fork: %s exists on %s", r.FullName, GitHubHost)
		return
	}

	client := gh.octokit()
	repoService, err := client.Repositories(&octokit.ForksURL, octokit.M{"owner": owner, "repo": name})
	repo, result := repoService.Create(nil)
	if result.HasError() {
		err = result.Err
	}

	return
}

func (gh *GitHub) ExpandRemoteUrl(owner, name string, isSSH bool) (url string) {
	project := gh.Project
	if owner == "origin" {
		config := gh.Config
		owner = config.FetchUser()
	}

	return project.GitURL(name, owner, isSSH)
}

func (gh *GitHub) repo() octokat.Repo {
	project := gh.Project
	return octokat.Repo{Name: project.Name, UserName: project.Owner}
}

func findOrCreateToken(user, password, twoFactorCode string) (string, error) {
	client := octokat.NewClient().WithLogin(user, password)
	options := &octokat.Options{}
	if twoFactorCode != "" {
		headers := octokat.Headers{"X-GitHub-OTP": twoFactorCode}
		options.Headers = headers
	}

	auths, err := client.Authorizations(options)
	if err != nil {
		return "", err
	}

	var token string
	for _, auth := range auths {
		if auth.NoteURL == OAuthAppURL {
			token = auth.Token
			break
		}
	}

	if token == "" {
		authParam := octokat.AuthorizationParams{}
		authParam.Scopes = append(authParam.Scopes, "repo")
		authParam.Note = "gh"
		authParam.NoteURL = OAuthAppURL
		options.Params = authParam

		auth, err := client.CreateAuthorization(options)
		if err != nil {
			return "", err
		}

		token = auth.Token
	}

	return token, nil
}

func (gh *GitHub) client() *octokat.Client {
	config := gh.Config
	config.FetchCredentials()

	return octokat.NewClient().WithToken(config.Token)
}

func (gh *GitHub) octokit() *octokit.Client {
	config := gh.Config
	config.FetchCredentials()
	tokenAuth := octokit.TokenAuth{AccessToken: config.Token}

	return octokit.NewClient(tokenAuth)
}

func New() *GitHub {
	project := CurrentProject()
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{project, c}
}

// TODO: detach project from GitHub
func NewWithoutProject() *GitHub {
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{nil, c}
}

func (gh *GitHub) Issues() ([]octokat.Issue, error) {
	client := gh.client()
	issues, err := client.Issues(gh.repo(), nil)
	if err != nil {
		return nil, err
	}

	return issues, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// golint lints the Go source files named on its command line.
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/lint"
)

var minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] package\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] directory\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] files... # must be a single package\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	switch flag.NArg() {
	case 0:
		lintDir(".")
	case 1:
		arg := flag.Arg(0)
		if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-4]) {
			for _, dirname := range allPackagesInFS(arg) {
				lintDir(dirname)
			}
		} else if isDir(arg) {
			lintDir(arg)
		} else if exists(arg) {
			lintFiles(arg)
		} else {
			for _, pkgname := range importPaths([]string{arg}) {
				lintPackage(pkgname)
			}
		}
	default:
		lintFiles(flag.Args()...)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func lintFiles(filenames ...string) {
	files := make(map[string][]byte)
	for _, filename := range filenames {
		src, err := ioutil.ReadFile(filename)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		files[filename] = src
	}

	l := new(lint.Linter)
	ps, err := l.LintFiles(files)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	for _, p := range ps {
		if p.Confidence >= *minConfidence {
			fmt.Printf("%v: %s\n", p.Position, p.Text)
		}
	}
}

func lintDir(dirname string) {
	pkg, err := build.ImportDir(dirname, 0)
	lintImportedPackage(pkg, err)
}

func lintPackage(pkgname string) {
	pkg, err := build.Import(pkgname, ".", 0)
	lintImportedPackage(pkg, err)
}

func lintImportedPackage(pkg *build.Package, err error) {
	if err != nil {
		if _, nogo := err.(*build.NoGoError); nogo {
			// Don't complain if the failure is due to no Go source files.
			return
		}
		fmt.Fprintln(os.Stderr, err)
		return
	}

	var files []string
	files = append(files, pkg.GoFiles...)
	files = append(files, pkg.CgoFiles...)
	files = append(files, pkg.TestGoFiles...)
	if pkg.Dir != "." {
		for i, f := range files {
			files[i] = filepath.Join(pkg.Dir, f)
		}
	}
	// TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles)

	lintFiles(files...)
}
<commit_msg>golint: Add a flag to cause a non-zero exit when suggestions are made.<commit_after>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// golint lints the Go source files named on its command line.
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/lint"
)

var (
	minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")
	setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found")
	suggestions   int
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] package\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] directory\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] files... # must be a single package\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	switch flag.NArg() {
	case 0:
		lintDir(".")
	case 1:
		arg := flag.Arg(0)
		if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-4]) {
			for _, dirname := range allPackagesInFS(arg) {
				lintDir(dirname)
			}
		} else if isDir(arg) {
			lintDir(arg)
		} else if exists(arg) {
			lintFiles(arg)
		} else {
			for _, pkgname := range importPaths([]string{arg}) {
				lintPackage(pkgname)
			}
		}
	default:
		lintFiles(flag.Args()...)
	}

	if *setExitStatus && suggestions > 0 {
		fmt.Fprintf(os.Stderr, "Found %d lint suggestions; failing.\n", suggestions)
		os.Exit(1)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func lintFiles(filenames ...string) {
	files := make(map[string][]byte)
	for _, filename := range filenames {
		src, err := ioutil.ReadFile(filename)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		files[filename] = src
	}

	l := new(lint.Linter)
	ps, err := l.LintFiles(files)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	for _, p := range ps {
		if p.Confidence >= *minConfidence {
			fmt.Printf("%v: %s\n", p.Position, p.Text)
			suggestions++
		}
	}
}

func lintDir(dirname string) {
	pkg, err := build.ImportDir(dirname, 0)
	lintImportedPackage(pkg, err)
}

func lintPackage(pkgname string) {
	pkg, err := build.Import(pkgname, ".", 0)
	lintImportedPackage(pkg, err)
}

func lintImportedPackage(pkg *build.Package, err error) {
	if err != nil {
		if _, nogo := err.(*build.NoGoError); nogo {
			// Don't complain if the failure is due to no Go source files.
			return
		}
		fmt.Fprintln(os.Stderr, err)
		return
	}

	var files []string
	files = append(files, pkg.GoFiles...)
	files = append(files, pkg.CgoFiles...)
	files = append(files, pkg.TestGoFiles...)
	if pkg.Dir != "." {
		for i, f := range files {
			files[i] = filepath.Join(pkg.Dir, f)
		}
	}
	// TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles)

	lintFiles(files...)
}
<|endoftext|>


<commit_before>package gonzo

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"net"
	"strings"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/tomb.v2"
)

type Backend interface {
	HandleQuery(c net.Conn, query *OpQueryMsg)
	HandleInsert(c net.Conn, insert *OpInsertMsg)

	DBNames() []string
	DB(name string) DB
}

type DB interface {
	Empty() bool
	CNames() []string
	C(name string) Collection

	LastError() error
	SetLastError(err error)
}

type Collection interface {
	Id(id string) interface{}
	All() []interface{}
	Match(pattern bson.M) []interface{}
	Insert(item interface{}) error
}

type MemoryCollection struct {
	docs map[string]bson.M
}

type MemoryDB struct {
	collections map[string]*MemoryCollection
	lastErr     error
}

func NewMemoryDB() *MemoryDB {
	return &MemoryDB{collections: make(map[string]*MemoryCollection)}
}

func (db *MemoryDB) Empty() bool {
	return len(db.collections) == 0
}

func (db *MemoryDB) CNames() (result []string) {
	for cname, _ := range db.collections {
		result = append(result, cname)
	}
	return result
}

func (db *MemoryDB) C(name string) Collection {
	result, ok := db.collections[name]
	if !ok {
		result = &MemoryCollection{docs: make(map[string]bson.M)}
		db.collections[name] = result
	}
	return result
}

func (db *MemoryDB) LastError() error       { return db.lastErr }
func (db *MemoryDB) SetLastError(err error) { db.lastErr = err }

func (c *MemoryCollection) Id(id string) interface{} {
	for _, item := range c.docs {
		mitem := item
		if match, ok := mitem["_id"]; ok && match == id {
			return item
		}
	}
	return nil
}

func (c *MemoryCollection) All() (result []interface{}) {
	for _, doc := range c.docs {
		result = append(result, doc)
	}
	return result
}

func (c *MemoryCollection) Match(pattern bson.M) (result []interface{}) {
	for _, doc := range c.docs {
		if isPatternMatch(doc, pattern) {
			result = append(result, doc)
		}
	}
	return result
}

func isPatternMatch(doc, pattern bson.M) bool {
	for matchKey, matchValue := range pattern {
		value, ok := doc[matchKey]
		if !ok || matchValue != value {
			return false
		}
	}
	return true
}

func (c *MemoryCollection) Insert(doc interface{}) error {
	mdoc, ok := doc.(bson.M)
	if !ok {
		return fmt.Errorf("cannot insert instance of this type: %v", doc)
	}
	var idStr string
	if id, ok := mdoc["_id"]; ok {
		idStr = id.(fmt.Stringer).String()
	} else {
		oid := bson.NewObjectId()
		mdoc["_id"] = oid
		idStr = oid.String()
	}
	c.docs[idStr] = mdoc
	return nil
}

type MemoryBackend struct {
	dbs map[string]*MemoryDB
	t   *tomb.Tomb
}

func NewMemoryBackend(t *tomb.Tomb) *MemoryBackend {
	return &MemoryBackend{
		dbs: make(map[string]*MemoryDB),
		t:   t,
	}
}

func (b *MemoryBackend) DBNames() (result []string) {
	for dbname, _ := range b.dbs {
		result = append(result, dbname)
	}
	return result
}

func (b *MemoryBackend) DB(name string) DB {
	result, ok := b.dbs[name]
	if !ok {
		result = NewMemoryDB()
		b.dbs[name] = result
	}
	return result
}

func (b *MemoryBackend) HandleQuery(c net.Conn, query *OpQueryMsg) {
	if query.FullCollectionName == "admin.$cmd" {
		err := b.handleAdminCommand(c, query)
		if err != nil {
			log.Println(err)
		}
		return
	}

	fields := strings.SplitN(query.FullCollectionName, ".", 2)
	if len(fields) < 2 {
		respError(c, query.RequestID, fmt.Errorf("malformed full collection name %q", query.FullCollectionName))
		return
	}
	dbname, cname := fields[0], fields[1]
	if strings.HasPrefix(cname, "system.") {
		b.handleSystemQuery(c, query, dbname, cname)
		return
	}
	db := b.DB(dbname)
	if cname == "$cmd" {
		err := b.handleDBCommand(c, db, query)
		if err != nil {
			log.Println(err)
		}
		return
	}
	coll := db.C(cname)

	var results []interface{}
	if match, ok := query.Get("$query"); ok {
		if matchDoc, ok := match.(bson.M); ok {
			results = append(results, coll.Match(matchDoc)...)
		} else {
			respError(c, query.RequestID, fmt.Errorf("unexpected $query type %v", match))
			return
		}
	} else if len(query.Doc) == 0 {
		results = append(results, coll.All()...)
	} else {
		results = append(results, coll.Match(query.Doc.Map())...)
	}
	respDoc(c, query.RequestID, results...)
}

func (b *MemoryBackend) HandleInsert(c net.Conn, insert *OpInsertMsg) {
	if strings.HasPrefix(insert.FullCollectionName, "admin.") {
		respError(c, insert.RequestID, fmt.Errorf("insert not supported on admin.*"))
		return
	}

	fields := strings.SplitN(insert.FullCollectionName, ".", 2)
	if len(fields) < 2 {
		respError(c, insert.RequestID, fmt.Errorf("malformed full collection name %q", insert.FullCollectionName))
		return
	}
	dbname, cname := fields[0], fields[1]
	if strings.HasPrefix(cname, "system.") {
		respError(c, insert.RequestID, fmt.Errorf("insert not supported on %s.system.*", dbname))
		return
	}
	db := b.DB(dbname)
	coll := db.C(cname)
	for _, doc := range insert.Docs {
		err := coll.Insert(doc)
		db.SetLastError(err)
		if err != nil {
			log.Println(err)
			return
		}
	}
	//respDoc(c, insert.RequestID, markOk(bson.D{}))
	// TODO: if err != nil { ... set last error ... }
}

// TODO: implement Collection interface instead
func (b *MemoryBackend) handleSystemQuery(c net.Conn, query *OpQueryMsg, dbname, cname string) {
	log.Println("system query:", dbname, cname, query.Doc)
	switch cname {
	case "system.namespaces":
		var result []interface{}
		for _, name := range b.DB(dbname).CNames() {
			result = append(result, bson.D{{"name", name}})
		}
		respDoc(c, query.RequestID, result...)
		return
	}
	respError(c, query.RequestID, fmt.Errorf(
		"unsupported system query on %s: %v", query.FullCollectionName, query.Doc))
}

func (b *MemoryBackend) handleDBCommand(c net.Conn, db DB, query *OpQueryMsg) error {
	switch cmd, _ := query.Command(); cmd {
	case "getLastError":
		fallthrough
	case "getlasterror":
		return respError(c, query.RequestID, db.LastError())
	}
	return respError(c, query.RequestID, fmt.Errorf("unsupported db command: %v", query))
}

func (b *MemoryBackend) handleAdminCommand(c net.Conn, query *OpQueryMsg) error {
	switch cmd, arg := query.Command(); cmd {
	case "getLog":
		var msg bson.D
		switch logName := arg.(string); logName {
		case "*":
			msg = markOk(bson.D{{"names", []string{"startupWarnings"}}})
		case "startupWarnings":
			msg = markOk(bson.D{
				{"totalLinesWritten", 0},
				{"log", []string{}},
			})
		default:
			msg = errReply(fmt.Errorf("log not found: %q", logName))
		}
		return respDoc(c, query.RequestID, msg)
	case "listDatabases":
		var dbinfos []bson.D
		for _, dbname := range b.DBNames() {
			dbinfos = append(dbinfos, bson.D{
				{"name", dbname},
				{"empty", b.DB(dbname).Empty()},
			})
		}
		return respDoc(c, query.RequestID, markOk(bson.D{
			{"databases", dbinfos},
		}))
	case "replSetGetStatus":
		return respError(c, query.RequestID, fmt.Errorf("not running with --replSet"))
	case "shutdown":
		log.Println("shutdown requested")
		b.t.Kill(nil)
		return c.Close()
	case "whatsmyuri":
		return respDoc(c, query.RequestID, bson.D{{"you", c.RemoteAddr().String()}})
	case "ismaster":
		return respDoc(c, query.RequestID, markOk(bson.D{{"ismaster", true}}))
	case "getnonce":
		nonce := make([]byte, 32)
		_, err := rand.Reader.Read(nonce[:])
		if err != nil {
			return err
		}
		return respDoc(c, query.RequestID, markOk(bson.D{
			{"nonce", hex.EncodeToString(nonce)},
		}))
	case "ping":
		return respDoc(c, query.RequestID, markOk(nil))
	}
	return respError(c, query.RequestID, fmt.Errorf("unsupported admin command: %v", query))
}
<commit_msg>Protect in-memory databases and collections with RWMutexes.<commit_after>package gonzo

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/tomb.v2"
)

type Backend interface {
	HandleQuery(c net.Conn, query *OpQueryMsg)
	HandleInsert(c net.Conn, insert *OpInsertMsg)

	DBNames() []string
	DB(name string) DB
}

type DB interface {
	Empty() bool
	CNames() []string
	C(name string) Collection

	LastError() error
	SetLastError(err error)
}

type Collection interface {
	Id(id string) interface{}
	All() []interface{}
	Match(pattern bson.M) []interface{}
	Insert(item interface{}) error
}

type MemoryCollection struct {
	docs map[string]bson.M

	mu sync.RWMutex
}

type MemoryDB struct {
	collections map[string]*MemoryCollection
	lastErr     error

	mu sync.RWMutex
}

func NewMemoryDB() *MemoryDB {
	return &MemoryDB{collections: make(map[string]*MemoryCollection)}
}

func (db *MemoryDB) Empty() bool {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return len(db.collections) == 0
}

func (db *MemoryDB) CNames() (result []string) {
	db.mu.RLock()
	defer db.mu.RUnlock()
	for cname, _ := range db.collections {
		result = append(result, cname)
	}
	return result
}

func (db *MemoryDB) C(name string) Collection {
	db.mu.Lock()
	defer db.mu.Unlock()
	result, ok := db.collections[name]
	if !ok {
		result = &MemoryCollection{docs: make(map[string]bson.M)}
		db.collections[name] = result
	}
	return result
}

func (db *MemoryDB) LastError() error {
	db.mu.RLock()
	defer db.mu.RUnlock()
	return db.lastErr
}

func (db *MemoryDB) SetLastError(err error) {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.lastErr = err
}

func (c *MemoryCollection) Id(id string) interface{} {
	c.mu.RLock()
	defer c.mu.RUnlock()
	for _, item := range c.docs {
		mitem := item
		if match, ok := mitem["_id"]; ok && match == id {
			return item
		}
	}
	return nil
}

func (c *MemoryCollection) All() (result []interface{}) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	for _, doc := range c.docs {
		result = append(result, doc)
	}
	return result
}

func (c *MemoryCollection) Match(pattern bson.M) (result []interface{}) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	for _, doc := range c.docs {
		if isPatternMatch(doc, pattern) {
			result = append(result, doc)
		}
	}
	return result
}

func isPatternMatch(doc, pattern bson.M) bool {
	for matchKey, matchValue := range pattern {
		value, ok := doc[matchKey]
		if !ok || matchValue != value {
			return false
		}
	}
	return true
}

func (c *MemoryCollection) Insert(doc interface{}) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	mdoc, ok := doc.(bson.M)
	if !ok {
		return fmt.Errorf("cannot insert instance of this type: %v", doc)
	}
	var idStr string
	if id, ok := mdoc["_id"]; ok {
		idStr = id.(fmt.Stringer).String()
	} else {
		oid := bson.NewObjectId()
		mdoc["_id"] = oid
		idStr = oid.String()
	}
	c.docs[idStr] = mdoc
	return nil
}

type MemoryBackend struct {
	dbs map[string]*MemoryDB
	t   *tomb.Tomb
}

func NewMemoryBackend(t *tomb.Tomb) *MemoryBackend {
	return &MemoryBackend{
		dbs: make(map[string]*MemoryDB),
		t:   t,
	}
}

func (b *MemoryBackend) DBNames() (result []string) {
	for dbname, _ := range b.dbs {
		result = append(result, dbname)
	}
	return result
}

func (b *MemoryBackend) DB(name string) DB {
	result, ok := b.dbs[name]
	if !ok {
		result = NewMemoryDB()
		b.dbs[name] = result
	}
	return result
}

func (b *MemoryBackend) HandleQuery(c net.Conn, query *OpQueryMsg) {
	if query.FullCollectionName == "admin.$cmd" {
		err := b.handleAdminCommand(c, query)
		if err != nil {
			log.Println(err)
		}
		return
	}

	fields := strings.SplitN(query.FullCollectionName, ".", 2)
	if len(fields) < 2 {
		respError(c, query.RequestID, fmt.Errorf("malformed full collection name %q", query.FullCollectionName))
		return
	}
	dbname, cname := fields[0], fields[1]
	if strings.HasPrefix(cname, "system.") {
		b.handleSystemQuery(c, query, dbname, cname)
		return
	}
	db := b.DB(dbname)
	if cname == "$cmd" {
		err := b.handleDBCommand(c, db, query)
		if err != nil {
			log.Println(err)
		}
		return
	}
	coll := db.C(cname)

	var results []interface{}
	if match, ok := query.Get("$query"); ok {
		if matchDoc, ok := match.(bson.M); ok {
			results = append(results, coll.Match(matchDoc)...)
		} else {
			respError(c, query.RequestID, fmt.Errorf("unexpected $query type %v", match))
			return
		}
	} else if len(query.Doc) == 0 {
		results = append(results, coll.All()...)
	} else {
		results = append(results, coll.Match(query.Doc.Map())...)
	}
	respDoc(c, query.RequestID, results...)
}

func (b *MemoryBackend) HandleInsert(c net.Conn, insert *OpInsertMsg) {
	if strings.HasPrefix(insert.FullCollectionName, "admin.") {
		respError(c, insert.RequestID, fmt.Errorf("insert not supported on admin.*"))
		return
	}

	fields := strings.SplitN(insert.FullCollectionName, ".", 2)
	if len(fields) < 2 {
		respError(c, insert.RequestID, fmt.Errorf("malformed full collection name %q", insert.FullCollectionName))
		return
	}
	dbname, cname := fields[0], fields[1]
	if strings.HasPrefix(cname, "system.") {
		respError(c, insert.RequestID, fmt.Errorf("insert not supported on %s.system.*", dbname))
		return
	}
	db := b.DB(dbname)
	coll := db.C(cname)
	for _, doc := range insert.Docs {
		err := coll.Insert(doc)
		db.SetLastError(err)
		if err != nil {
			log.Println(err)
			return
		}
	}
	//respDoc(c, insert.RequestID, markOk(bson.D{}))
	// TODO: if err != nil { ... set last error ... }
}

// TODO: implement Collection interface instead
func (b *MemoryBackend) handleSystemQuery(c net.Conn, query *OpQueryMsg, dbname, cname string) {
	log.Println("system query:", dbname, cname, query.Doc)
	switch cname {
	case "system.namespaces":
		var result []interface{}
		for _, name := range b.DB(dbname).CNames() {
			result = append(result, bson.D{{"name", name}})
		}
		respDoc(c, query.RequestID, result...)
		return
	}
	respError(c, query.RequestID, fmt.Errorf(
		"unsupported system query on %s: %v", query.FullCollectionName, query.Doc))
}

func (b *MemoryBackend) handleDBCommand(c net.Conn, db DB, query *OpQueryMsg) error {
	switch cmd, _ := query.Command(); cmd {
	case "getLastError":
		fallthrough
	case "getlasterror":
		return respError(c, query.RequestID, db.LastError())
	}
	return respError(c, query.RequestID, fmt.Errorf("unsupported db command: %v", query))
}

func (b *MemoryBackend) handleAdminCommand(c net.Conn, query *OpQueryMsg) error {
	switch cmd, arg := query.Command(); cmd {
	case "getLog":
		var msg bson.D
		switch logName := arg.(string); logName {
		case "*":
			msg = markOk(bson.D{{"names", []string{"startupWarnings"}}})
		case "startupWarnings":
			msg = markOk(bson.D{
				{"totalLinesWritten", 0},
				{"log", []string{}},
			})
		default:
			msg = errReply(fmt.Errorf("log not found: %q", logName))
		}
		return respDoc(c, query.RequestID, msg)
	case "listDatabases":
		var dbinfos []bson.D
		for _, dbname := range b.DBNames() {
			dbinfos = append(dbinfos, bson.D{
				{"name", dbname},
				{"empty", b.DB(dbname).Empty()},
			})
		}
		return respDoc(c, query.RequestID, markOk(bson.D{
			{"databases", dbinfos},
		}))
	case "replSetGetStatus":
		return respError(c, query.RequestID, fmt.Errorf("not running with --replSet"))
	case "shutdown":
		log.Println("shutdown requested")
		b.t.Kill(nil)
		return c.Close()
	case "whatsmyuri":
		return respDoc(c, query.RequestID, bson.D{{"you", c.RemoteAddr().String()}})
	case "ismaster":
		return respDoc(c, query.RequestID, markOk(bson.D{{"ismaster", true}}))
	case "getnonce":
		nonce := make([]byte, 32)
		_, err := rand.Reader.Read(nonce[:])
		if err != nil {
			return err
		}
		return respDoc(c, query.RequestID, markOk(bson.D{
			{"nonce", hex.EncodeToString(nonce)},
		}))
	case "ping":
		return respDoc(c, query.RequestID, markOk(nil))
	}
	return respError(c, query.RequestID, fmt.Errorf("unsupported admin command: %v", query))
}
<|endoftext|>


<commit_before>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/blob/master/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	_, err := io.ReadFull(rand.Reader, nonceBytes)
	if err != nil {
		panic("gothic: source of randomness unavailable: " + err.Error())
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// As a fallback, loop over the used providers, if we already have a valid session for any provider (ie. user has already begun authentication with a provider), then return that provider name
	providers := goth.GetProviders()
	session, _ := Store.Get(req, SessionName)
	for _, provider := range providers {
		p := provider.Name()
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

// StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.New(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

// GetFromSession retrieves a previously-stored value from the session.
// If no value has previously been stored at the specified key, it will return an error.
func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<commit_msg>Export a non-collidable context key for provider<commit_after>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/blob/master/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

type key int
// ProviderParamKey can be used as a key in context when passing in a provider
const ProviderParamKey key = iota

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	_, err := io.ReadFull(rand.Reader, nonceBytes)
	if err != nil {
		panic("gothic: source of randomness unavailable: " + err.Error())
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// try to get it from the go-context's value of providerContextKey key
	if p, ok := req.Context().Value(ProviderParamKey).(string); ok {
		return p, nil
	}

	// As a fallback, loop over the used providers, if we already have a valid session for any provider (ie. user has already begun authentication with a provider), then return that provider name
	providers := goth.GetProviders()
	session, _ := Store.Get(req, SessionName)
	for _, provider := range providers {
		p := provider.Name()
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

// StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.New(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

// GetFromSession retrieves a previously-stored value from the session.
// If no value has previously been stored at the specified key, it will return an error.
func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<|endoftext|>


<commit_before>package flash

import "net/http"

type Route struct {
	router  *Router
	prefix  string
	handler http.Handler
	params  map[string]string
	ctr     func(map[string]string) http.HandlerFunc
}

// HandleFunc setting function to handle route
func (r *Route) HandleFunc(s string, f func(http.ResponseWriter, *http.Request)) {
	r.NewRoute(s).HandlerFunc(f).addRoute()
}

// Route registers a new route with a matcher for URL path
// ex:
//    r := api.NewRouter()
//    api = r.PathPrefix("/api/v1")
//    api.Route("/pages/:id/comments", PageComments, AuthFunc)
// where
//  - PageComments is the function implementing func(*flash.Ctx)
//  - AuthFunc is middleware function that implements ReqFunc.
//
func (r *Route) Route(path string, f handlerFunc, funcs ...ReqFunc) {
	route := r.NewRoute(path)
	route.ctr = func(params map[string]string) http.HandlerFunc {
		return http.HandlerFunc(handleRoute(f, params, funcs...))
	}

	route.addRoute()
}

// Resource registers a new route with a matcher for URL path
// and registering controller handler
// ex:
//    r := api.NewRouter()
//    api = r.PathPrefix("/api/v1")
//    api.Resource("/pages", &PagesController{}, AuthFunc)
// where
//  - PagesController is the type implementing Controller
//  - AuthFunc is middleware function that implements ReqFunc.
//
func (r *Route) Resource(path string, i Ctr, funcs ...ReqFunc) {
	route := r.NewRoute(path)
	route.ctr = func(params map[string]string) http.HandlerFunc {
		return http.HandlerFunc(handleResource(i, params, implements(i), funcs...))
	}

	route.addRoute()
}

// FileServer provides static files serving
// ex:
//    r := api.NewRouter()
//    dirIndex := false
//    preferGzip := false
//    r.PathPrefix("/images/").FileServer("./public", dirIndex, preferGzip)
//
// where
//  - dirIndex specifying if it should display directory content or not
//  - preferGzip specifying if it should look for gzipped file version
//
func (r *Route) FileServer(path string, b ...bool) {
	r.Handler(fileServer(path, b)).addRoute()
}

// NewRoute registers an empty route.
func (r *Route) NewRoute(prefix string) *Route {
	return &Route{router: r.router, prefix: cleanPath(r.prefix + prefix)}
}

// Handler sets a handler for the route.
func (r *Route) Handler(handler http.Handler) *Route {
	r.handler = handler
	return r
}

// HandlerFunc sets a handler function for the route.
func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handler(http.HandlerFunc(f))
}

func (r *Route) addRoute() {
	if r.ctr != nil {
		r.router.tree.assign(r, r.prefix, "id", "action")
		return
	}
	r.router.tree.assign(r, r.prefix)
}
<commit_msg>adding comment to Route<commit_after>package flash

import "net/http"

// Route storing route information
type Route struct {
	router  *Router
	prefix  string
	handler http.Handler
	params  map[string]string
	ctr     func(map[string]string) http.HandlerFunc
}

// HandleFunc setting function to handle route
func (r *Route) HandleFunc(s string, f func(http.ResponseWriter, *http.Request)) {
	r.NewRoute(s).HandlerFunc(f).addRoute()
}

// Route registers a new route with a matcher for URL path
// ex:
//    r := api.NewRouter()
//    api = r.PathPrefix("/api/v1")
//    api.Route("/pages/:id/comments", PageComments, AuthFunc)
// where
//  - PageComments is the function implementing func(*flash.Ctx)
//  - AuthFunc is middleware function that implements ReqFunc.
//
func (r *Route) Route(path string, f handlerFunc, funcs ...ReqFunc) {
	route := r.NewRoute(path)
	route.ctr = func(params map[string]string) http.HandlerFunc {
		return http.HandlerFunc(handleRoute(f, params, funcs...))
	}

	route.addRoute()
}

// Resource registers a new route with a matcher for URL path
// and registering controller handler
// ex:
//    r := api.NewRouter()
//    api = r.PathPrefix("/api/v1")
//    api.Resource("/pages", &PagesController{}, AuthFunc)
// where
//  - PagesController is the type implementing Controller
//  - AuthFunc is middleware function that implements ReqFunc.
//
func (r *Route) Resource(path string, i Ctr, funcs ...ReqFunc) {
	route := r.NewRoute(path)
	route.ctr = func(params map[string]string) http.HandlerFunc {
		return http.HandlerFunc(handleResource(i, params, implements(i), funcs...))
	}

	route.addRoute()
}

// FileServer provides static files serving
// ex:
//    r := api.NewRouter()
//    dirIndex := false
//    preferGzip := false
//    r.PathPrefix("/images/").FileServer("./public", dirIndex, preferGzip)
//
// where
//  - dirIndex specifying if it should display directory content or not
//  - preferGzip specifying if it should look for gzipped file version
//
func (r *Route) FileServer(path string, b ...bool) {
	r.Handler(fileServer(path, b)).addRoute()
}

// NewRoute registers an empty route.
func (r *Route) NewRoute(prefix string) *Route {
	return &Route{router: r.router, prefix: cleanPath(r.prefix + prefix)}
}

// Handler sets a handler for the route.
func (r *Route) Handler(handler http.Handler) *Route {
	r.handler = handler
	return r
}

// HandlerFunc sets a handler function for the route.
func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handler(http.HandlerFunc(f))
}

func (r *Route) addRoute() {
	if r.ctr != nil {
		r.router.tree.assign(r, r.prefix, "id", "action")
		return
	}
	r.router.tree.assign(r, r.prefix)
}
<|endoftext|>


<commit_before>package mux

import (
	"net/http"

	muxpath "github.com/gogolfing/mux/path"
)

type Route struct {
	path         string
	children     []*Route
	routeHandler *routeHandler
}

func newRoute(path string, children ...*Route) *Route {
	return &Route{
		path,
		children,
		nil,
	}
}

func (route *Route) Delete(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) DeleteFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Get(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) GetFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Head(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) HeadFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Options(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) OptionsFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Post(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) PostFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Put(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) PutFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) SubRoute(path string) *Route {
	return route.insertSubRoute(path)
}

func (route *Route) HandleFunc(handlerFunc http.HandlerFunc, methods ...string) *Route {
	return route.Handle(http.HandlerFunc(handlerFunc), methods...)
}

func (route *Route) Handle(handler http.Handler, methods ...string) *Route {
	if route.routeHandler == nil {
		route.routeHandler = &routeHandler{}
	}
	route.routeHandler.handle(handler, methods...)
	return route
}

func (route *Route) getHandler(r *http.Request) (http.Handler, error) {
	if route.routeHandler == nil {
		return nil, ErrNotFound
	}
	return route.routeHandler.getHandler(r)
}

func (route *Route) insertSubRoute(path string) *Route {
	parent, found, remainingPath := route.findSubRoute(path)
	if len(remainingPath) == 0 {
		return found
	}
	if found == nil {
		return parent.insertLeaf(remainingPath)
	}
	return parent.insertSplitChild(remainingPath)
}

func (route *Route) insertLeaf(path string) *Route {
	child := newRoute(path)
	route.insertChildAtIndex(child, 0)
	return child
}

func (route *Route) insertSplitChild(path string) *Route {
	oldChild, index, _ := route.findChildWithCommonPrefix(path)
	newChild := newRoute(path, oldChild)
	route.children[index] = newChild
	oldChild.path = oldChild.path[len(path):]
	return newChild
}

func (route *Route) findSubRoute(path string) (*Route, *Route, string) {
	parent := route
	child, _, prefix := parent.findChildWithCommonPrefix(path)
	for child != nil && len(path) > 0 && len(prefix) == len(child.path) {
		path = path[len(prefix):]
		if len(path) == 0 {
			break
		}
		parent = child
		child, _, prefix = parent.findChildWithCommonPrefix(path)
	}
	return parent, child, path
}

func (route *Route) findChildWithCommonPrefix(path string) (*Route, int, string) {
	index, prefix := route.indexOfCommonPrefixChild(path)
	if index >= 0 {
		return route.children[index], index, prefix
	}
	return nil, index, prefix
}

func (route *Route) indexOfCommonPrefixChild(path string) (int, string) {
	low, high := 0, len(route.children)
	for low < high {
		mid := (low + high) >> 1
		comparison, prefix := muxpath.CompareIgnorePrefix(path, route.children[mid].path)
		if len(prefix) > 0 {
			return mid, prefix
		} else if comparison == 0 {
			return mid, path
		} else if comparison < 0 {
			high = mid
		} else { //comparison must be > 0.
			low = mid + 1
		}
	}
	return ^high, ""
}

func (route *Route) insertChildAtIndex(child *Route, index int) {
	if index < 0 || index > len(route.children) {
		return
	}
	if route.children == nil {
		route.children = []*Route{child}
		return
	}
	before := route.children[:index]
	after := route.children[index:]
	route.children = make([]*Route, 0, len(before)+1+len(after))
	route.children = append(route.children, before...)
	route.children = append(route.children, child)
	route.children = append(route.children, after...)
}

func (route *Route) Methods() []string {
	return route.routeHandler.methods()
}
<commit_msg>Removes uncommon methods from *Route.<commit_after>package mux

import (
	"net/http"

	muxpath "github.com/gogolfing/mux/path"
)

type Route struct {
	path         string
	children     []*Route
	routeHandler *routeHandler
}

func newRoute(path string, children ...*Route) *Route {
	return &Route{
		path,
		children,
		nil,
	}
}

func (route *Route) Delete(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) DeleteFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Get(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) GetFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Post(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) PostFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) Put(handler http.Handler) *Route {
	return route.Handle(handler)
}

func (route *Route) PutFunc(handlerFunc http.HandlerFunc) *Route {
	return route.HandleFunc(handlerFunc)
}

func (route *Route) SubRoute(path string) *Route {
	return route.insertSubRoute(path)
}

func (route *Route) HandleFunc(handlerFunc http.HandlerFunc, methods ...string) *Route {
	return route.Handle(http.HandlerFunc(handlerFunc), methods...)
}

func (route *Route) Handle(handler http.Handler, methods ...string) *Route {
	if route.routeHandler == nil {
		route.routeHandler = &routeHandler{}
	}
	route.routeHandler.handle(handler, methods...)
	return route
}

func (route *Route) getHandler(r *http.Request) (http.Handler, error) {
	if route.routeHandler == nil {
		return nil, ErrNotFound
	}
	return route.routeHandler.getHandler(r)
}

func (route *Route) insertSubRoute(path string) *Route {
	parent, found, remainingPath := route.findSubRoute(path)
	if len(remainingPath) == 0 {
		return found
	}
	if found == nil {
		return parent.insertLeaf(remainingPath)
	}
	return parent.insertSplitChild(remainingPath)
}

func (route *Route) insertLeaf(path string) *Route {
	child := newRoute(path)
	route.insertChildAtIndex(child, 0)
	return child
}

func (route *Route) insertSplitChild(path string) *Route {
	oldChild, index, _ := route.findChildWithCommonPrefix(path)
	newChild := newRoute(path, oldChild)
	route.children[index] = newChild
	oldChild.path = oldChild.path[len(path):]
	return newChild
}

func (route *Route) findSubRoute(path string) (*Route, *Route, string) {
	parent := route
	child, _, prefix := parent.findChildWithCommonPrefix(path)
	for child != nil && len(path) > 0 && len(prefix) == len(child.path) {
		path = path[len(prefix):]
		if len(path) == 0 {
			break
		}
		parent = child
		child, _, prefix = parent.findChildWithCommonPrefix(path)
	}
	return parent, child, path
}

func (route *Route) findChildWithCommonPrefix(path string) (*Route, int, string) {
	index, prefix := route.indexOfCommonPrefixChild(path)
	if index >= 0 {
		return route.children[index], index, prefix
	}
	return nil, index, prefix
}

func (route *Route) indexOfCommonPrefixChild(path string) (int, string) {
	low, high := 0, len(route.children)
	for low < high {
		mid := (low + high) >> 1
		comparison, prefix := muxpath.CompareIgnorePrefix(path, route.children[mid].path)
		if len(prefix) > 0 {
			return mid, prefix
		} else if comparison == 0 {
			return mid, path
		} else if comparison < 0 {
			high = mid
		} else { //comparison must be > 0.
			low = mid + 1
		}
	}
	return ^high, ""
}

func (route *Route) insertChildAtIndex(child *Route, index int) {
	if index < 0 || index > len(route.children) {
		return
	}
	if route.children == nil {
		route.children = []*Route{child}
		return
	}
	before := route.children[:index]
	after := route.children[index:]
	route.children = make([]*Route, 0, len(before)+1+len(after))
	route.children = append(route.children, before...)
	route.children = append(route.children, child)
	route.children = append(route.children, after...)
}

func (route *Route) Methods() []string {
	return route.routeHandler.methods()
}
<|endoftext|>