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

package ld

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"fmt"
	"log"
)

// A BuildMode indicates the sort of object we are building.
//
// Possible build modes are the same as those for the -buildmode flag
// in cmd/go, and are documented in 'go help buildmode'.
type BuildMode uint8

const (
	BuildModeUnset BuildMode = iota
	BuildModeExe
	BuildModePIE
	BuildModeCArchive
	BuildModeCShared
	BuildModeShared
	BuildModePlugin
)

func (mode *BuildMode) Set(s string) error {
	badmode := func() error {
		return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH)
	}
	switch s {
	default:
		return fmt.Errorf("invalid buildmode: %q", s)
	case "exe":
		if objabi.GOOS == "darwin" && objabi.GOARCH == "arm64" {
			*mode = BuildModePIE // On darwin/arm64 everything is PIE.
			break
		}
		*mode = BuildModeExe
	case "pie":
		switch objabi.GOOS {
		case "aix", "android", "linux", "windows", "darwin", "ios":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePIE
	case "c-archive":
		switch objabi.GOOS {
		case "aix", "darwin", "ios", "linux":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		case "windows":
			switch objabi.GOARCH {
			case "amd64", "386", "arm":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeCArchive
	case "c-shared":
		switch objabi.GOARCH {
		case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
		default:
			return badmode()
		}
		*mode = BuildModeCShared
	case "shared":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeShared
	case "plugin":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
			default:
				return badmode()
			}
		case "darwin":
			switch objabi.GOARCH {
			case "amd64", "arm64":
			default:
				return badmode()
			}
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePlugin
	}
	return nil
}

func (mode *BuildMode) String() string {
	switch *mode {
	case BuildModeUnset:
		return "" // avoid showing a default in usage message
	case BuildModeExe:
		return "exe"
	case BuildModePIE:
		return "pie"
	case BuildModeCArchive:
		return "c-archive"
	case BuildModeCShared:
		return "c-shared"
	case BuildModeShared:
		return "shared"
	case BuildModePlugin:
		return "plugin"
	}
	return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
}

// LinkMode indicates whether an external linker is used for the final link.
type LinkMode uint8

const (
	LinkAuto LinkMode = iota
	LinkInternal
	LinkExternal
)

func (mode *LinkMode) Set(s string) error {
	switch s {
	default:
		return fmt.Errorf("invalid linkmode: %q", s)
	case "auto":
		*mode = LinkAuto
	case "internal":
		*mode = LinkInternal
	case "external":
		*mode = LinkExternal
	}
	return nil
}

func (mode *LinkMode) String() string {
	switch *mode {
	case LinkAuto:
		return "auto"
	case LinkInternal:
		return "internal"
	case LinkExternal:
		return "external"
	}
	return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
}

// mustLinkExternal reports whether the program being linked requires
// the external linker be used to complete the link.
func mustLinkExternal(ctxt *Link) (res bool, reason string) {
	if ctxt.Debugvlog > 1 {
		defer func() {
			if res {
				log.Printf("external linking is forced by: %s\n", reason)
			}
		}()
	}

	if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
		return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
	}

	if *flagMsan {
		return true, "msan"
	}

	// Internally linking cgo is incomplete on some architectures.
	// https://golang.org/issue/14449
	// https://golang.org/issue/21961
	if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
		return true, objabi.GOARCH + " does not support internal cgo"
	}
	if iscgo && objabi.GOOS == "android" {
		return true, objabi.GOOS + " does not support internal cgo"
	}

	// When the race flag is set, the LLVM tsan relocatable file is linked
	// into the final binary, which means external linking is required because
	// internal linking does not support it.
	if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
		return true, "race on " + objabi.GOARCH
	}

	// Some build modes require work the internal linker cannot do (yet).
	switch ctxt.BuildMode {
	case BuildModeCArchive:
		return true, "buildmode=c-archive"
	case BuildModeCShared:
		return true, "buildmode=c-shared"
	case BuildModePIE:
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "linux/amd64", "linux/arm64", "android/arm64":
		case "windows/386", "windows/amd64", "windows/arm":
		case "darwin/amd64", "darwin/arm64":
		default:
			// Internal linking does not support TLS_IE.
			return true, "buildmode=pie"
		}
	case BuildModePlugin:
		return true, "buildmode=plugin"
	case BuildModeShared:
		return true, "buildmode=shared"
	}
	if ctxt.linkShared {
		return true, "dynamically linking with a shared library"
	}

	return false, ""
}

// determineLinkMode sets ctxt.LinkMode.
//
// It is called after flags are processed and inputs are processed,
// so the ctxt.LinkMode variable has an initial value from the -linkmode
// flag and the iscgo externalobj variables are set.
func determineLinkMode(ctxt *Link) {
	extNeeded, extReason := mustLinkExternal(ctxt)
	via := ""

	if ctxt.LinkMode == LinkAuto {
		// The environment variable GO_EXTLINK_ENABLED controls the
		// default value of -linkmode. If it is not set when the
		// linker is called we take the value it was set to when
		// cmd/link was compiled. (See make.bash.)
		switch objabi.Getgoextlinkenabled() {
		case "0":
			ctxt.LinkMode = LinkInternal
			via = "via GO_EXTLINK_ENABLED "
		case "1":
			ctxt.LinkMode = LinkExternal
			via = "via GO_EXTLINK_ENABLED "
		default:
			if extNeeded || (iscgo && externalobj) {
				ctxt.LinkMode = LinkExternal
			} else {
				ctxt.LinkMode = LinkInternal
			}
		}
	}

	switch ctxt.LinkMode {
	case LinkInternal:
		if extNeeded {
			Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
		}
	case LinkExternal:
		switch {
		case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
			Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
		}
	}
}
<commit_msg>cmd/link: mark windows/arm as all PIE<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ld

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"fmt"
	"log"
)

// A BuildMode indicates the sort of object we are building.
//
// Possible build modes are the same as those for the -buildmode flag
// in cmd/go, and are documented in 'go help buildmode'.
type BuildMode uint8

const (
	BuildModeUnset BuildMode = iota
	BuildModeExe
	BuildModePIE
	BuildModeCArchive
	BuildModeCShared
	BuildModeShared
	BuildModePlugin
)

func (mode *BuildMode) Set(s string) error {
	badmode := func() error {
		return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH)
	}
	switch s {
	default:
		return fmt.Errorf("invalid buildmode: %q", s)
	case "exe":
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "darwin/arm64", "windows/arm": // On these platforms, everything is PIE
			*mode = BuildModePIE
		default:
			*mode = BuildModeExe
		}
	case "pie":
		switch objabi.GOOS {
		case "aix", "android", "linux", "windows", "darwin", "ios":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePIE
	case "c-archive":
		switch objabi.GOOS {
		case "aix", "darwin", "ios", "linux":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		case "windows":
			switch objabi.GOARCH {
			case "amd64", "386", "arm":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeCArchive
	case "c-shared":
		switch objabi.GOARCH {
		case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
		default:
			return badmode()
		}
		*mode = BuildModeCShared
	case "shared":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeShared
	case "plugin":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
			default:
				return badmode()
			}
		case "darwin":
			switch objabi.GOARCH {
			case "amd64", "arm64":
			default:
				return badmode()
			}
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePlugin
	}
	return nil
}

func (mode *BuildMode) String() string {
	switch *mode {
	case BuildModeUnset:
		return "" // avoid showing a default in usage message
	case BuildModeExe:
		return "exe"
	case BuildModePIE:
		return "pie"
	case BuildModeCArchive:
		return "c-archive"
	case BuildModeCShared:
		return "c-shared"
	case BuildModeShared:
		return "shared"
	case BuildModePlugin:
		return "plugin"
	}
	return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
}

// LinkMode indicates whether an external linker is used for the final link.
type LinkMode uint8

const (
	LinkAuto LinkMode = iota
	LinkInternal
	LinkExternal
)

func (mode *LinkMode) Set(s string) error {
	switch s {
	default:
		return fmt.Errorf("invalid linkmode: %q", s)
	case "auto":
		*mode = LinkAuto
	case "internal":
		*mode = LinkInternal
	case "external":
		*mode = LinkExternal
	}
	return nil
}

func (mode *LinkMode) String() string {
	switch *mode {
	case LinkAuto:
		return "auto"
	case LinkInternal:
		return "internal"
	case LinkExternal:
		return "external"
	}
	return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
}

// mustLinkExternal reports whether the program being linked requires
// the external linker be used to complete the link.
func mustLinkExternal(ctxt *Link) (res bool, reason string) {
	if ctxt.Debugvlog > 1 {
		defer func() {
			if res {
				log.Printf("external linking is forced by: %s\n", reason)
			}
		}()
	}

	if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
		return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
	}

	if *flagMsan {
		return true, "msan"
	}

	// Internally linking cgo is incomplete on some architectures.
	// https://golang.org/issue/14449
	// https://golang.org/issue/21961
	if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
		return true, objabi.GOARCH + " does not support internal cgo"
	}
	if iscgo && objabi.GOOS == "android" {
		return true, objabi.GOOS + " does not support internal cgo"
	}

	// When the race flag is set, the LLVM tsan relocatable file is linked
	// into the final binary, which means external linking is required because
	// internal linking does not support it.
	if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
		return true, "race on " + objabi.GOARCH
	}

	// Some build modes require work the internal linker cannot do (yet).
	switch ctxt.BuildMode {
	case BuildModeCArchive:
		return true, "buildmode=c-archive"
	case BuildModeCShared:
		return true, "buildmode=c-shared"
	case BuildModePIE:
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "linux/amd64", "linux/arm64", "android/arm64":
		case "windows/386", "windows/amd64", "windows/arm":
		case "darwin/amd64", "darwin/arm64":
		default:
			// Internal linking does not support TLS_IE.
			return true, "buildmode=pie"
		}
	case BuildModePlugin:
		return true, "buildmode=plugin"
	case BuildModeShared:
		return true, "buildmode=shared"
	}
	if ctxt.linkShared {
		return true, "dynamically linking with a shared library"
	}

	return false, ""
}

// determineLinkMode sets ctxt.LinkMode.
//
// It is called after flags are processed and inputs are processed,
// so the ctxt.LinkMode variable has an initial value from the -linkmode
// flag and the iscgo externalobj variables are set.
func determineLinkMode(ctxt *Link) {
	extNeeded, extReason := mustLinkExternal(ctxt)
	via := ""

	if ctxt.LinkMode == LinkAuto {
		// The environment variable GO_EXTLINK_ENABLED controls the
		// default value of -linkmode. If it is not set when the
		// linker is called we take the value it was set to when
		// cmd/link was compiled. (See make.bash.)
		switch objabi.Getgoextlinkenabled() {
		case "0":
			ctxt.LinkMode = LinkInternal
			via = "via GO_EXTLINK_ENABLED "
		case "1":
			ctxt.LinkMode = LinkExternal
			via = "via GO_EXTLINK_ENABLED "
		default:
			if extNeeded || (iscgo && externalobj) {
				ctxt.LinkMode = LinkExternal
			} else {
				ctxt.LinkMode = LinkInternal
			}
		}
	}

	switch ctxt.LinkMode {
	case LinkInternal:
		if extNeeded {
			Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
		}
	case LinkExternal:
		switch {
		case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
			Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
		}
	}
}
<|endoftext|>


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

// Package json implements encoding and decoding of JSON objects as defined in
// RFC 4627.
package json

import (
	"bytes"
	"encoding/base64"
	"os"
	"reflect"
	"runtime"
	"sort"
	"strconv"
	"unicode"
	"utf8"
)

// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface,
// Marshal calls its MarshalJSON method to produce JSON.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as JSON booleans.
//
// Floating point and integer values encode as JSON numbers.
//
// String values encode as JSON strings, with each invalid UTF-8 sequence
// replaced by the encoding of the Unicode replacement character U+FFFD.
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string.
//
// Struct values encode as JSON objects.  Each exported struct field
// becomes a member of the object.  By default the object's key string
// is the struct field name.  If the struct field's tag has a "json" key with a
// value that is a non-empty string consisting of only Unicode letters,
// digits, and underscores, that value will be used as the object key.
// For example, the field tag `json:"myName"` says to use "myName"
// as the object key.
//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
// as map keys.
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null JSON object.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON object.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an InvalidTypeError.
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them.  Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(v interface{}) ([]byte, os.Error) {
	e := &encodeState{}
	err := e.marshal(v)
	if err != nil {
		return nil, err
	}
	return e.Bytes(), nil
}

// MarshalIndent is like Marshal but applies Indent to format the output.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) {
	b, err := Marshal(v)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	err = Indent(&buf, b, prefix, indent)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// MarshalForHTML is like Marshal but applies HTMLEscape to the output.
func MarshalForHTML(v interface{}) ([]byte, os.Error) {
	b, err := Marshal(v)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	HTMLEscape(&buf, b)
	return buf.Bytes(), nil
}

// HTMLEscape appends to dst the JSON-encoded src with <, >, and &
// characters inside string literals changed to \u003c, \u003e, \u0026
// so that the JSON will be safe to embed inside HTML <script> tags.
// For historical reasons, web browsers don't honor standard HTML
// escaping within <script> tags, so an alternative JSON encoding must
// be used.
func HTMLEscape(dst *bytes.Buffer, src []byte) {
	// < > & can only appear in string literals,
	// so just scan the string one byte at a time.
	start := 0
	for i, c := range src {
		if c == '<' || c == '>' || c == '&' {
			if start < i {
				dst.Write(src[start:i])
			}
			dst.WriteString(`\u00`)
			dst.WriteByte(hex[c>>4])
			dst.WriteByte(hex[c&0xF])
			start = i + 1
		}
	}
	if start < len(src) {
		dst.Write(src[start:])
	}
}

// Marshaler is the interface implemented by objects that
// can marshal themselves into valid JSON.
type Marshaler interface {
	MarshalJSON() ([]byte, os.Error)
}

type UnsupportedTypeError struct {
	Type reflect.Type
}

func (e *UnsupportedTypeError) String() string {
	return "json: unsupported type: " + e.Type.String()
}

type InvalidUTF8Error struct {
	S string
}

func (e *InvalidUTF8Error) String() string {
	return "json: invalid UTF-8 in string: " + strconv.Quote(e.S)
}

type MarshalerError struct {
	Type  reflect.Type
	Error os.Error
}

func (e *MarshalerError) String() string {
	return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String()
}

type interfaceOrPtrValue interface {
	IsNil() bool
	Elem() reflect.Value
}

var hex = "0123456789abcdef"

// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
	bytes.Buffer // accumulated output
}

func (e *encodeState) marshal(v interface{}) (err os.Error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(os.Error)
		}
	}()
	e.reflectValue(reflect.ValueOf(v))
	return nil
}

func (e *encodeState) error(err os.Error) {
	panic(err)
}

var byteSliceType = reflect.TypeOf([]byte(nil))

func (e *encodeState) reflectValue(v reflect.Value) {
	if !v.IsValid() {
		e.WriteString("null")
		return
	}

	if j, ok := v.Interface().(Marshaler); ok {
		b, err := j.MarshalJSON()
		if err == nil {
			// copy JSON into buffer, checking validity.
			err = Compact(&e.Buffer, b)
		}
		if err != nil {
			e.error(&MarshalerError{v.Type(), err})
		}
		return
	}

	switch v.Kind() {
	case reflect.Bool:
		x := v.Bool()
		if x {
			e.WriteString("true")
		} else {
			e.WriteString("false")
		}

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		e.WriteString(strconv.Itoa64(v.Int()))

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		e.WriteString(strconv.Uitoa64(v.Uint()))

	case reflect.Float32, reflect.Float64:
		e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))

	case reflect.String:
		e.string(v.String())

	case reflect.Struct:
		e.WriteByte('{')
		t := v.Type()
		n := v.NumField()
		first := true
		for i := 0; i < n; i++ {
			f := t.Field(i)
			if f.PkgPath != "" {
				continue
			}
			if first {
				first = false
			} else {
				e.WriteByte(',')
			}
			if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) {
				e.string(tag)
			} else {
				e.string(f.Name)
			}
			e.WriteByte(':')
			e.reflectValue(v.Field(i))
		}
		e.WriteByte('}')

	case reflect.Map:
		if v.Type().Key().Kind() != reflect.String {
			e.error(&UnsupportedTypeError{v.Type()})
		}
		if v.IsNil() {
			e.WriteString("null")
			break
		}
		e.WriteByte('{')
		var sv stringValues = v.MapKeys()
		sort.Sort(sv)
		for i, k := range sv {
			if i > 0 {
				e.WriteByte(',')
			}
			e.string(k.String())
			e.WriteByte(':')
			e.reflectValue(v.MapIndex(k))
		}
		e.WriteByte('}')

	case reflect.Array, reflect.Slice:
		if v.Type() == byteSliceType {
			e.WriteByte('"')
			s := v.Interface().([]byte)
			if len(s) < 1024 {
				// for small buffers, using Encode directly is much faster.
				dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
				base64.StdEncoding.Encode(dst, s)
				e.Write(dst)
			} else {
				// for large buffers, avoid unnecessary extra temporary
				// buffer space.
				enc := base64.NewEncoder(base64.StdEncoding, e)
				enc.Write(s)
				enc.Close()
			}
			e.WriteByte('"')
			break
		}
		e.WriteByte('[')
		n := v.Len()
		for i := 0; i < n; i++ {
			if i > 0 {
				e.WriteByte(',')
			}
			e.reflectValue(v.Index(i))
		}
		e.WriteByte(']')

	case reflect.Interface, reflect.Ptr:
		if v.IsNil() {
			e.WriteString("null")
			return
		}
		e.reflectValue(v.Elem())

	default:
		e.error(&UnsupportedTypeError{v.Type()})
	}
	return
}

func isValidTag(s string) bool {
	if s == "" {
		return false
	}
	for _, c := range s {
		if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
			return false
		}
	}
	return true
}

// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value

func (sv stringValues) Len() int           { return len(sv) }
func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
func (sv stringValues) get(i int) string   { return sv[i].String() }

func (e *encodeState) string(s string) {
	e.WriteByte('"')
	start := 0
	for i := 0; i < len(s); {
		if b := s[i]; b < utf8.RuneSelf {
			if 0x20 <= b && b != '\\' && b != '"' {
				i++
				continue
			}
			if start < i {
				e.WriteString(s[start:i])
			}
			if b == '\\' || b == '"' {
				e.WriteByte('\\')
				e.WriteByte(b)
			} else {
				e.WriteString(`\u00`)
				e.WriteByte(hex[b>>4])
				e.WriteByte(hex[b&0xF])
			}
			i++
			start = i
			continue
		}
		c, size := utf8.DecodeRuneInString(s[i:])
		if c == utf8.RuneError && size == 1 {
			e.error(&InvalidUTF8Error{s})
		}
		i += size
	}
	if start < len(s) {
		e.WriteString(s[start:])
	}
	e.WriteByte('"')
}
<commit_msg>json: encode \r and \n in strings as e.g. "\n", not "\u000A"<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package json implements encoding and decoding of JSON objects as defined in
// RFC 4627.
package json

import (
	"bytes"
	"encoding/base64"
	"os"
	"reflect"
	"runtime"
	"sort"
	"strconv"
	"unicode"
	"utf8"
)

// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface,
// Marshal calls its MarshalJSON method to produce JSON.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as JSON booleans.
//
// Floating point and integer values encode as JSON numbers.
//
// String values encode as JSON strings, with each invalid UTF-8 sequence
// replaced by the encoding of the Unicode replacement character U+FFFD.
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string.
//
// Struct values encode as JSON objects.  Each exported struct field
// becomes a member of the object.  By default the object's key string
// is the struct field name.  If the struct field's tag has a "json" key with a
// value that is a non-empty string consisting of only Unicode letters,
// digits, and underscores, that value will be used as the object key.
// For example, the field tag `json:"myName"` says to use "myName"
// as the object key.
//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
// as map keys.
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null JSON object.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON object.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an InvalidTypeError.
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them.  Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(v interface{}) ([]byte, os.Error) {
	e := &encodeState{}
	err := e.marshal(v)
	if err != nil {
		return nil, err
	}
	return e.Bytes(), nil
}

// MarshalIndent is like Marshal but applies Indent to format the output.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) {
	b, err := Marshal(v)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	err = Indent(&buf, b, prefix, indent)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// MarshalForHTML is like Marshal but applies HTMLEscape to the output.
func MarshalForHTML(v interface{}) ([]byte, os.Error) {
	b, err := Marshal(v)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	HTMLEscape(&buf, b)
	return buf.Bytes(), nil
}

// HTMLEscape appends to dst the JSON-encoded src with <, >, and &
// characters inside string literals changed to \u003c, \u003e, \u0026
// so that the JSON will be safe to embed inside HTML <script> tags.
// For historical reasons, web browsers don't honor standard HTML
// escaping within <script> tags, so an alternative JSON encoding must
// be used.
func HTMLEscape(dst *bytes.Buffer, src []byte) {
	// < > & can only appear in string literals,
	// so just scan the string one byte at a time.
	start := 0
	for i, c := range src {
		if c == '<' || c == '>' || c == '&' {
			if start < i {
				dst.Write(src[start:i])
			}
			dst.WriteString(`\u00`)
			dst.WriteByte(hex[c>>4])
			dst.WriteByte(hex[c&0xF])
			start = i + 1
		}
	}
	if start < len(src) {
		dst.Write(src[start:])
	}
}

// Marshaler is the interface implemented by objects that
// can marshal themselves into valid JSON.
type Marshaler interface {
	MarshalJSON() ([]byte, os.Error)
}

type UnsupportedTypeError struct {
	Type reflect.Type
}

func (e *UnsupportedTypeError) String() string {
	return "json: unsupported type: " + e.Type.String()
}

type InvalidUTF8Error struct {
	S string
}

func (e *InvalidUTF8Error) String() string {
	return "json: invalid UTF-8 in string: " + strconv.Quote(e.S)
}

type MarshalerError struct {
	Type  reflect.Type
	Error os.Error
}

func (e *MarshalerError) String() string {
	return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String()
}

type interfaceOrPtrValue interface {
	IsNil() bool
	Elem() reflect.Value
}

var hex = "0123456789abcdef"

// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
	bytes.Buffer // accumulated output
}

func (e *encodeState) marshal(v interface{}) (err os.Error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(os.Error)
		}
	}()
	e.reflectValue(reflect.ValueOf(v))
	return nil
}

func (e *encodeState) error(err os.Error) {
	panic(err)
}

var byteSliceType = reflect.TypeOf([]byte(nil))

func (e *encodeState) reflectValue(v reflect.Value) {
	if !v.IsValid() {
		e.WriteString("null")
		return
	}

	if j, ok := v.Interface().(Marshaler); ok {
		b, err := j.MarshalJSON()
		if err == nil {
			// copy JSON into buffer, checking validity.
			err = Compact(&e.Buffer, b)
		}
		if err != nil {
			e.error(&MarshalerError{v.Type(), err})
		}
		return
	}

	switch v.Kind() {
	case reflect.Bool:
		x := v.Bool()
		if x {
			e.WriteString("true")
		} else {
			e.WriteString("false")
		}

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		e.WriteString(strconv.Itoa64(v.Int()))

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		e.WriteString(strconv.Uitoa64(v.Uint()))

	case reflect.Float32, reflect.Float64:
		e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))

	case reflect.String:
		e.string(v.String())

	case reflect.Struct:
		e.WriteByte('{')
		t := v.Type()
		n := v.NumField()
		first := true
		for i := 0; i < n; i++ {
			f := t.Field(i)
			if f.PkgPath != "" {
				continue
			}
			if first {
				first = false
			} else {
				e.WriteByte(',')
			}
			if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) {
				e.string(tag)
			} else {
				e.string(f.Name)
			}
			e.WriteByte(':')
			e.reflectValue(v.Field(i))
		}
		e.WriteByte('}')

	case reflect.Map:
		if v.Type().Key().Kind() != reflect.String {
			e.error(&UnsupportedTypeError{v.Type()})
		}
		if v.IsNil() {
			e.WriteString("null")
			break
		}
		e.WriteByte('{')
		var sv stringValues = v.MapKeys()
		sort.Sort(sv)
		for i, k := range sv {
			if i > 0 {
				e.WriteByte(',')
			}
			e.string(k.String())
			e.WriteByte(':')
			e.reflectValue(v.MapIndex(k))
		}
		e.WriteByte('}')

	case reflect.Array, reflect.Slice:
		if v.Type() == byteSliceType {
			e.WriteByte('"')
			s := v.Interface().([]byte)
			if len(s) < 1024 {
				// for small buffers, using Encode directly is much faster.
				dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
				base64.StdEncoding.Encode(dst, s)
				e.Write(dst)
			} else {
				// for large buffers, avoid unnecessary extra temporary
				// buffer space.
				enc := base64.NewEncoder(base64.StdEncoding, e)
				enc.Write(s)
				enc.Close()
			}
			e.WriteByte('"')
			break
		}
		e.WriteByte('[')
		n := v.Len()
		for i := 0; i < n; i++ {
			if i > 0 {
				e.WriteByte(',')
			}
			e.reflectValue(v.Index(i))
		}
		e.WriteByte(']')

	case reflect.Interface, reflect.Ptr:
		if v.IsNil() {
			e.WriteString("null")
			return
		}
		e.reflectValue(v.Elem())

	default:
		e.error(&UnsupportedTypeError{v.Type()})
	}
	return
}

func isValidTag(s string) bool {
	if s == "" {
		return false
	}
	for _, c := range s {
		if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
			return false
		}
	}
	return true
}

// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value

func (sv stringValues) Len() int           { return len(sv) }
func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
func (sv stringValues) get(i int) string   { return sv[i].String() }

func (e *encodeState) string(s string) {
	e.WriteByte('"')
	start := 0
	for i := 0; i < len(s); {
		if b := s[i]; b < utf8.RuneSelf {
			if 0x20 <= b && b != '\\' && b != '"' {
				i++
				continue
			}
			if start < i {
				e.WriteString(s[start:i])
			}
			switch b {
			case '\\', '"':
				e.WriteByte('\\')
				e.WriteByte(b)
			case '\n':
				e.WriteByte('\\')
				e.WriteByte('n')
			case '\r':
				e.WriteByte('\\')
				e.WriteByte('r')
			default:
				e.WriteString(`\u00`)
				e.WriteByte(hex[b>>4])
				e.WriteByte(hex[b&0xF])
			}
			i++
			start = i
			continue
		}
		c, size := utf8.DecodeRuneInString(s[i:])
		if c == utf8.RuneError && size == 1 {
			e.error(&InvalidUTF8Error{s})
		}
		i += size
	}
	if start < len(s) {
		e.WriteString(s[start:])
	}
	e.WriteByte('"')
}
<|endoftext|>


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

// Marshalling and unmarshalling of
// JSON data into Go structs using reflection.

package json

import (
	"fmt"
	"io"
	"os"
	"reflect"
	"strings"
)

type structBuilder struct {
	val reflect.Value

	// if map_ != nil, write val to map_[key] on each change
	map_ *reflect.MapValue
	key  reflect.Value
}

var nobuilder *structBuilder

func isfloat(v reflect.Value) bool {
	switch v.(type) {
	case *reflect.FloatValue, *reflect.Float32Value, *reflect.Float64Value:
		return true
	}
	return false
}

func setfloat(v reflect.Value, f float64) {
	switch v := v.(type) {
	case *reflect.FloatValue:
		v.Set(float(f))
	case *reflect.Float32Value:
		v.Set(float32(f))
	case *reflect.Float64Value:
		v.Set(float64(f))
	}
}

func setint(v reflect.Value, i int64) {
	switch v := v.(type) {
	case *reflect.IntValue:
		v.Set(int(i))
	case *reflect.Int8Value:
		v.Set(int8(i))
	case *reflect.Int16Value:
		v.Set(int16(i))
	case *reflect.Int32Value:
		v.Set(int32(i))
	case *reflect.Int64Value:
		v.Set(int64(i))
	case *reflect.UintValue:
		v.Set(uint(i))
	case *reflect.Uint8Value:
		v.Set(uint8(i))
	case *reflect.Uint16Value:
		v.Set(uint16(i))
	case *reflect.Uint32Value:
		v.Set(uint32(i))
	case *reflect.Uint64Value:
		v.Set(uint64(i))
	}
}

// If updating b.val is not enough to update the original,
// copy a changed b.val out to the original.
func (b *structBuilder) Flush() {
	if b == nil {
		return
	}
	if b.map_ != nil {
		b.map_.SetElem(b.key, b.val)
	}
}

func (b *structBuilder) Int64(i int64) {
	if b == nil {
		return
	}
	v := b.val
	if isfloat(v) {
		setfloat(v, float64(i))
	} else {
		setint(v, i)
	}
}

func (b *structBuilder) Uint64(i uint64) {
	if b == nil {
		return
	}
	v := b.val
	if isfloat(v) {
		setfloat(v, float64(i))
	} else {
		setint(v, int64(i))
	}
}

func (b *structBuilder) Float64(f float64) {
	if b == nil {
		return
	}
	v := b.val
	if isfloat(v) {
		setfloat(v, f)
	} else {
		setint(v, int64(f))
	}
}

func (b *structBuilder) Null() {}

func (b *structBuilder) String(s string) {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.StringValue); ok {
		v.Set(s)
	}
}

func (b *structBuilder) Bool(tf bool) {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.BoolValue); ok {
		v.Set(tf)
	}
}

func (b *structBuilder) Array() {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.SliceValue); ok {
		if v.IsNil() {
			v.Set(reflect.MakeSlice(v.Type().(*reflect.SliceType), 0, 8))
		}
	}
}

func (b *structBuilder) Elem(i int) Builder {
	if b == nil || i < 0 {
		return nobuilder
	}
	switch v := b.val.(type) {
	case *reflect.ArrayValue:
		if i < v.Len() {
			return &structBuilder{val: v.Elem(i)}
		}
	case *reflect.SliceValue:
		if i >= v.Cap() {
			n := v.Cap()
			if n < 8 {
				n = 8
			}
			for n <= i {
				n *= 2
			}
			nv := reflect.MakeSlice(v.Type().(*reflect.SliceType), v.Len(), n)
			reflect.ArrayCopy(nv, v)
			v.Set(nv)
		}
		if v.Len() <= i && i < v.Cap() {
			v.SetLen(i + 1)
		}
		if i < v.Len() {
			return &structBuilder{val: v.Elem(i)}
		}
	}
	return nobuilder
}

func (b *structBuilder) Map() {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.PtrValue); ok && v.IsNil() {
		if v.IsNil() {
			v.PointTo(reflect.MakeZero(v.Type().(*reflect.PtrType).Elem()))
			b.Flush()
		}
		b.map_ = nil
		b.val = v.Elem()
	}
	if v, ok := b.val.(*reflect.MapValue); ok && v.IsNil() {
		v.Set(reflect.MakeMap(v.Type().(*reflect.MapType)))
	}
}

func (b *structBuilder) Key(k string) Builder {
	if b == nil {
		return nobuilder
	}
	switch v := reflect.Indirect(b.val).(type) {
	case *reflect.StructValue:
		t := v.Type().(*reflect.StructType)
		// Case-insensitive field lookup.
		k = strings.ToLower(k)
		for i := 0; i < t.NumField(); i++ {
			if strings.ToLower(t.Field(i).Name) == k {
				return &structBuilder{val: v.Field(i)}
			}
		}
	case *reflect.MapValue:
		t := v.Type().(*reflect.MapType)
		if t.Key() != reflect.Typeof(k) {
			break
		}
		key := reflect.NewValue(k)
		elem := v.Elem(key)
		if elem == nil {
			v.SetElem(key, reflect.MakeZero(t.Elem()))
			elem = v.Elem(key)
		}
		return &structBuilder{val: elem, map_: v, key: key}
	}
	return nobuilder
}

// Unmarshal parses the JSON syntax string s and fills in
// an arbitrary struct or slice pointed at by val.
// It uses the reflect package to assign to fields
// and arrays embedded in val.  Well-formed data that does not fit
// into the struct is discarded.
//
// For example, given these definitions:
//
//	type Email struct {
//		Where string;
//		Addr string;
//	}
//
//	type Result struct {
//		Name string;
//		Phone string;
//		Email []Email
//	}
//
//	var r = Result{ "name", "phone", nil }
//
// unmarshalling the JSON syntax string
//
//	{
//	  "email": [
//	    {
//	      "where": "home",
//	      "addr": "gre@example.com"
//	    },
//	    {
//	      "where": "work",
//	      "addr": "gre@work.com"
//	    }
//	  ],
//	  "name": "Grace R. Emlin",
//	  "address": "123 Main Street"
//	}
//
// via Unmarshal(s, &r) is equivalent to assigning
//
//	r = Result{
//		"Grace R. Emlin",	// name
//		"phone",		// no phone given
//		[]Email{
//			Email{ "home", "gre@example.com" },
//			Email{ "work", "gre@work.com" }
//		}
//	}
//
// Note that the field r.Phone has not been modified and
// that the JSON field "address" was discarded.
//
// Because Unmarshal uses the reflect package, it can only
// assign to upper case fields.  Unmarshal uses a case-insensitive
// comparison to match JSON field names to struct field names.
//
// To unmarshal a top-level JSON array, pass in a pointer to an empty
// slice of the correct type.
//
// On success, Unmarshal returns with ok set to true.
// On a syntax error, it returns with ok set to false and errtok
// set to the offending token.
func Unmarshal(s string, val interface{}) (ok bool, errtok string) {
	v := reflect.NewValue(val)
	var b *structBuilder

	// If val is a pointer to a slice, we append to the slice.
	if ptr, ok := v.(*reflect.PtrValue); ok {
		if slice, ok := ptr.Elem().(*reflect.SliceValue); ok {
			b = &structBuilder{val: slice}
		}
	}

	if b == nil {
		b = &structBuilder{val: v}
	}

	ok, _, errtok = Parse(s, b)
	if !ok {
		return false, errtok
	}
	return true, ""
}

type MarshalError struct {
	T reflect.Type
}

func (e *MarshalError) String() string {
	return "json cannot encode value of type " + e.T.String()
}
func writeArrayOrSlice(w io.Writer, val reflect.ArrayOrSliceValue) os.Error {
	fmt.Fprint(w, "[")

	for i := 0; i < val.Len(); i++ {
		if err := writeValue(w, val.Elem(i)); err != nil {
			return err
		}

		if i < val.Len()-1 {
			fmt.Fprint(w, ",")
		}
	}

	fmt.Fprint(w, "]")
	return nil
}

func writeMap(w io.Writer, val *reflect.MapValue) os.Error {
	key := val.Type().(*reflect.MapType).Key()
	if _, ok := key.(*reflect.StringType); !ok {
		return &MarshalError{val.Type()}
	}

	keys := val.Keys()
	fmt.Fprint(w, "{")
	for i := 0; i < len(keys); i++ {
		fmt.Fprintf(w, "%q:", keys[i].(*reflect.StringValue).Get())

		if err := writeValue(w, val.Elem(keys[i])); err != nil {
			return err
		}

		if i < len(keys)-1 {
			fmt.Fprint(w, ",")
		}
	}

	fmt.Fprint(w, "}")
	return nil
}

func writeStruct(w io.Writer, val *reflect.StructValue) os.Error {
	fmt.Fprint(w, "{")

	typ := val.Type().(*reflect.StructType)

	for i := 0; i < val.NumField(); i++ {
		fieldValue := val.Field(i)
		fmt.Fprintf(w, "%q:", typ.Field(i).Name)
		writeValue(w, fieldValue)
		if i < val.NumField()-1 {
			fmt.Fprint(w, ",")
		}
	}

	fmt.Fprint(w, "}")
	return nil
}

func writeValue(w io.Writer, val reflect.Value) (err os.Error) {
	if val == nil {
		fmt.Fprint(w, "null")
		return
	}

	switch v := val.(type) {
	case *reflect.StringValue:
		fmt.Fprintf(w, "%q", v.Get())
	case *reflect.ArrayValue:
		err = writeArrayOrSlice(w, v)
	case *reflect.SliceValue:
		err = writeArrayOrSlice(w, v)
	case *reflect.MapValue:
		err = writeMap(w, v)
	case *reflect.StructValue:
		err = writeStruct(w, v)
	case *reflect.ChanValue,
		*reflect.PtrValue,
		*reflect.UnsafePointerValue:
		err = &MarshalError{val.Type()}
	case *reflect.InterfaceValue:
		if v.IsNil() {
			fmt.Fprint(w, "null")
		} else {
			err = &MarshalError{val.Type()}
		}
	default:
		value := val.(reflect.Value)
		fmt.Fprint(w, value.Interface())
	}
	return
}

func Marshal(w io.Writer, val interface{}) os.Error {
	return writeValue(w, reflect.NewValue(val))
}
<commit_msg>Check for errors when writing fields of a struct.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Marshalling and unmarshalling of
// JSON data into Go structs using reflection.

package json

import (
	"fmt"
	"io"
	"os"
	"reflect"
	"strings"
)

type structBuilder struct {
	val reflect.Value

	// if map_ != nil, write val to map_[key] on each change
	map_ *reflect.MapValue
	key  reflect.Value
}

var nobuilder *structBuilder

func isfloat(v reflect.Value) bool {
	switch v.(type) {
	case *reflect.FloatValue, *reflect.Float32Value, *reflect.Float64Value:
		return true
	}
	return false
}

func setfloat(v reflect.Value, f float64) {
	switch v := v.(type) {
	case *reflect.FloatValue:
		v.Set(float(f))
	case *reflect.Float32Value:
		v.Set(float32(f))
	case *reflect.Float64Value:
		v.Set(float64(f))
	}
}

func setint(v reflect.Value, i int64) {
	switch v := v.(type) {
	case *reflect.IntValue:
		v.Set(int(i))
	case *reflect.Int8Value:
		v.Set(int8(i))
	case *reflect.Int16Value:
		v.Set(int16(i))
	case *reflect.Int32Value:
		v.Set(int32(i))
	case *reflect.Int64Value:
		v.Set(int64(i))
	case *reflect.UintValue:
		v.Set(uint(i))
	case *reflect.Uint8Value:
		v.Set(uint8(i))
	case *reflect.Uint16Value:
		v.Set(uint16(i))
	case *reflect.Uint32Value:
		v.Set(uint32(i))
	case *reflect.Uint64Value:
		v.Set(uint64(i))
	}
}

// If updating b.val is not enough to update the original,
// copy a changed b.val out to the original.
func (b *structBuilder) Flush() {
	if b == nil {
		return
	}
	if b.map_ != nil {
		b.map_.SetElem(b.key, b.val)
	}
}

func (b *structBuilder) Int64(i int64) {
	if b == nil {
		return
	}
	v := b.val
	if isfloat(v) {
		setfloat(v, float64(i))
	} else {
		setint(v, i)
	}
}

func (b *structBuilder) Uint64(i uint64) {
	if b == nil {
		return
	}
	v := b.val
	if isfloat(v) {
		setfloat(v, float64(i))
	} else {
		setint(v, int64(i))
	}
}

func (b *structBuilder) Float64(f float64) {
	if b == nil {
		return
	}
	v := b.val
	if isfloat(v) {
		setfloat(v, f)
	} else {
		setint(v, int64(f))
	}
}

func (b *structBuilder) Null() {}

func (b *structBuilder) String(s string) {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.StringValue); ok {
		v.Set(s)
	}
}

func (b *structBuilder) Bool(tf bool) {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.BoolValue); ok {
		v.Set(tf)
	}
}

func (b *structBuilder) Array() {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.SliceValue); ok {
		if v.IsNil() {
			v.Set(reflect.MakeSlice(v.Type().(*reflect.SliceType), 0, 8))
		}
	}
}

func (b *structBuilder) Elem(i int) Builder {
	if b == nil || i < 0 {
		return nobuilder
	}
	switch v := b.val.(type) {
	case *reflect.ArrayValue:
		if i < v.Len() {
			return &structBuilder{val: v.Elem(i)}
		}
	case *reflect.SliceValue:
		if i >= v.Cap() {
			n := v.Cap()
			if n < 8 {
				n = 8
			}
			for n <= i {
				n *= 2
			}
			nv := reflect.MakeSlice(v.Type().(*reflect.SliceType), v.Len(), n)
			reflect.ArrayCopy(nv, v)
			v.Set(nv)
		}
		if v.Len() <= i && i < v.Cap() {
			v.SetLen(i + 1)
		}
		if i < v.Len() {
			return &structBuilder{val: v.Elem(i)}
		}
	}
	return nobuilder
}

func (b *structBuilder) Map() {
	if b == nil {
		return
	}
	if v, ok := b.val.(*reflect.PtrValue); ok && v.IsNil() {
		if v.IsNil() {
			v.PointTo(reflect.MakeZero(v.Type().(*reflect.PtrType).Elem()))
			b.Flush()
		}
		b.map_ = nil
		b.val = v.Elem()
	}
	if v, ok := b.val.(*reflect.MapValue); ok && v.IsNil() {
		v.Set(reflect.MakeMap(v.Type().(*reflect.MapType)))
	}
}

func (b *structBuilder) Key(k string) Builder {
	if b == nil {
		return nobuilder
	}
	switch v := reflect.Indirect(b.val).(type) {
	case *reflect.StructValue:
		t := v.Type().(*reflect.StructType)
		// Case-insensitive field lookup.
		k = strings.ToLower(k)
		for i := 0; i < t.NumField(); i++ {
			if strings.ToLower(t.Field(i).Name) == k {
				return &structBuilder{val: v.Field(i)}
			}
		}
	case *reflect.MapValue:
		t := v.Type().(*reflect.MapType)
		if t.Key() != reflect.Typeof(k) {
			break
		}
		key := reflect.NewValue(k)
		elem := v.Elem(key)
		if elem == nil {
			v.SetElem(key, reflect.MakeZero(t.Elem()))
			elem = v.Elem(key)
		}
		return &structBuilder{val: elem, map_: v, key: key}
	}
	return nobuilder
}

// Unmarshal parses the JSON syntax string s and fills in
// an arbitrary struct or slice pointed at by val.
// It uses the reflect package to assign to fields
// and arrays embedded in val.  Well-formed data that does not fit
// into the struct is discarded.
//
// For example, given these definitions:
//
//	type Email struct {
//		Where string;
//		Addr string;
//	}
//
//	type Result struct {
//		Name string;
//		Phone string;
//		Email []Email
//	}
//
//	var r = Result{ "name", "phone", nil }
//
// unmarshalling the JSON syntax string
//
//	{
//	  "email": [
//	    {
//	      "where": "home",
//	      "addr": "gre@example.com"
//	    },
//	    {
//	      "where": "work",
//	      "addr": "gre@work.com"
//	    }
//	  ],
//	  "name": "Grace R. Emlin",
//	  "address": "123 Main Street"
//	}
//
// via Unmarshal(s, &r) is equivalent to assigning
//
//	r = Result{
//		"Grace R. Emlin",	// name
//		"phone",		// no phone given
//		[]Email{
//			Email{ "home", "gre@example.com" },
//			Email{ "work", "gre@work.com" }
//		}
//	}
//
// Note that the field r.Phone has not been modified and
// that the JSON field "address" was discarded.
//
// Because Unmarshal uses the reflect package, it can only
// assign to upper case fields.  Unmarshal uses a case-insensitive
// comparison to match JSON field names to struct field names.
//
// To unmarshal a top-level JSON array, pass in a pointer to an empty
// slice of the correct type.
//
// On success, Unmarshal returns with ok set to true.
// On a syntax error, it returns with ok set to false and errtok
// set to the offending token.
func Unmarshal(s string, val interface{}) (ok bool, errtok string) {
	v := reflect.NewValue(val)
	var b *structBuilder

	// If val is a pointer to a slice, we append to the slice.
	if ptr, ok := v.(*reflect.PtrValue); ok {
		if slice, ok := ptr.Elem().(*reflect.SliceValue); ok {
			b = &structBuilder{val: slice}
		}
	}

	if b == nil {
		b = &structBuilder{val: v}
	}

	ok, _, errtok = Parse(s, b)
	if !ok {
		return false, errtok
	}
	return true, ""
}

type MarshalError struct {
	T reflect.Type
}

func (e *MarshalError) String() string {
	return "json cannot encode value of type " + e.T.String()
}
func writeArrayOrSlice(w io.Writer, val reflect.ArrayOrSliceValue) os.Error {
	fmt.Fprint(w, "[")

	for i := 0; i < val.Len(); i++ {
		if err := writeValue(w, val.Elem(i)); err != nil {
			return err
		}

		if i < val.Len()-1 {
			fmt.Fprint(w, ",")
		}
	}

	fmt.Fprint(w, "]")
	return nil
}

func writeMap(w io.Writer, val *reflect.MapValue) os.Error {
	key := val.Type().(*reflect.MapType).Key()
	if _, ok := key.(*reflect.StringType); !ok {
		return &MarshalError{val.Type()}
	}

	keys := val.Keys()
	fmt.Fprint(w, "{")
	for i := 0; i < len(keys); i++ {
		fmt.Fprintf(w, "%q:", keys[i].(*reflect.StringValue).Get())

		if err := writeValue(w, val.Elem(keys[i])); err != nil {
			return err
		}

		if i < len(keys)-1 {
			fmt.Fprint(w, ",")
		}
	}

	fmt.Fprint(w, "}")
	return nil
}

func writeStruct(w io.Writer, val *reflect.StructValue) os.Error {
	fmt.Fprint(w, "{")

	typ := val.Type().(*reflect.StructType)

	for i := 0; i < val.NumField(); i++ {
		fieldValue := val.Field(i)
		fmt.Fprintf(w, "%q:", typ.Field(i).Name)

		if err := writeValue(w, fieldValue); err != nil {
			return err
		}

		if i < val.NumField()-1 {
			fmt.Fprint(w, ",")
		}
	}

	fmt.Fprint(w, "}")
	return nil
}

func writeValue(w io.Writer, val reflect.Value) (err os.Error) {
	if val == nil {
		fmt.Fprint(w, "null")
		return
	}

	switch v := val.(type) {
	case *reflect.StringValue:
		fmt.Fprintf(w, "%q", v.Get())
	case *reflect.ArrayValue:
		err = writeArrayOrSlice(w, v)
	case *reflect.SliceValue:
		err = writeArrayOrSlice(w, v)
	case *reflect.MapValue:
		err = writeMap(w, v)
	case *reflect.StructValue:
		err = writeStruct(w, v)
	case *reflect.ChanValue,
		*reflect.PtrValue,
		*reflect.UnsafePointerValue:
		err = &MarshalError{val.Type()}
	case *reflect.InterfaceValue:
		if v.IsNil() {
			fmt.Fprint(w, "null")
		} else {
			err = &MarshalError{val.Type()}
		}
	default:
		value := val.(reflect.Value)
		fmt.Fprint(w, value.Interface())
	}
	return
}

func Marshal(w io.Writer, val interface{}) os.Error {
	return writeValue(w, reflect.NewValue(val))
}
<|endoftext|>


<commit_before>package bootstrapper

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"github.com/cloudfoundry-incubator/cf-mysql-bootstrap/clock"
	"github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config"
	"github.com/pivotal-golang/lager"
)

type Bootstrapper struct {
	rootConfig *config.Config
	clock      clock.Clock
}

func New(rootConfig *config.Config, clock clock.Clock) *Bootstrapper {
	return &Bootstrapper{
		rootConfig: rootConfig,
		clock:      clock,
	}
}

func (b *Bootstrapper) Run() error {
	logger := b.rootConfig.Logger

	for _, url := range b.rootConfig.HealthcheckURLs {
		stopMysqlUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.ShutDownMysql)
		resp, err := http.Get(stopMysqlUrl)
		if err != nil {
			return fmt.Errorf("Failed to stop mysql: %s", err.Error())
		}

		if resp.StatusCode != http.StatusOK {
			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}
			return fmt.Errorf("Non 200 response from stopping mysql at %s: %s", stopMysqlUrl, responseBody)
		}

		logger.Info(fmt.Sprintf("Successfully sent stop request to URL: %s", stopMysqlUrl))
	}

	pollingIntervalInSec := 3
	timeoutInSec := 30
	maxIterations := timeoutInSec / pollingIntervalInSec
	for _, url := range b.rootConfig.HealthcheckURLs {
		statusUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.MysqlStatus)
		stoppedSuccessfully := false

		for i := 0; i < maxIterations; i++ {
			resp, err := http.Get(statusUrl)
			if err != nil {
				return fmt.Errorf("Failed to get mysql status: %s", err.Error())
			}

			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}

			if resp.StatusCode != http.StatusOK {
				return fmt.Errorf("Received non-200 response when getting mysql status: %s", responseBody)
			}

			if responseBody == "stopped" {
				stoppedSuccessfully = true
				break
			}

			<-b.clock.After(time.Duration(pollingIntervalInSec) * time.Second)
		}

		if stoppedSuccessfully == false {
			return fmt.Errorf("Timed out waiting for mysql to stop after %d seconds", timeoutInSec)
		} else {
			logger.Info("Successfully stopped mysql process", lager.Data{"url": statusUrl})
		}
	}

	logger.Info("Successfully stopped mysql process on all vms")
	sequenceNumberMap := make(map[string]int)
	for _, url := range b.rootConfig.HealthcheckURLs {
		getSeqNumberUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.GetSeqNumber)
		resp, err := http.Get(getSeqNumberUrl)
		if err != nil {
			return fmt.Errorf("Failed to get sequence number from mysql: %s", err.Error())
		}

		responseBody := ""
		if resp.Body != nil {
			responseBytes, _ := ioutil.ReadAll(resp.Body)
			responseBody = string(responseBytes)
		}

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("Non 200 response for fetching sequence number from mysql at %s: %s", getSeqNumberUrl, responseBody)
		}

		logger.Info("resp body", lager.Data{
			"seqno response": responseBody})
		sequenceNumber, err := strconv.Atoi(responseBody)

		if err != nil {
			return fmt.Errorf("Failed to get valid sequence number from %s with %s", getSeqNumberUrl, err.Error())
		}

		sequenceNumberMap[url] = sequenceNumber

		logger.Info(fmt.Sprintf("Successfully sent request to URL to fetch sequence number: %s", getSeqNumberUrl))
	}

	bootstrapNode, joinNodes := largestSequenceNumber(sequenceNumberMap)
	bootstrapReqURL := fmt.Sprintf("%s/%s", bootstrapNode, b.rootConfig.StartMysqlInBootstrapMode)
	resp, err := http.Get(bootstrapReqURL)
	if err != nil {
		return fmt.Errorf("Failed to bootstrap mysql node: %s", err.Error())
	}

	if resp.StatusCode != http.StatusOK {
		responseBody := ""
		if resp.Body != nil {
			responseBytes, _ := ioutil.ReadAll(resp.Body)
			responseBody = string(responseBytes)
		}
		return fmt.Errorf("Non 200 response from bootstrapping mysql at %s: %s", bootstrapReqURL, responseBody)
	}

	logger.Info(fmt.Sprintf("Successfully sent bootstrap request to URL: %s", bootstrapReqURL))

	statusUrl := fmt.Sprintf("%s/%s", bootstrapNode, b.rootConfig.MysqlStatus)
	runningSuccessfully := false

	for i := 0; i < maxIterations; i++ {
		resp, err := http.Get(statusUrl)
		if err != nil {
			return fmt.Errorf("Failed to get mysql status: %s", err.Error())
		}

		responseBody := ""
		if resp.Body != nil {
			responseBytes, _ := ioutil.ReadAll(resp.Body)
			responseBody = string(responseBytes)
		}

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("Received non-200 response when getting mysql status: %s", responseBody)
		}

		if responseBody == "running" {
			runningSuccessfully = true
			break
		}

		<-b.clock.After(time.Duration(pollingIntervalInSec) * time.Second)
	}

	if runningSuccessfully == false {
		return fmt.Errorf("Timed out waiting for mysql to start after %d seconds", timeoutInSec)
	} else {
		logger.Info("Successfully bootstrapped mysql node", lager.Data{"url": statusUrl})
	}

	for _, joinNode := range joinNodes {
		joinReqURL := fmt.Sprintf("%s/%s", joinNode, b.rootConfig.StartMysqlInJoinMode)
		resp, err := http.Get(joinReqURL)
		if err != nil {
			return fmt.Errorf("Failed to join mysql node: %s", err.Error())
		}

		if resp.StatusCode != http.StatusOK {
			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}
			return fmt.Errorf("Non 200 response from joining mysql at %s: %s", joinReqURL, responseBody)
		}

		logger.Info(fmt.Sprintf("Successfully sent join request to URL: %s", joinReqURL))
	}

	for _, url := range joinNodes {
		statusUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.MysqlStatus)
		runningSuccessfully := false

		for i := 0; i < maxIterations; i++ {
			resp, err := http.Get(statusUrl)
			if err != nil {
				return fmt.Errorf("Failed to get mysql status: %s", err.Error())
			}

			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}

			if resp.StatusCode != http.StatusOK {
				return fmt.Errorf("Received non-200 response when getting mysql status: %s", responseBody)
			}

			if responseBody == "running" {
				runningSuccessfully = true
				break
			}

			<-b.clock.After(time.Duration(pollingIntervalInSec) * time.Second)
		}

		if runningSuccessfully == false {
			return fmt.Errorf("Timed out waiting for mysql to start after %d seconds", timeoutInSec)
		} else {
			logger.Info("Successfully running mysql process", lager.Data{"url": statusUrl})
		}
	}
	logger.Info("Successfully started mysql process on all joining nodes")

	return nil
}

func largestSequenceNumber(seqMap map[string]int) (string, []string) {
	maxSeq := -1
	maxSeqURL := ""
	joinNodes := []string{}
	for url, seqno := range seqMap {
		if seqno > maxSeq {
			maxSeq = seqno
			maxSeqURL = url
		}
	}

	for url, _ := range seqMap {
		if url != maxSeqURL {
			joinNodes = append(joinNodes, url)
		}
	}

	return maxSeqURL, joinNodes
}
<commit_msg>Changed polling interval, and timeout<commit_after>package bootstrapper

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"github.com/cloudfoundry-incubator/cf-mysql-bootstrap/clock"
	"github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config"
	"github.com/pivotal-golang/lager"
)

type Bootstrapper struct {
	rootConfig *config.Config
	clock      clock.Clock
}

func New(rootConfig *config.Config, clock clock.Clock) *Bootstrapper {
	return &Bootstrapper{
		rootConfig: rootConfig,
		clock:      clock,
	}
}

func (b *Bootstrapper) Run() error {
	logger := b.rootConfig.Logger

	for _, url := range b.rootConfig.HealthcheckURLs {
		stopMysqlUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.ShutDownMysql)
		resp, err := http.Get(stopMysqlUrl)
		if err != nil {
			return fmt.Errorf("Failed to stop mysql: %s", err.Error())
		}

		if resp.StatusCode != http.StatusOK {
			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}
			return fmt.Errorf("Non 200 response from stopping mysql at %s: %s", stopMysqlUrl, responseBody)
		}

		logger.Info(fmt.Sprintf("Successfully sent stop request to URL: %s", stopMysqlUrl))
	}

	pollingIntervalInSec := 5
	timeoutInSec := 60
	maxIterations := timeoutInSec / pollingIntervalInSec
	for _, url := range b.rootConfig.HealthcheckURLs {
		statusUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.MysqlStatus)
		stoppedSuccessfully := false

		for i := 0; i < maxIterations; i++ {
			resp, err := http.Get(statusUrl)
			if err != nil {
				return fmt.Errorf("Failed to get mysql status: %s", err.Error())
			}

			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}

			if resp.StatusCode != http.StatusOK {
				return fmt.Errorf("Received non-200 response when getting mysql status: %s", responseBody)
			}

			if responseBody == "stopped" {
				stoppedSuccessfully = true
				break
			}

			<-b.clock.After(time.Duration(pollingIntervalInSec) * time.Second)
		}

		if stoppedSuccessfully == false {
			return fmt.Errorf("Timed out waiting for mysql to stop after %d seconds", timeoutInSec)
		} else {
			logger.Info("Successfully stopped mysql process", lager.Data{"url": statusUrl})
		}
	}

	logger.Info("Successfully stopped mysql process on all vms")
	sequenceNumberMap := make(map[string]int)
	for _, url := range b.rootConfig.HealthcheckURLs {
		getSeqNumberUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.GetSeqNumber)
		resp, err := http.Get(getSeqNumberUrl)
		if err != nil {
			return fmt.Errorf("Failed to get sequence number from mysql: %s", err.Error())
		}

		responseBody := ""
		if resp.Body != nil {
			responseBytes, _ := ioutil.ReadAll(resp.Body)
			responseBody = string(responseBytes)
		}

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("Non 200 response for fetching sequence number from mysql at %s: %s", getSeqNumberUrl, responseBody)
		}

		logger.Info("resp body", lager.Data{
			"seqno response": responseBody})
		sequenceNumber, err := strconv.Atoi(responseBody)

		if err != nil {
			return fmt.Errorf("Failed to get valid sequence number from %s with %s", getSeqNumberUrl, err.Error())
		}

		sequenceNumberMap[url] = sequenceNumber

		logger.Info(fmt.Sprintf("Successfully sent request to URL to fetch sequence number: %s", getSeqNumberUrl))
	}

	bootstrapNode, joinNodes := largestSequenceNumber(sequenceNumberMap)
	bootstrapReqURL := fmt.Sprintf("%s/%s", bootstrapNode, b.rootConfig.StartMysqlInBootstrapMode)
	resp, err := http.Get(bootstrapReqURL)
	if err != nil {
		return fmt.Errorf("Failed to bootstrap mysql node: %s", err.Error())
	}

	if resp.StatusCode != http.StatusOK {
		responseBody := ""
		if resp.Body != nil {
			responseBytes, _ := ioutil.ReadAll(resp.Body)
			responseBody = string(responseBytes)
		}
		return fmt.Errorf("Non 200 response from bootstrapping mysql at %s: %s", bootstrapReqURL, responseBody)
	}

	logger.Info(fmt.Sprintf("Successfully sent bootstrap request to URL: %s", bootstrapReqURL))

	statusUrl := fmt.Sprintf("%s/%s", bootstrapNode, b.rootConfig.MysqlStatus)
	runningSuccessfully := false

	for i := 0; i < maxIterations; i++ {
		resp, err := http.Get(statusUrl)
		if err != nil {
			return fmt.Errorf("Failed to get mysql status: %s", err.Error())
		}

		responseBody := ""
		if resp.Body != nil {
			responseBytes, _ := ioutil.ReadAll(resp.Body)
			responseBody = string(responseBytes)
		}

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("Received non-200 response when getting mysql status: %s", responseBody)
		}

		if responseBody == "running" {
			runningSuccessfully = true
			break
		}

		<-b.clock.After(time.Duration(pollingIntervalInSec) * time.Second)
	}

	if runningSuccessfully == false {
		return fmt.Errorf("Timed out waiting for mysql to start after %d seconds", timeoutInSec)
	} else {
		logger.Info("Successfully bootstrapped mysql node", lager.Data{"url": statusUrl})
	}

	for _, joinNode := range joinNodes {
		joinReqURL := fmt.Sprintf("%s/%s", joinNode, b.rootConfig.StartMysqlInJoinMode)
		resp, err := http.Get(joinReqURL)
		if err != nil {
			return fmt.Errorf("Failed to join mysql node: %s", err.Error())
		}

		if resp.StatusCode != http.StatusOK {
			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}
			return fmt.Errorf("Non 200 response from joining mysql at %s: %s", joinReqURL, responseBody)
		}

		logger.Info(fmt.Sprintf("Successfully sent join request to URL: %s", joinReqURL))
	}

	for _, url := range joinNodes {
		statusUrl := fmt.Sprintf("%s/%s", url, b.rootConfig.MysqlStatus)
		runningSuccessfully := false

		for i := 0; i < maxIterations; i++ {
			resp, err := http.Get(statusUrl)
			if err != nil {
				return fmt.Errorf("Failed to get mysql status: %s", err.Error())
			}

			responseBody := ""
			if resp.Body != nil {
				responseBytes, _ := ioutil.ReadAll(resp.Body)
				responseBody = string(responseBytes)
			}

			if resp.StatusCode != http.StatusOK {
				return fmt.Errorf("Received non-200 response when getting mysql status: %s", responseBody)
			}

			if responseBody == "running" {
				runningSuccessfully = true
				break
			}

			<-b.clock.After(time.Duration(pollingIntervalInSec) * time.Second)
		}

		if runningSuccessfully == false {
			return fmt.Errorf("Timed out waiting for mysql to start after %d seconds", timeoutInSec)
		} else {
			logger.Info("Successfully running mysql process", lager.Data{"url": statusUrl})
		}
	}
	logger.Info("Successfully started mysql process on all joining nodes")

	return nil
}

func largestSequenceNumber(seqMap map[string]int) (string, []string) {
	maxSeq := -1
	maxSeqURL := ""
	joinNodes := []string{}
	for url, seqno := range seqMap {
		if seqno > maxSeq {
			maxSeq = seqno
			maxSeqURL = url
		}
	}

	for url, _ := range seqMap {
		if url != maxSeqURL {
			joinNodes = append(joinNodes, url)
		}
	}

	return maxSeqURL, joinNodes
}
<|endoftext|>


<commit_before>package mch

import (
	"net/http"
	"time"
	"crypto/md5"
	"crypto/hmac"
	"fmt"
	"crypto/sha256"
	"bytes"
	"sync"
	"strings"
	"io"
	"crypto/tls"
	"go.uber.org/zap"
	"github.com/jiudaoyun/wechat"
)

const API_URL = "https://api.mch.weixin.qq.com"

type Client struct {
	appID  string
	mchID  string
	apiKey string

	subAppID string
	subMchID string

	signType string // 签名类型，目前支持HMAC-SHA256和MD5，默认为MD5

	client    *http.Client
	tlsClient *http.Client

	*zap.SugaredLogger
}

func New(appID, mchID, apiKey string, timeout ...time.Duration) *Client {
	client := &Client{
		appID:  appID,
		mchID:  mchID,
		apiKey: apiKey,

		SugaredLogger: wechat.Sugar,
	}
	if len(timeout) > 0 {
		client.client = &http.Client{
			Timeout: timeout[0],
		}
	}
	return client
}

func NewWithSubMch(appID, mchID, apiKey, subAppID, subMchID string, timeout ...time.Duration) *Client {
	client := &Client{
		appID:    appID,
		mchID:    mchID,
		apiKey:   apiKey,
		subAppID: subAppID,
		subMchID: subMchID,
	}
	if len(timeout) > 0 {
		client.client = &http.Client{
			Timeout: timeout[0],
		}
	}
	return client
}

func (client *Client) SetCert(certPEMBlock, keyPEMBlock []byte) error {
	cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
	if err != nil {
		return err
	}
	return client.setCert(cert)
}

func (client *Client) SetCertFromFile(certFile, keyFile string) error {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	return client.setCert(cert)
}

func (client *Client) setCert(cert tls.Certificate) error {
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	transport := *http.DefaultTransport.(*http.Transport)
	transport.TLSClientConfig = tlsConfig
	client.tlsClient = &http.Client{
		Transport: &transport,
		Timeout:   client.client.Timeout,
	}
	return nil
}

func (client *Client) SetSignType(signType string) error {
	if signType != SignTypeHMAC_SHA256 && signType != SignTypeMD5 {
		return fmt.Errorf("unsupported sign type: %s", signType)
	}
	client.signType = signType
	return nil
}

var pool = sync.Pool{
	New: func() interface{} {
		return bytes.NewBuffer(make([]byte, 0, 4<<10)) // 4KB
	},
}

func (client *Client) PostXML(relativeURL string, req map[string]string) (rep map[string]string, err error) {
	return client.postXML(false, relativeURL, req)
}

func (client *Client) PostXMLWithCert(relativeURL string, req map[string]string) (rep map[string]string, err error) {
	return client.postXML(true, relativeURL, req)
}

func (client *Client) postXML(withCert bool, relativeURL string, req map[string]string) (rep map[string]string, err error) {
	buffer, err := client.makeRequest(req)
	defer pool.Put(buffer)
	if err != nil {
		return nil, err
	}

	url := API_URL + relativeURL
	rep, err = client.postToMap(withCert, url, buffer)
	if err != nil {
		bizError, ok := err.(*BizError)
		if !ok || bizError.ErrCode != ErrCodeSYSTEMERROR {
			return
		}
		url = switchReqURL(url)
		return client.postToMap(withCert, url, buffer) // retry
	}
	return
}

func (client *Client) makeRequest(req map[string]string) (*bytes.Buffer, error) {
	req["appid"] = client.appID
	req["mch_id"] = client.mchID
	if client.subAppID != "" {
		req["sub_appid"] = client.subAppID
	}
	if client.subMchID != "" {
		req["sub_mch_id"] = client.subMchID
	}

	req["nonce_str"] = RandString(32)

	switch client.signType {
	case "", SignTypeMD5:
		req["sign_type"] = SignTypeMD5
		req["sign"] = Sign(req, client.apiKey, md5.New())
	case SignTypeHMAC_SHA256:
		req["sign_type"] = SignTypeHMAC_SHA256
		req["sign"] = Sign(req, client.apiKey, hmac.New(sha256.New, []byte(client.apiKey)))
	}

	buffer := pool.Get().(*bytes.Buffer)
	buffer.Reset()

	err := EncodeXML(buffer, req)
	return buffer, err
}

func (client *Client) postToMap(withCert bool, url string, body io.Reader) (rep map[string]string, err error) {
	repBody, err := client.post(withCert, url, body)
	if err != nil {
		return nil, err
	}
	defer repBody.Close()
	return client.toMap(repBody)
}

func (client *Client) post(withCert bool, url string, body io.Reader) (io.ReadCloser, error) {
	c := client.client
	if withCert {
		c = client.tlsClient
	}
	rep, err := c.Post(url, "text/xml; charset=utf-8", body)
	if err != nil {
		return nil, err
	}
	if rep.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http status: %s", rep.Status)
	}

	return rep.Body, nil
}

func (client *Client) toMap(repBody io.Reader) (rep map[string]string, err error) {
	if closer, ok := repBody.(io.Closer); ok {
		defer closer.Close()
	}

	defer func() {
		if err != nil {
			client.Infof("QueryOrder err: %s", err)
		}
		client.Infof("QueryOrder response map: %s", rep)
	}()
	rep, err = DecodeXML(repBody)
	if err != nil {
		return nil, err
	}

	returnCode := rep["return_code"]
	if returnCode != ReturnCodeSuccess {
		return nil, &Error{
			ReturnCode: returnCode,
			ReturnMsg:  rep["return_msg"],
		}
	}

	appId := rep["appid"]
	if appId != client.appID {
		return nil, fmt.Errorf("appid mismatch, have: %s, want: %s", appId, client.appID)
	}
	mchId := rep["mch_id"]
	if mchId != client.mchID {
		return nil, fmt.Errorf("mch_id mismatch, have: %s, want: %s", mchId, client.mchID)
	}

	if client.subAppID != "" {
		subAppId := rep["sub_appid"]
		subMchId := rep["sub_mch_id"]
		if subAppId != "" && subAppId != client.subAppID {
			return nil, fmt.Errorf("sub_appid mismatch, have: %s, want: %s", subAppId, client.subAppID)
		}
		if subMchId != client.subMchID {
			return nil, fmt.Errorf("sub_mch_id mismatch, have: %s, want: %s", subMchId, client.subMchID)
		}
	}

	var signWant string
	signHave := rep["sign"]
	repSignType := rep["sign_type"]
	switch repSignType {
	case "", SignTypeMD5:
		signWant = Sign(rep, client.apiKey, md5.New())
	case SignTypeHMAC_SHA256:
		signWant = Sign(rep, client.apiKey, hmac.New(sha256.New, []byte(client.apiKey)))
	default:
		err = fmt.Errorf("unsupported response sign_type: %s", repSignType)
		return nil, err
	}
	if signHave != signWant {
		return nil, fmt.Errorf("sign mismatch,\nhave: %s,\nwant: %s", signHave, signWant)
	}

	resultCode := rep["result_code"]
	if resultCode != ResultCodeSuccess {
		return nil, &BizError{
			ResultCode:  resultCode,
			ErrCode:     rep["err_code"],
			ErrCodeDesc: rep["err_code_des"],
		}
	}
	return rep, nil
}

func switchReqURL(url string) string {
	switch {
	case strings.HasPrefix(url, "https://api.mch.weixin.qq.com/"):
		return "https://api2.mch.weixin.qq.com/" + url[len("https://api.mch.weixin.qq.com/"):]
	case strings.HasPrefix(url, "https://api2.mch.weixin.qq.com/"):
		return "https://api.mch.weixin.qq.com/" + url[len("https://api2.mch.weixin.qq.com/"):]
	default:
		return url
	}
}
<commit_msg>Update source files<commit_after>package mch

import (
	"net/http"
	"time"
	"crypto/md5"
	"crypto/hmac"
	"fmt"
	"crypto/sha256"
	"bytes"
	"sync"
	"strings"
	"io"
	"crypto/tls"
	"go.uber.org/zap"
	"github.com/jiudaoyun/wechat"
)

const API_URL = "https://api.mch.weixin.qq.com"

type Client struct {
	appID  string
	mchID  string
	apiKey string

	subAppID string
	subMchID string

	signType string // 签名类型，目前支持HMAC-SHA256和MD5，默认为MD5

	client    *http.Client
	tlsClient *http.Client

	*zap.SugaredLogger
}

func New(appID, mchID, apiKey string, timeout ...time.Duration) *Client {
	client := &Client{
		appID:  appID,
		mchID:  mchID,
		apiKey: apiKey,

		SugaredLogger: wechat.Sugar,
	}
	if len(timeout) > 0 {
		client.client = &http.Client{
			Timeout: timeout[0],
		}
	}
	return client
}

func NewWithSubMch(appID, mchID, apiKey, subAppID, subMchID string, timeout ...time.Duration) *Client {
	client := &Client{
		appID:    appID,
		mchID:    mchID,
		apiKey:   apiKey,
		subAppID: subAppID,
		subMchID: subMchID,
	}
	if len(timeout) > 0 {
		client.client = &http.Client{
			Timeout: timeout[0],
		}
	}
	return client
}

func (client *Client) SetCert(certPEMBlock, keyPEMBlock []byte) error {
	cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
	if err != nil {
		return err
	}
	return client.setCert(cert)
}

func (client *Client) SetCertFromFile(certFile, keyFile string) error {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	return client.setCert(cert)
}

func (client *Client) setCert(cert tls.Certificate) error {
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	transport := *http.DefaultTransport.(*http.Transport)
	transport.TLSClientConfig = tlsConfig
	client.tlsClient = &http.Client{
		Transport: &transport,
		Timeout:   client.client.Timeout,
	}
	return nil
}

func (client *Client) SetSignType(signType string) error {
	if signType != SignTypeHMAC_SHA256 && signType != SignTypeMD5 {
		return fmt.Errorf("unsupported sign type: %s", signType)
	}
	client.signType = signType
	return nil
}

var pool = sync.Pool{
	New: func() interface{} {
		return bytes.NewBuffer(make([]byte, 0, 4<<10)) // 4KB
	},
}

func (client *Client) PostXML(relativeURL string, req map[string]string) (rep map[string]string, err error) {
	return client.postXML(false, relativeURL, req)
}

func (client *Client) PostXMLWithCert(relativeURL string, req map[string]string) (rep map[string]string, err error) {
	return client.postXML(true, relativeURL, req)
}

func (client *Client) postXML(withCert bool, relativeURL string, req map[string]string) (rep map[string]string, err error) {
	buffer, err := client.makeRequest(req)
	defer pool.Put(buffer)
	if err != nil {
		return nil, err
	}

	url := API_URL + relativeURL
	rep, err = client.postToMap(withCert, url, buffer)
	if err != nil {
		bizError, ok := err.(*BizError)
		if !ok || bizError.ErrCode != ErrCodeSYSTEMERROR {
			return
		}
		url = switchReqURL(url)
		return client.postToMap(withCert, url, buffer) // retry
	}
	return
}

func (client *Client) makeRequest(req map[string]string) (*bytes.Buffer, error) {
	req["appid"] = client.appID
	req["mch_id"] = client.mchID
	if client.subAppID != "" {
		req["sub_appid"] = client.subAppID
	}
	if client.subMchID != "" {
		req["sub_mch_id"] = client.subMchID
	}

	req["nonce_str"] = RandString(32)

	switch client.signType {
	case "", SignTypeMD5:
		req["sign_type"] = SignTypeMD5
		req["sign"] = Sign(req, client.apiKey, md5.New())
	case SignTypeHMAC_SHA256:
		req["sign_type"] = SignTypeHMAC_SHA256
		req["sign"] = Sign(req, client.apiKey, hmac.New(sha256.New, []byte(client.apiKey)))
	}

	buffer := pool.Get().(*bytes.Buffer)
	buffer.Reset()

	err := EncodeXML(buffer, req)
	return buffer, err
}

func (client *Client) postToMap(withCert bool, url string, body io.Reader) (rep map[string]string, err error) {
	repBody, err := client.post(withCert, url, body)
	if err != nil {
		return nil, err
	}
	defer repBody.Close()
	return client.toMap(repBody)
}

func (client *Client) post(withCert bool, url string, body io.Reader) (io.ReadCloser, error) {
	c := client.client
	if withCert {
		c = client.tlsClient
	}
	rep, err := c.Post(url, "text/xml; charset=utf-8", body)
	if err != nil {
		return nil, err
	}
	if rep.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http status: %s", rep.Status)
	}

	return rep.Body, nil
}

func (client *Client) toMap(repBody io.Reader) (rep map[string]string, err error) {
	if closer, ok := repBody.(io.Closer); ok {
		defer closer.Close()
	}

	defer func() {
		if err != nil {
			client.Infof("QueryOrder err: %s", err)
		}
		client.Infof("QueryOrder response map: %s", len(rep))
	}()
	rep, err = DecodeXML(repBody)
	if err != nil {
		return nil, err
	}

	returnCode := rep["return_code"]
	if returnCode != ReturnCodeSuccess {
		return nil, &Error{
			ReturnCode: returnCode,
			ReturnMsg:  rep["return_msg"],
		}
	}

	appId := rep["appid"]
	if appId != client.appID {
		return nil, fmt.Errorf("appid mismatch, have: %s, want: %s", appId, client.appID)
	}
	mchId := rep["mch_id"]
	if mchId != client.mchID {
		return nil, fmt.Errorf("mch_id mismatch, have: %s, want: %s", mchId, client.mchID)
	}

	if client.subAppID != "" {
		subAppId := rep["sub_appid"]
		subMchId := rep["sub_mch_id"]
		if subAppId != "" && subAppId != client.subAppID {
			return nil, fmt.Errorf("sub_appid mismatch, have: %s, want: %s", subAppId, client.subAppID)
		}
		if subMchId != client.subMchID {
			return nil, fmt.Errorf("sub_mch_id mismatch, have: %s, want: %s", subMchId, client.subMchID)
		}
	}

	var signWant string
	signHave := rep["sign"]
	repSignType := rep["sign_type"]
	switch repSignType {
	case "", SignTypeMD5:
		signWant = Sign(rep, client.apiKey, md5.New())
	case SignTypeHMAC_SHA256:
		signWant = Sign(rep, client.apiKey, hmac.New(sha256.New, []byte(client.apiKey)))
	default:
		err = fmt.Errorf("unsupported response sign_type: %s", repSignType)
		return nil, err
	}
	if signHave != signWant {
		return nil, fmt.Errorf("sign mismatch,\nhave: %s,\nwant: %s", signHave, signWant)
	}

	resultCode := rep["result_code"]
	if resultCode != ResultCodeSuccess {
		return nil, &BizError{
			ResultCode:  resultCode,
			ErrCode:     rep["err_code"],
			ErrCodeDesc: rep["err_code_des"],
		}
	}
	return rep, nil
}

func switchReqURL(url string) string {
	switch {
	case strings.HasPrefix(url, "https://api.mch.weixin.qq.com/"):
		return "https://api2.mch.weixin.qq.com/" + url[len("https://api.mch.weixin.qq.com/"):]
	case strings.HasPrefix(url, "https://api2.mch.weixin.qq.com/"):
		return "https://api.mch.weixin.qq.com/" + url[len("https://api2.mch.weixin.qq.com/"):]
	default:
		return url
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"git.ianfross.com/ifross/expensetracker/env"
	"git.ianfross.com/ifross/expensetracker/auth"

	"github.com/julienschmidt/httprouter"
	"github.com/golang/glog"
	"github.com/juju/errors"

	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"git.ianfross.com/ifross/expensetracker/models"
)

type jsonResponse struct {
	Status  string      `json:"status"`
	Data    interface{} `json:"data,omitempty"`
	Message string      `json:"message,omitempty"`
	Code    int         `json:"code,omitempty"`
}

func jsonSuccess(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	err := json.NewEncoder(w).Encode(jsonResponse{"success", data, "", http.StatusOK})
	if err != nil {
		glog.Errorf("Error encoding json in successful respone:%v", err)
	}
}

func jsonError(w http.ResponseWriter, code int, message string, err error) error {
	if err != nil {
		glog.Errorf("Error in handler: error=%v\nmessage=%s", errors.ErrorStack(err), message)
	} else {
		glog.Errorf("Error in handler: message=%s", message)
	}

	w.WriteHeader(code)
	w.Header().Set("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(jsonResponse{"error", nil, message, code})
}

func jsonErrorWithCodeText(w http.ResponseWriter, code int, err error) error {
	return jsonError(w, code, http.StatusText(code), err)
}

type HandlerVars struct {
	env *env.Env
	ps  httprouter.Params
}

func createHandlerVars(e *env.Env, ps httprouter.Params) *HandlerVars {
	return &HandlerVars{e, ps}
}

type adminUsersPOSTHandler struct {
	*HandlerVars
}

func (a adminUsersPOSTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	u := struct {
		Name     string `json:"name"`
		Email    string `json:"email"`
		Admin    bool   `json:"admin"`
		Active   bool   `json:"active"`
		Password string `json:"password"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&u)

	if err != nil && err != io.EOF {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	user, err := a.env.New(u.Name, u.Email, u.Password, u.Password, u.Active, u.Admin)
	if err != nil {
		err = errors.Trace(err)
		jsonError(w, http.StatusBadRequest, err.Error(), nil)
		return
	}

	err = a.env.Insert(user)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	jsonSuccess(w, user)
	if err != nil {
		glog.Errorf("Error encoding json: %v\n", err)
	}
}

func CreateAdminUsersPOSTHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminUsersPOSTHandler{createHandlerVars(e, ps)}, 200, nil
}

type adminUsersGETHandler struct {
	*HandlerVars
}

func CreateAdminUsersGETHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminUsersGETHandler{createHandlerVars(e, ps)}, 200, nil
}

func (a adminUsersGETHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	users, err := a.env.Users()
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	fmt.Println("Users:", users)

	jsonSuccess(w, users)
	if err != nil {
		glog.Errorf("Error encoding json: %v\n", err)
	}
}

type adminUserDELETEHandler struct {
	*HandlerVars
}

func CreateAdminUserDELETEHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminUserDELETEHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

func (h adminUserDELETEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	uidStr := h.ps.ByName("user_id")
	uid, err := strconv.Atoi(uidStr)
	if err != nil {
		jsonError(w, http.StatusInternalServerError, err.Error(), errors.Trace(err))
		return
	}

	err = h.env.DeleteUserById(int64(uid))
	if err != nil {
		jsonError(w, http.StatusInternalServerError, err.Error(), errors.Trace(err))
		return
	}

	jsonSuccess(w, nil)
	if err != nil {
		fmt.Printf("Error sending success: %v", err)
	}
}

type adminGroupsGETHandler struct {
	*HandlerVars
}

func CreateAdminGroupsGETHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupsGETHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

func (h adminGroupsGETHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Get user
	_, err := h.env.AdminFromSession(w, r)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	// User is authenticated
	groups, err := h.env.AllGroups()
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	jsonSuccess(w, groups)
}

type adminGroupPOSTHandler struct {
	*HandlerVars
}

func CreateAdminGroupPOSTHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupPOSTHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

func (h adminGroupPOSTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := h.env.AdminFromSession(w, r)

	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	newGroup := struct {
		Name string `json:"name"`
		Emails []string `json:"emails"`
		}{}

	err = json.NewDecoder(r.Body).Decode(&newGroup)

	if err != nil && err != io.EOF {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	var users []*auth.User
	// First make sure all of them are users
	for _, email := range newGroup.Emails {
		u, err := h.env.UserManager.ByEmail(email)
		if err != nil {
			jsonError(w, http.StatusBadRequest, fmt.Sprintf("User with email %s does not exist", email), errors.Trace(err))
			return
		}
		users = append(users, u)
	}

	g, err := h.env.NewGroup(newGroup.Name)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	for _, user := range users {
		err := h.env.AddUserToGroup(g, user, false)
		if err != nil {
			jsonError(w, http.StatusInternalServerError, "Error creating group", errors.Trace(err))
			return
		}
	}

	jsonSuccess(w, g)
}

type adminGroupDELETEHandler struct {
	*HandlerVars
}

func CreateAdminGroupDELETEHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, err) {
	return adminGroupDELETEHandler{createHandlerVars(e, ps)}
}

func (h adminGroupDELETEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := h.env.AdminFromSession(w, r)

	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	// User is admin

	groupId := struct {
		Id int64 `json:"id"`
	}{}

	err = json.NewDecoder(r.Body).Decode(&groupId)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	err = h.env.DeleteGroup(&models.Group{Id:groupId.Id})
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	jsonSuccess(w, nil)
}

type adminGroupPUTHandler struct {
	*HandlerVars
}

func CreateAdminGroupPUTHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupPUTHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

// Need to figure out what happens to the expenses
func (h adminGroupPUTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := h.env.AdminFromSession(w, r)

	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	group := struct {
		Id int64 `json:"id"`
		Name string `json:"name"`
		Emails []string `json:"emails"`
		}{}

	err = json.NewDecoder(r.Body).Decode(&group)

	if err != nil && err != io.EOF {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	// TODO: finish this function

	jsonError(w, http.StatusServiceUnavailable, "Unimplemented", nil)
}
<commit_msg>Fixed typo in handlers/admin.go<commit_after>package handlers

import (
	"git.ianfross.com/ifross/expensetracker/env"
	"git.ianfross.com/ifross/expensetracker/auth"

	"github.com/julienschmidt/httprouter"
	"github.com/golang/glog"
	"github.com/juju/errors"

	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"git.ianfross.com/ifross/expensetracker/models"
)

type jsonResponse struct {
	Status  string      `json:"status"`
	Data    interface{} `json:"data,omitempty"`
	Message string      `json:"message,omitempty"`
	Code    int         `json:"code,omitempty"`
}

func jsonSuccess(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	err := json.NewEncoder(w).Encode(jsonResponse{"success", data, "", http.StatusOK})
	if err != nil {
		glog.Errorf("Error encoding json in successful respone:%v", err)
	}
}

func jsonError(w http.ResponseWriter, code int, message string, err error) error {
	if err != nil {
		glog.Errorf("Error in handler: error=%v\nmessage=%s", errors.ErrorStack(err), message)
	} else {
		glog.Errorf("Error in handler: message=%s", message)
	}

	w.WriteHeader(code)
	w.Header().Set("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(jsonResponse{"error", nil, message, code})
}

func jsonErrorWithCodeText(w http.ResponseWriter, code int, err error) error {
	return jsonError(w, code, http.StatusText(code), err)
}

type HandlerVars struct {
	env *env.Env
	ps  httprouter.Params
}

func createHandlerVars(e *env.Env, ps httprouter.Params) *HandlerVars {
	return &HandlerVars{e, ps}
}

type adminUsersPOSTHandler struct {
	*HandlerVars
}

func (a adminUsersPOSTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	u := struct {
		Name     string `json:"name"`
		Email    string `json:"email"`
		Admin    bool   `json:"admin"`
		Active   bool   `json:"active"`
		Password string `json:"password"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&u)

	if err != nil && err != io.EOF {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	user, err := a.env.New(u.Name, u.Email, u.Password, u.Password, u.Active, u.Admin)
	if err != nil {
		err = errors.Trace(err)
		jsonError(w, http.StatusBadRequest, err.Error(), nil)
		return
	}

	err = a.env.Insert(user)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	jsonSuccess(w, user)
	if err != nil {
		glog.Errorf("Error encoding json: %v\n", err)
	}
}

func CreateAdminUsersPOSTHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminUsersPOSTHandler{createHandlerVars(e, ps)}, 200, nil
}

type adminUsersGETHandler struct {
	*HandlerVars
}

func CreateAdminUsersGETHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminUsersGETHandler{createHandlerVars(e, ps)}, 200, nil
}

func (a adminUsersGETHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	users, err := a.env.Users()
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	fmt.Println("Users:", users)

	jsonSuccess(w, users)
	if err != nil {
		glog.Errorf("Error encoding json: %v\n", err)
	}
}

type adminUserDELETEHandler struct {
	*HandlerVars
}

func CreateAdminUserDELETEHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminUserDELETEHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

func (h adminUserDELETEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	uidStr := h.ps.ByName("user_id")
	uid, err := strconv.Atoi(uidStr)
	if err != nil {
		jsonError(w, http.StatusInternalServerError, err.Error(), errors.Trace(err))
		return
	}

	err = h.env.DeleteUserById(int64(uid))
	if err != nil {
		jsonError(w, http.StatusInternalServerError, err.Error(), errors.Trace(err))
		return
	}

	jsonSuccess(w, nil)
	if err != nil {
		fmt.Printf("Error sending success: %v", err)
	}
}

type adminGroupsGETHandler struct {
	*HandlerVars
}

func CreateAdminGroupsGETHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupsGETHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

func (h adminGroupsGETHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Get user
	_, err := h.env.AdminFromSession(w, r)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	// User is authenticated
	groups, err := h.env.AllGroups()
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	jsonSuccess(w, groups)
}

type adminGroupPOSTHandler struct {
	*HandlerVars
}

func CreateAdminGroupPOSTHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupPOSTHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

func (h adminGroupPOSTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := h.env.AdminFromSession(w, r)

	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	newGroup := struct {
		Name string `json:"name"`
		Emails []string `json:"emails"`
		}{}

	err = json.NewDecoder(r.Body).Decode(&newGroup)

	if err != nil && err != io.EOF {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	var users []*auth.User
	// First make sure all of them are users
	for _, email := range newGroup.Emails {
		u, err := h.env.UserManager.ByEmail(email)
		if err != nil {
			jsonError(w, http.StatusBadRequest, fmt.Sprintf("User with email %s does not exist", email), errors.Trace(err))
			return
		}
		users = append(users, u)
	}

	g, err := h.env.NewGroup(newGroup.Name)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	for _, user := range users {
		err := h.env.AddUserToGroup(g, user, false)
		if err != nil {
			jsonError(w, http.StatusInternalServerError, "Error creating group", errors.Trace(err))
			return
		}
	}

	jsonSuccess(w, g)
}

type adminGroupDELETEHandler struct {
	*HandlerVars
}

func CreateAdminGroupDELETEHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupDELETEHandler{createHandlerVars(e, ps)}
}

func (h adminGroupDELETEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := h.env.AdminFromSession(w, r)

	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	// User is admin

	groupId := struct {
		Id int64 `json:"id"`
	}{}

	err = json.NewDecoder(r.Body).Decode(&groupId)
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	err = h.env.DeleteGroup(&models.Group{Id:groupId.Id})
	if err != nil {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	jsonSuccess(w, nil)
}

type adminGroupPUTHandler struct {
	*HandlerVars
}

func CreateAdminGroupPUTHandler(
	e *env.Env,
	w http.ResponseWriter,
	r *http.Request,
	ps httprouter.Params) (http.Handler, int, error) {
	return adminGroupPUTHandler{createHandlerVars(e, ps)}, http.StatusOK, nil
}

// Need to figure out what happens to the expenses
func (h adminGroupPUTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := h.env.AdminFromSession(w, r)

	if err != nil {
		jsonErrorWithCodeText(w, http.StatusUnauthorized, errors.Trace(err))
		return
	}

	group := struct {
		Id int64 `json:"id"`
		Name string `json:"name"`
		Emails []string `json:"emails"`
		}{}

	err = json.NewDecoder(r.Body).Decode(&group)

	if err != nil && err != io.EOF {
		jsonErrorWithCodeText(w, http.StatusInternalServerError, errors.Trace(err))
		return
	}

	// TODO: finish this function

	jsonError(w, http.StatusServiceUnavailable, "Unimplemented", nil)
}
<|endoftext|>