<commit_before>package debug

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"

	"github.com/labstack/gommon/color"
	"github.com/mattn/go-colorable"
	"github.com/mattn/go-isatty"
)

var enableDebug = false

func Switch(on bool) {
	enableDebug = on
}

var gopaths = func() []string {
	paths := strings.Split(os.Getenv("GOPATH"), ";")
	for i, path := range paths {
		paths[i] = filepath.Join(path, "src") + "/"
	}
	if goroot := runtime.GOROOT(); goroot != "" {
		paths = append(paths, filepath.Join(goroot, "src")+"/")
	}
	return paths
}()

var debugOut, debugColor = func() (io.Writer, color.Color) {
	clr := color.Color{}
	out := colorable.NewColorableStdout()
	ColorSwitch(&clr, out, os.Stdout.Fd())
	return out, clr
}()

func Debugf(format string, args ...interface{}) {
	if !enableDebug {
		return
	}
	_, file, line, _ := runtime.Caller(1)
	fileline := makeFileLine(file, line)
	fmt.Fprintf(debugOut, "[DEBUG]"+debugColor.Bold(fileline)+" "+format+"\n", args...)
}

func Panicf(format string, args ...interface{}) {
	clr := color.Color{}
	out := colorable.NewColorableStderr()
	ColorSwitch(&clr, out, os.Stderr.Fd())
	buff := bytes.NewBufferString("")
	buff.WriteString(clr.Red(fmt.Sprintf(format, args...)))
	buff.WriteString("\n\n[stack]\n")
	skip := 1
	for {
		_, file, line, ok := runtime.Caller(skip)
		if !ok {
			break
		}
		skip++
		buff.WriteString(makeFileLine(file, line))
		buff.WriteString("\n")
	}
	fmt.Fprintf(out, buff.String())
	os.Exit(999)
}

func TypeName(i interface{}) string {
	t := reflect.TypeOf(i)
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return t.Name()
}

func JSON(v interface{}) string {
	data, err := json.MarshalIndent(v, "", "\t")
	if err != nil {
		return ""
	}
	return string(data)
}

func makeFileLine(file string, line int) string {
	for _, path := range gopaths {
		if strings.HasPrefix(file, path) {
			file = strings.TrimPrefix(strings.TrimPrefix(file, path), "/")
			break
		}
	}
	return fmt.Sprintf("%s:%d", file, line)
}

func ColorSwitch(clr *color.Color, w io.Writer, fds ...uintptr) {
	clr.Disable()
	if len(fds) > 0 {
		if isatty.IsTerminal(fds[0]) {
			clr.Enable()
		}
	} else if w, ok := w.(*os.File); ok && isatty.IsTerminal(w.Fd()) {
		clr.Enable()
	}
}
<commit_msg>fix Panicf<commit_after>package debug

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"

	"github.com/labstack/gommon/color"
	"github.com/mattn/go-colorable"
	"github.com/mattn/go-isatty"
)

var enableDebug = false

func Switch(on bool) {
	enableDebug = on
}

var gopaths = func() []string {
	paths := strings.Split(os.Getenv("GOPATH"), ";")
	for i, path := range paths {
		paths[i] = filepath.Join(path, "src") + "/"
	}
	if goroot := runtime.GOROOT(); goroot != "" {
		paths = append(paths, filepath.Join(goroot, "src")+"/")
	}
	return paths
}()

var debugOut, debugColor = func() (io.Writer, color.Color) {
	clr := color.Color{}
	out := colorable.NewColorableStdout()
	ColorSwitch(&clr, out, os.Stdout.Fd())
	return out, clr
}()

func Debugf(format string, args ...interface{}) {
	if !enableDebug {
		return
	}
	_, file, line, _ := runtime.Caller(1)
	fileline := makeFileLine(file, line)
	fmt.Fprintf(debugOut, "[DEBUG]"+debugColor.Bold(fileline)+" "+format+"\n", args...)
}

func Panicf(format string, args ...interface{}) {
	buff := bytes.NewBufferString("")
	buff.WriteString(fmt.Sprintf(format, args...))
	buff.WriteString("\n\n[stack]\n")
	skip := 1
	for {
		_, file, line, ok := runtime.Caller(skip)
		if !ok {
			break
		}
		skip++
		buff.WriteString(makeFileLine(file, line))
		buff.WriteString("\n")
	}
	panic(buff.String())
}

func TypeName(i interface{}) string {
	t := reflect.TypeOf(i)
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return t.Name()
}

func JSON(v interface{}) string {
	data, err := json.MarshalIndent(v, "", "\t")
	if err != nil {
		return ""
	}
	return string(data)
}

func makeFileLine(file string, line int) string {
	for _, path := range gopaths {
		if strings.HasPrefix(file, path) {
			file = strings.TrimPrefix(strings.TrimPrefix(file, path), "/")
			break
		}
	}
	return fmt.Sprintf("%s:%d", file, line)
}

func ColorSwitch(clr *color.Color, w io.Writer, fds ...uintptr) {
	clr.Disable()
	if len(fds) > 0 {
		if isatty.IsTerminal(fds[0]) {
			clr.Enable()
		}
	} else if w, ok := w.(*os.File); ok && isatty.IsTerminal(w.Fd()) {
		clr.Enable()
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package relay

import (
	"crypto/tls"
	"encoding/json"
	"net"
	"net/http"
	"net/url"
	"time"

	"github.com/syncthing/relaysrv/client"
	"github.com/syncthing/relaysrv/protocol"
	"github.com/syncthing/syncthing/lib/config"
	"github.com/syncthing/syncthing/lib/discover"
	"github.com/syncthing/syncthing/lib/model"
	"github.com/syncthing/syncthing/lib/osutil"
	"github.com/syncthing/syncthing/lib/sync"

	"github.com/thejerf/suture"
)

func NewSvc(cfg *config.Wrapper, tlsCfg *tls.Config, conns chan<- model.IntermediateConnection) *Svc {
	svc := &Svc{
		Supervisor: suture.New("Svc", suture.Spec{
			Log: func(log string) {
				if debug {
					l.Debugln(log)
				}
			},
			FailureBackoff:   5 * time.Minute,
			FailureDecay:     float64((10 * time.Minute) / time.Second),
			FailureThreshold: 5,
		}),
		cfg:    cfg,
		tlsCfg: tlsCfg,

		tokens:  make(map[string]suture.ServiceToken),
		clients: make(map[string]*client.ProtocolClient),
		mut:     sync.NewRWMutex(),

		invitations: make(chan protocol.SessionInvitation),
	}

	rcfg := cfg.Raw()
	svc.CommitConfiguration(rcfg, rcfg)
	cfg.Subscribe(svc)

	receiver := &invitationReceiver{
		tlsCfg:      tlsCfg,
		conns:       conns,
		invitations: svc.invitations,
		stop:        make(chan struct{}),
	}

	svc.Add(receiver)

	return svc
}

type Svc struct {
	*suture.Supervisor
	cfg    *config.Wrapper
	tlsCfg *tls.Config

	tokens      map[string]suture.ServiceToken
	clients     map[string]*client.ProtocolClient
	mut         sync.RWMutex
	invitations chan protocol.SessionInvitation
}

func (s *Svc) VerifyConfiguration(from, to config.Configuration) error {
	for _, addr := range to.Options.RelayServers {
		_, err := url.Parse(addr)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Svc) CommitConfiguration(from, to config.Configuration) bool {
	existing := make(map[string]*url.URL, len(to.Options.RelayServers))

	for _, addr := range to.Options.RelayServers {
		uri, err := url.Parse(addr)
		if err != nil {
			if debug {
				l.Debugln("Failed to parse relay address", addr, err)
			}
			continue
		}
		existing[uri.String()] = uri
	}

	// Query dynamic addresses, and pick the closest relay from the ones they provide.
	for key, uri := range existing {
		if uri.Scheme != "dynamic+http" && uri.Scheme != "dynamic+https" {
			continue
		}
		delete(existing, key)

		// Trim off the `dynamic+` prefix
		uri.Scheme = uri.Scheme[8:]

		if debug {
			l.Debugln("Looking up dynamic relays from", uri)
		}

		data, err := http.Get(uri.String())
		if err != nil {
			if debug {
				l.Debugln("Failed to lookup dynamic relays", err)
			}
			continue
		}

		var ann dynamicAnnouncement
		err = json.NewDecoder(data.Body).Decode(&ann)
		data.Body.Close()
		if err != nil {
			if debug {
				l.Debugln("Failed to lookup dynamic relays", err)
			}
			continue
		}

		dynRelays := make([]discover.Relay, 0, len(ann.Relays))
		for _, relayAnn := range ann.Relays {
			ruri, err := url.Parse(relayAnn.URL)
			if err != nil {
				if debug {
					l.Debugln("Failed to parse dynamic relay address", relayAnn.URL, err)
				}
				continue
			}
			if debug {
				l.Debugln("Found", ruri, "via", uri)
			}
			dynRelays = append(dynRelays, discover.Relay{
				Address: ruri.String(),
			})
		}

		dynRelayAddrs := discover.RelayAddressesSortedByLatency(dynRelays)
		if len(dynRelayAddrs) > 0 {
			closestRelay := dynRelayAddrs[0]
			if debug {
				l.Debugln("Picking", closestRelay, "as closest dynamic relay from", uri)
			}
			ruri, _ := url.Parse(closestRelay)
			existing[closestRelay] = ruri
		} else if debug {
			l.Debugln("No dynamic relay found on", uri)
		}
	}

	s.mut.Lock()

	for key, uri := range existing {
		_, ok := s.tokens[key]
		if !ok {
			if debug {
				l.Debugln("Connecting to relay", uri)
			}
			c := client.NewProtocolClient(uri, s.tlsCfg.Certificates, s.invitations)
			s.tokens[key] = s.Add(c)
			s.clients[key] = c
		}
	}

	for key, token := range s.tokens {
		_, ok := existing[key]
		if !ok {
			err := s.Remove(token)
			delete(s.tokens, key)
			delete(s.clients, key)
			if debug {
				l.Debugln("Disconnecting from relay", key, err)
			}
		}
	}

	s.mut.Unlock()

	return true
}

func (s *Svc) ClientStatus() map[string]bool {
	s.mut.RLock()
	status := make(map[string]bool, len(s.clients))
	for uri, client := range s.clients {
		status[uri] = client.StatusOK()
	}
	s.mut.RUnlock()
	return status
}

type invitationReceiver struct {
	invitations chan protocol.SessionInvitation
	tlsCfg      *tls.Config
	conns       chan<- model.IntermediateConnection
	stop        chan struct{}
}

func (r *invitationReceiver) Serve() {
	for {
		select {
		case inv := <-r.invitations:
			if debug {
				l.Debugln("Received relay invitation", inv)
			}
			conn, err := client.JoinSession(inv)
			if err != nil {
				if debug {
					l.Debugf("Failed to join relay session %s: %v", inv, err)
				}
				continue
			}

			err = osutil.SetTCPOptions(conn.(*net.TCPConn))
			if err != nil {
				l.Infoln(err)
			}

			var tc *tls.Conn

			if inv.ServerSocket {
				tc = tls.Server(conn, r.tlsCfg)
			} else {
				tc = tls.Client(conn, r.tlsCfg)
			}
			err = tc.Handshake()
			if err != nil {
				l.Infof("TLS handshake (BEP/relay %s): %v", inv, err)
				tc.Close()
				continue
			}
			r.conns <- model.IntermediateConnection{
				tc, model.ConnectionTypeRelayAccept,
			}

		case <-r.stop:
			return
		}
	}
}

func (r *invitationReceiver) Stop() {
	close(r.stop)
}

// This is the announcement recieved from the relay server;
// {"relays": [{"url": "relay://10.20.30.40:5060"}, ...]}
type dynamicAnnouncement struct {
	Relays []struct {
		URL string
	}
}
<commit_msg>Fix crash when relaying or global discovery is disabled (fixes #2246)<commit_after>// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package relay

import (
	"crypto/tls"
	"encoding/json"
	"net"
	"net/http"
	"net/url"
	"time"

	"github.com/syncthing/relaysrv/client"
	"github.com/syncthing/relaysrv/protocol"
	"github.com/syncthing/syncthing/lib/config"
	"github.com/syncthing/syncthing/lib/discover"
	"github.com/syncthing/syncthing/lib/model"
	"github.com/syncthing/syncthing/lib/osutil"
	"github.com/syncthing/syncthing/lib/sync"

	"github.com/thejerf/suture"
)

func NewSvc(cfg *config.Wrapper, tlsCfg *tls.Config, conns chan<- model.IntermediateConnection) *Svc {
	svc := &Svc{
		Supervisor: suture.New("Svc", suture.Spec{
			Log: func(log string) {
				if debug {
					l.Debugln(log)
				}
			},
			FailureBackoff:   5 * time.Minute,
			FailureDecay:     float64((10 * time.Minute) / time.Second),
			FailureThreshold: 5,
		}),
		cfg:    cfg,
		tlsCfg: tlsCfg,

		tokens:  make(map[string]suture.ServiceToken),
		clients: make(map[string]*client.ProtocolClient),
		mut:     sync.NewRWMutex(),

		invitations: make(chan protocol.SessionInvitation),
	}

	rcfg := cfg.Raw()
	svc.CommitConfiguration(rcfg, rcfg)
	cfg.Subscribe(svc)

	receiver := &invitationReceiver{
		tlsCfg:      tlsCfg,
		conns:       conns,
		invitations: svc.invitations,
		stop:        make(chan struct{}),
	}

	svc.Add(receiver)

	return svc
}

type Svc struct {
	*suture.Supervisor
	cfg    *config.Wrapper
	tlsCfg *tls.Config

	tokens      map[string]suture.ServiceToken
	clients     map[string]*client.ProtocolClient
	mut         sync.RWMutex
	invitations chan protocol.SessionInvitation
}

func (s *Svc) VerifyConfiguration(from, to config.Configuration) error {
	for _, addr := range to.Options.RelayServers {
		_, err := url.Parse(addr)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Svc) CommitConfiguration(from, to config.Configuration) bool {
	existing := make(map[string]*url.URL, len(to.Options.RelayServers))

	for _, addr := range to.Options.RelayServers {
		uri, err := url.Parse(addr)
		if err != nil {
			if debug {
				l.Debugln("Failed to parse relay address", addr, err)
			}
			continue
		}
		existing[uri.String()] = uri
	}

	// Query dynamic addresses, and pick the closest relay from the ones they provide.
	for key, uri := range existing {
		if uri.Scheme != "dynamic+http" && uri.Scheme != "dynamic+https" {
			continue
		}
		delete(existing, key)

		// Trim off the `dynamic+` prefix
		uri.Scheme = uri.Scheme[8:]

		if debug {
			l.Debugln("Looking up dynamic relays from", uri)
		}

		data, err := http.Get(uri.String())
		if err != nil {
			if debug {
				l.Debugln("Failed to lookup dynamic relays", err)
			}
			continue
		}

		var ann dynamicAnnouncement
		err = json.NewDecoder(data.Body).Decode(&ann)
		data.Body.Close()
		if err != nil {
			if debug {
				l.Debugln("Failed to lookup dynamic relays", err)
			}
			continue
		}

		dynRelays := make([]discover.Relay, 0, len(ann.Relays))
		for _, relayAnn := range ann.Relays {
			ruri, err := url.Parse(relayAnn.URL)
			if err != nil {
				if debug {
					l.Debugln("Failed to parse dynamic relay address", relayAnn.URL, err)
				}
				continue
			}
			if debug {
				l.Debugln("Found", ruri, "via", uri)
			}
			dynRelays = append(dynRelays, discover.Relay{
				Address: ruri.String(),
			})
		}

		dynRelayAddrs := discover.RelayAddressesSortedByLatency(dynRelays)
		if len(dynRelayAddrs) > 0 {
			closestRelay := dynRelayAddrs[0]
			if debug {
				l.Debugln("Picking", closestRelay, "as closest dynamic relay from", uri)
			}
			ruri, _ := url.Parse(closestRelay)
			existing[closestRelay] = ruri
		} else if debug {
			l.Debugln("No dynamic relay found on", uri)
		}
	}

	s.mut.Lock()

	for key, uri := range existing {
		_, ok := s.tokens[key]
		if !ok {
			if debug {
				l.Debugln("Connecting to relay", uri)
			}
			c := client.NewProtocolClient(uri, s.tlsCfg.Certificates, s.invitations)
			s.tokens[key] = s.Add(c)
			s.clients[key] = c
		}
	}

	for key, token := range s.tokens {
		_, ok := existing[key]
		if !ok {
			err := s.Remove(token)
			delete(s.tokens, key)
			delete(s.clients, key)
			if debug {
				l.Debugln("Disconnecting from relay", key, err)
			}
		}
	}

	s.mut.Unlock()

	return true
}

func (s *Svc) ClientStatus() map[string]bool {
	if s == nil {
		// A nil client does not have a status, really. Yet we may be called
		// this way, for raisins...
		return nil
	}

	s.mut.RLock()
	status := make(map[string]bool, len(s.clients))
	for uri, client := range s.clients {
		status[uri] = client.StatusOK()
	}
	s.mut.RUnlock()
	return status
}

type invitationReceiver struct {
	invitations chan protocol.SessionInvitation
	tlsCfg      *tls.Config
	conns       chan<- model.IntermediateConnection
	stop        chan struct{}
}

func (r *invitationReceiver) Serve() {
	for {
		select {
		case inv := <-r.invitations:
			if debug {
				l.Debugln("Received relay invitation", inv)
			}
			conn, err := client.JoinSession(inv)
			if err != nil {
				if debug {
					l.Debugf("Failed to join relay session %s: %v", inv, err)
				}
				continue
			}

			err = osutil.SetTCPOptions(conn.(*net.TCPConn))
			if err != nil {
				l.Infoln(err)
			}

			var tc *tls.Conn

			if inv.ServerSocket {
				tc = tls.Server(conn, r.tlsCfg)
			} else {
				tc = tls.Client(conn, r.tlsCfg)
			}
			err = tc.Handshake()
			if err != nil {
				l.Infof("TLS handshake (BEP/relay %s): %v", inv, err)
				tc.Close()
				continue
			}
			r.conns <- model.IntermediateConnection{
				tc, model.ConnectionTypeRelayAccept,
			}

		case <-r.stop:
			return
		}
	}
}

func (r *invitationReceiver) Stop() {
	close(r.stop)
}

// This is the announcement recieved from the relay server;
// {"relays": [{"url": "relay://10.20.30.40:5060"}, ...]}
type dynamicAnnouncement struct {
	Relays []struct {
		URL string
	}
}
<|endoftext|>


<commit_before>package obj

import "reflect"

// The missing definition of 'reflect.IsZero' you've always wanted.
//
// This definition always considers structs non-empty
// (checking struct emptiness can be a relatively costly operation, O(size_of_struct);
// all other kinds can define emptiness in constant time).
func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

// zeroishness checks on a struct are possible in theory but we're passing for now.
// There's no easy route exposed from the reflect package, due to a variety of reasons:
//
//   - The `DeepEqual` method would almost suffice, but we'd need one that takes
//     `reflect.Value` instead of `interface{}` params, because we already have
//     the former, and un/re-boxing those into two `interface{}` values is two
//     heap mallocs and that's a wildly unacceptable performance overhead for this.
//     `DeepEqual` calls `deepValueEqual`, which does what we want, but...
//   - We can't easily copy the `deepValueEqual` method.  It imports the unsafe
//     package.  This is undesirable because it would reduce the portability of refmt.
//
// It's possible we could produce a struct isZero method which is *simpler* than
// `deepValueEqual`, because we can actually just halt on any non-zero pointer,
// and without any need for following pointers we also need no cycle detection.
// But this is an exercise I'm leaving for later.  PRs welcome if someone wants it.
<commit_msg>add support for checking if a struct is empty<commit_after>package obj

import "reflect"

// The missing definition of 'reflect.IsZero' you've always wanted.
func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	case reflect.Struct:
		// Note: we can't rely on a *literal* "is zero" check because
		// non-zero pointers may still point to *empty* things.
		for i := 0; i < v.NumField(); i++ {
			if !isEmptyValue(v.Field(i)) {
				return false
			}
		}
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package keyseq

import (
	"sync"
	"time"

	"github.com/nsf/termbox-go"
)

// Key is data in one trie node in the KeySequence
type Key struct {
	Modifier int // Alt, etc
	Key      termbox.Key
	Ch       rune
}

func NewKeyFromKey(k termbox.Key) Key {
	return Key{0, k, rune(0)}
}

// KeyList is just the list of keys
type KeyList []Key

func (k Key) Compare(x Key) int {
	if k.Modifier < x.Modifier {
		return -1
	} else if k.Modifier > x.Modifier {
		return 1
	}

	if k.Key < x.Key {
		return -1
	} else if k.Key > x.Key {
		return 1
	}

	if k.Ch < x.Ch {
		return -1
	} else if k.Ch > x.Ch {
		return 1
	}

	return 0
}

func (k KeyList) Equals(x KeyList) bool {
	if len(k) != len(x) {
		return false
	}

	for i := 0; i < len(k); i++ {
		if k[i].Compare(x[i]) != 0 {
			return false
		}
	}
	return true
}

type keyseqMatcher interface {
	Get(Key) Node
	GetList(KeyList) Node
}

type Keyseq struct {
	*Matcher
	current       keyseqMatcher
	mutex         *sync.Mutex
	prevInputTime time.Time
}

func New() *Keyseq {
	return &Keyseq{NewMatcher(), nil, &sync.Mutex{}, time.Time{}}
}

func (k *Keyseq) SetCurrent(m keyseqMatcher) {
	k.current = m
}

func (k *Keyseq) Current() keyseqMatcher {
	if k.current == nil {
		k.current = k.Matcher
	}
	return k.current
}

func (k *Keyseq) AcceptKey(key Key) interface{} {
	k.mutex.Lock()
	defer k.mutex.Unlock()
	defer func() { k.prevInputTime = time.Now() }()
	c := k.Current()
	n := c.Get(key)

	// nothing matched
	if n == nil {
		k.SetCurrent(k.Matcher)
		return nil
	}

	// Matched node has children. It MAY BE a part of a key sequence
	if n.HasChildren() {
		// Did we get this input in succession, i.e. withing 200 msecs?
		// if yes, we may need to check for key sequence
		if time.Since(k.prevInputTime) <= time.Second {
			k.SetCurrent(n)
			// if this is in the middle of a sequence, nodeData contains
			// nothing. in that case we should just return what the default
			// (top-level) key action would have been
			x := n.Value().(*nodeData).Value()
			if x != nil {
				return x
			}
		}
		n = k.Matcher.Get(key)
		if n == nil {
			// Nothing matched, but we may be in the middle of a sequence,
			// so don't reset the current node
			return nil
		}

		// Something matched, return it
		return n.Value().(*nodeData).Value()
	}

	// If it got here, we should just rest the matcher, and return
	// whatever we matched
	k.SetCurrent(k.Matcher)
	return n.Value().(*nodeData).Value()
}<commit_msg>check n.Value() before doing anything<commit_after>package keyseq

import (
	"sync"
	"time"

	"github.com/nsf/termbox-go"
)

// Key is data in one trie node in the KeySequence
type Key struct {
	Modifier int // Alt, etc
	Key      termbox.Key
	Ch       rune
}

func NewKeyFromKey(k termbox.Key) Key {
	return Key{0, k, rune(0)}
}

// KeyList is just the list of keys
type KeyList []Key

func (k Key) Compare(x Key) int {
	if k.Modifier < x.Modifier {
		return -1
	} else if k.Modifier > x.Modifier {
		return 1
	}

	if k.Key < x.Key {
		return -1
	} else if k.Key > x.Key {
		return 1
	}

	if k.Ch < x.Ch {
		return -1
	} else if k.Ch > x.Ch {
		return 1
	}

	return 0
}

func (k KeyList) Equals(x KeyList) bool {
	if len(k) != len(x) {
		return false
	}

	for i := 0; i < len(k); i++ {
		if k[i].Compare(x[i]) != 0 {
			return false
		}
	}
	return true
}

type keyseqMatcher interface {
	Get(Key) Node
	GetList(KeyList) Node
}

type Keyseq struct {
	*Matcher
	current       keyseqMatcher
	mutex         *sync.Mutex
	prevInputTime time.Time
}

func New() *Keyseq {
	return &Keyseq{NewMatcher(), nil, &sync.Mutex{}, time.Time{}}
}

func (k *Keyseq) SetCurrent(m keyseqMatcher) {
	k.current = m
}

func (k *Keyseq) Current() keyseqMatcher {
	if k.current == nil {
		k.current = k.Matcher
	}
	return k.current
}

func (k *Keyseq) AcceptKey(key Key) interface{} {
	k.mutex.Lock()
	defer k.mutex.Unlock()
	defer func() { k.prevInputTime = time.Now() }()
	c := k.Current()
	n := c.Get(key)

	// nothing matched
	if n == nil {
		k.SetCurrent(k.Matcher)
		return nil
	}

	// Matched node has children. It MAY BE a part of a key sequence
	if n.HasChildren() {
		// Did we get this input in succession, i.e. withing 200 msecs?
		// if yes, we may need to check for key sequence
		if time.Since(k.prevInputTime) <= time.Second {
			k.SetCurrent(n)
			// if this is in the middle of a sequence, nodeData contains
			// nothing. in that case we should just return what the default
			// (top-level) key action would have been
			x := n.Value().(*nodeData).Value()
			if x != nil {
				return x
			}
		}
		n = k.Matcher.Get(key)
		if n == nil || n.Value() == nil {
			// Nothing matched, but we may be in the middle of a sequence,
			// so don't reset the current node
			return nil
		}

		// Something matched, return it
		return n.Value().(*nodeData).Value()
	}

	// If it got here, we should just rest the matcher, and return
	// whatever we matched
	k.SetCurrent(k.Matcher)
	return n.Value().(*nodeData).Value()
}<|endoftext|>


<commit_before>package js

// FunctionsTemplate provides the template for js clients of models
var FunctionsTemplate = `{{$schema := .Schema}}{{$title := $schema.Title}}module.exports.{{.ModuleName}} = {
  // New creates a new local {{ToUpperFirst $title}} js client
  {{ToUpperFirst $title}} = function(){}

  // create validators
  {{ToUpperFirst $title}}.validate = function(data){
    return  null
  }

  // create mapper
  {{ToUpperFirst $title}}.map = function(data){
    return null
  }

{{range $funcKey, $funcValue := $schema.Functions}}
  {{ToUpperFirst $title}}.{{$funcKey}} = function(data, callback) {
    // data should be type of {{Argumentize $funcValue.Properties.incoming}}
{{if Equal "array" $funcValue.Properties.incoming.Type}}{{$incoming := index $funcValue.Properties.incoming.Items 0}}
    for (var i = 0; i < data.length; i++){
      if(err = {{$incoming.Title}}.validate(data[i])) {
        return callback(err, null)
      }
    }
{{else}}
    if(err = {{ToUpperFirst $funcValue.Properties.incoming.Title}}.validate(data)) {
      return callback(err, null)
    }
{{end}}

    // send request to the server
    // we got the response
    var res = {}
    // response should be type of {{Argumentize $funcValue.Properties.outgoing}}x{{if Equal "array" $funcValue.Properties.outgoing.Type}}{{$outgoing := index $funcValue.Properties.outgoing.Items 0}}
    res = res.map(function(datum) {
      return {{ToUpperFirst $outgoing.Title}}.map(datum);
    });{{else}}
    res = {{ToUpperFirst $funcValue.Properties.outgoing.Title}}.map(res){{end}}
    callback(null, res)
  }
{{end}}
}
`
<commit_msg>gene/js: simplify js validator generation<commit_after>package js

// FunctionsTemplate provides the template for js clients of models
var FunctionsTemplate = `{{$schema := .Schema}}{{$title := $schema.Title}}{{$moduleName := .ModuleName}}module.exports.{{$moduleName}} = {
  // New creates a new local {{ToUpperFirst $title}} js client
  {{ToUpperFirst $title}} = function(){}

  // create validators
  {{ToUpperFirst $title}}.validate = function(data){
    return  null
  }

  // create mapper
  {{ToUpperFirst $title}}.map = function(data){
    return null
  }

{{range $funcKey, $funcValue := $schema.Functions}}
  {{ToUpperFirst $title}}.{{$funcKey}} = function(data, callback) {
  {{GenerateJSValidator $funcValue.Properties.incoming}}
    return processRequest('/{{ToLower $title}}/{{ToLower $funcKey}}', data, callback)
  }
{{end}}
}
`
<|endoftext|>


<commit_before>// Package gist7576804 implements TypeChainString to get a full type chain string.
package gist7576804

import (
	"fmt"

	"golang.org/x/tools/go/types"
)

// TypeChainString returns the full type chain as a string.
func TypeChainString(t types.Type) string {
	out := fmt.Sprintf("%s", t)
	for {
		if t == t.Underlying() {
			break
		} else {
			t = t.Underlying()
		}
		out += fmt.Sprintf(" -> %s", t)
	}
	return out
}
<commit_msg>Delete gists/gist7576804 package.<commit_after><|endoftext|>


<commit_before>package api

import (
	"aguin/model"
	"aguin/utils"
	"aguin/validator"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/http"
	"net/url"
	"time"
)

type RequestData struct {
	app       model.Application
	data      url.Values
}

type ApplicationSetting struct {
	dbSession *mgo.Session
	log *log.Logger // General log
}

func serveOK(render render.Render) {
	render.JSON(http.StatusOK, map[string]interface{}{"status": "OK"})
}

func serveForbidden(render render.Render) {
	render.JSON(http.StatusForbidden, map[string]interface{}{"status": "FORBIDDEN"})
}

func serveBadRequestData(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"status": "ERROR", "msg": "Invalid data"})
}

func serveBadRequestJson(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"status": "ERROR", "msg": "Invalid json"})
}

func serveInternalServerError(render render.Render) {
	render.JSON(http.StatusInternalServerError, map[string]interface{}{"status": "ERROR"})
}

func VerifyRequest() interface{} {
	return func(c martini.Context, res http.ResponseWriter, req *http.Request, render render.Render) {
		log := utils.GetLogger("aguin")
		// Recover from panic and serve internal server error in json format
		defer func() {
			if r := recover(); r != nil {
				log.Print(r)
				serveInternalServerError(render)
			}
		}()

		apiKey := req.Header.Get("X-AGUIN-API-KEY")

		if apiKey == "" {
			serveForbidden(render)
			return
		}
		// If empty then data must be encrypted
		apiSecret := req.Header.Get("X-AGUIN-API-SECRET")

		app := model.Application{}

		setting := ApplicationSetting{}
		requestData := RequestData{}
		setting.log = log
		setting.dbSession = model.Session()

		defer setting.dbSession.Close()

		err := model.AppCollection(setting.dbSession).FindId(bson.ObjectIdHex(apiKey)).One(&app)

		log.Print(app, err)

		if err != nil {
			serveForbidden(render)
			return
		}

		requestData.app = app

		// parse form for data
		req.ParseForm()
		// if secret empty it must be crypted
		if apiSecret == "" {
			// decrypting data here

		} else {
			// have secret, compare with the one in database
			if apiSecret != app.Secret {
				serveForbidden(render)
				return
			}
		}
		requestData.data = req.Form
		c.Map(requestData)
		c.Map(setting)
		c.Next()
	}
}

func IndexGet(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting ApplicationSetting) {
	log := setting.log

	var results []model.Entity
	da := []byte(requestData.data.Get("message"))

	data3, err := utils.Bytes2json(&da)
	if err != nil {
		log.Print(err)
		serveBadRequestJson(render)
		return
	}
	criteria := validator.ValidateSearch(data3)
	if criteria.Validated == false {
		serveBadRequestData(render)
		return
	}
	err = model.EntityCollection(setting.dbSession).Find(bson.M{"name": criteria.Entity, "appid": requestData.app.Id}).All(&results)

	render.JSON(http.StatusOK, results)
}

func IndexPost(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting ApplicationSetting) {
	log := setting.log
	da := []byte(requestData.data.Get("message"))
	data3, err := utils.Bytes2json(&da)
	if err != nil {
		log.Print(err)
		serveBadRequestJson(render)
		return
	}
	entity, data, validated := validator.ValidateEntity(data3)
	if validated && entity != "" {
		doc := model.Entity{}
		doc.Name = entity
		doc.AppId = requestData.app.Id
		doc.CreatedAt = time.Now()
		doc.Data = data
		err = doc.Save(setting.dbSession)
		if err == nil {
			serveOK(render)
			return
		} else {
			log.Print(err)
			serveInternalServerError(render)
			return
		}
	}
	serveBadRequestData(render)
}

func IndexStatus(render render.Render) {
	serveOK(render)
}

func NotFound(render render.Render) {
	render.JSON(http.StatusNotFound, map[string]interface{}{"status": "ERROR", "msg": "Not found"})
}
<commit_msg>call AguinSetting instead so not confuse with collection app<commit_after>package api

import (
	"aguin/model"
	"aguin/utils"
	"aguin/validator"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/http"
	"net/url"
	"time"
)

type RequestData struct {
	app       model.Application
	data      url.Values
}

type AguinSetting struct {
	dbSession *mgo.Session
	log *log.Logger // General log
}

func serveOK(render render.Render) {
	render.JSON(http.StatusOK, map[string]interface{}{"status": "OK"})
}

func serveForbidden(render render.Render) {
	render.JSON(http.StatusForbidden, map[string]interface{}{"status": "FORBIDDEN"})
}

func serveBadRequestData(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"status": "ERROR", "msg": "Invalid data"})
}

func serveBadRequestJson(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"status": "ERROR", "msg": "Invalid json"})
}

func serveInternalServerError(render render.Render) {
	render.JSON(http.StatusInternalServerError, map[string]interface{}{"status": "ERROR"})
}

func VerifyRequest() interface{} {
	return func(c martini.Context, res http.ResponseWriter, req *http.Request, render render.Render) {
		log := utils.GetLogger("aguin")
		// Recover from panic and serve internal server error in json format
		defer func() {
			if r := recover(); r != nil {
				log.Print(r)
				serveInternalServerError(render)
			}
		}()

		apiKey := req.Header.Get("X-AGUIN-API-KEY")

		if apiKey == "" {
			serveForbidden(render)
			return
		}
		// If empty then data must be encrypted
		apiSecret := req.Header.Get("X-AGUIN-API-SECRET")

		app := model.Application{}

		setting := AguinSetting{}
		requestData := RequestData{}
		setting.log = log
		setting.dbSession = model.Session()

		defer setting.dbSession.Close()

		err := model.AppCollection(setting.dbSession).FindId(bson.ObjectIdHex(apiKey)).One(&app)

		log.Print(app, err)

		if err != nil {
			serveForbidden(render)
			return
		}

		requestData.app = app

		// parse form for data
		req.ParseForm()
		// if secret empty it must be crypted
		if apiSecret == "" {
			// decrypting data here

		} else {
			// have secret, compare with the one in database
			if apiSecret != app.Secret {
				serveForbidden(render)
				return
			}
		}
		requestData.data = req.Form
		c.Map(requestData)
		c.Map(setting)
		c.Next()
	}
}

func IndexGet(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting AguinSetting) {
	log := setting.log

	var results []model.Entity
	da := []byte(requestData.data.Get("message"))

	data3, err := utils.Bytes2json(&da)
	if err != nil {
		log.Print(err)
		serveBadRequestJson(render)
		return
	}
	criteria := validator.ValidateSearch(data3)
	if criteria.Validated == false {
		serveBadRequestData(render)
		return
	}
	err = model.EntityCollection(setting.dbSession).Find(bson.M{"name": criteria.Entity, "appid": requestData.app.Id}).All(&results)

	render.JSON(http.StatusOK, results)
}

func IndexPost(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting AguinSetting) {
	log := setting.log
	da := []byte(requestData.data.Get("message"))
	data3, err := utils.Bytes2json(&da)
	if err != nil {
		log.Print(err)
		serveBadRequestJson(render)
		return
	}
	entity, data, validated := validator.ValidateEntity(data3)
	if validated && entity != "" {
		doc := model.Entity{}
		doc.Name = entity
		doc.AppId = requestData.app.Id
		doc.CreatedAt = time.Now()
		doc.Data = data
		err = doc.Save(setting.dbSession)
		if err == nil {
			serveOK(render)
			return
		} else {
			log.Print(err)
			serveInternalServerError(render)
			return
		}
	}
	serveBadRequestData(render)
}

func IndexStatus(render render.Render) {
	serveOK(render)
}

func NotFound(render render.Render) {
	render.JSON(http.StatusNotFound, map[string]interface{}{"status": "ERROR", "msg": "Not found"})
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "encoding/hex"
    //"runtime"
    "time"
    "github.com/fuzxxl/nfc/2.0/nfc"
    "github.com/fuzxxl/freefare/0.3/freefare"
    "./keydiversification"
    "./helpers"
)

// Use structs to pass data around so I can refactor
type AppInfo struct {
    aid freefare.DESFireAid
    aidbytes []byte
    sysid []byte
    acl_read_base []byte
    acl_write_base []byte
    acl_file_id byte
}

type KeyChain struct {
    uid_read_key_id byte
    acl_read_key_id byte
    acl_write_key_id byte

    uid_read_key *freefare.DESFireKey
    acl_read_key *freefare.DESFireKey
    acl_write_key *freefare.DESFireKey
}

var (
    keychain = KeyChain{}
    appinfo = AppInfo{}
)

func init_appinfo() {
    keymap, err := helpers.LoadYAMLFile("keys.yaml")
    if err != nil {
        panic(err)
    }

    appmap, err := helpers.LoadYAMLFile("apps.yaml")
    if err != nil {
        panic(err)
    }

    // Application-id
    appinfo.aid, err = helpers.String2aid(appmap["hacklab_acl"].(map[interface{}]interface{})["aid"].(string))
    if err != nil {
        panic(err)
    }

    // Needed for diversification
    appinfo.aidbytes = helpers.Aid2bytes(appinfo.aid)
    appinfo.sysid, err = hex.DecodeString(appmap["hacklab_acl"].(map[interface{}]interface{})["sysid"].(string))
    if err != nil {
        panic(err)
    }

    appinfo.acl_file_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["acl_file_id"].(string))
    if err != nil {
        panic(err)
    }

    // Key id numbers from config
    keychain.uid_read_key_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["uid_read_key_id"].(string))
    if err != nil {
        panic(err)
    }
    keychain.acl_read_key_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["acl_read_key_id"].(string))
    if err != nil {
        panic(err)
    }
    keychain.acl_write_key_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["acl_write_key_id"].(string))
    if err != nil {
        panic(err)
    }

    // The static app key to read UID
    keychain.uid_read_key, err = helpers.String2aeskey(keymap["uid_read_key"].(string))
    if err != nil {
        panic(err)
    }

    // Bases for the diversified keys
    appinfo.acl_read_base, err = hex.DecodeString(keymap["acl_read_key"].(string))
    if err != nil {
        panic(err)
    }
    appinfo.acl_write_base, err = hex.DecodeString(keymap["acl_write_key"].(string))
    if err != nil {
        panic(err)
    }

}

func recalculate_diversified_keys(realuid []byte) error {
    acl_read_bytes, err := keydiversification.AES128(appinfo.acl_read_base[:], appinfo.aidbytes[:], realuid[:], appinfo.sysid[:])
    if err != nil {
        return err
    }
    acl_write_bytes, err := keydiversification.AES128(appinfo.acl_write_base[:], appinfo.aidbytes[:], realuid[:], appinfo.sysid[:])
    if err != nil {
        return err
    }
    keychain.acl_read_key = helpers.Bytes2aeskey(acl_read_bytes)
    keychain.acl_write_key = helpers.Bytes2aeskey(acl_write_bytes)
    return nil
}

func handle_tag(desfiretag *freefare.DESFireTag) {
    desfiretag.Connect()

    uid_str := desfiretag.UID()

    fmt.Printf("Found tag %s\n", uid_str)

    fmt.Println("Selecting application");
    err := desfiretag.SelectApplication(appinfo.aid);
    if err != nil {
        panic(err)
    }
    fmt.Println("Done");

    fmt.Println("Authenticating");
    err = desfiretag.Authenticate(keychain.uid_read_key_id,*keychain.uid_read_key)
    if err != nil {
        panic(err)
    }
    fmt.Println("Done");

    realuid_str, err := desfiretag.CardUID()
    if err != nil {
        panic(err)
    }

    realuid, err := hex.DecodeString(realuid_str)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed to parse real UID (%s), skipping tag", err))
        return
    }
    fmt.Println("Got UID: ", hex.EncodeToString(realuid));

    // Calculate the diversified keys
    err = recalculate_diversified_keys(realuid)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed to get diversified ACL keys (%s), skipping tag", err))
        return
    }

    fmt.Println("Got real ACL read key: ", keychain.acl_read_key)

    fmt.Print("Re-auth with ACL read key, ")
    err = desfiretag.Authenticate(keychain.acl_read_key_id,*keychain.acl_read_key)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed (%s), skipping tag", err))
        return
    }
    fmt.Println("Done")

    desfiretag.Disconnect()
}

func main() {

    init_appinfo();

    d, err := nfc.Open("");
    if err != nil {
        panic(err)
    }

    for {
        var tags []freefare.Tag
        for {
            tags, err = freefare.GetTags(d);
            if err != nil {
                continue
            }
            if len(tags) > 0 {
                break
            }
            time.Sleep(100 * time.Millisecond)
            //fmt.Println("...polling")
        }

        for i := 0; i < len(tags); i++ {
            tag := tags[i]
            desfiretag := tag.(freefare.DESFireTag)
            handle_tag(&desfiretag)
        }
        //runtime.GC()
    }

}

<commit_msg>NOW it crashes<commit_after>package main

import (
    "fmt"
    "encoding/hex"
    //"runtime"
    "time"
    "encoding/binary"
    "github.com/fuzxxl/nfc/2.0/nfc"
    "github.com/fuzxxl/freefare/0.3/freefare"
    "./keydiversification"
    "./helpers"
)

// Use structs to pass data around so I can refactor
type AppInfo struct {
    aid freefare.DESFireAid
    aidbytes []byte
    sysid []byte
    acl_read_base []byte
    acl_write_base []byte
    acl_file_id byte
}

type KeyChain struct {
    uid_read_key_id byte
    acl_read_key_id byte
    acl_write_key_id byte

    uid_read_key *freefare.DESFireKey
    acl_read_key *freefare.DESFireKey
    acl_write_key *freefare.DESFireKey
}

var (
    keychain = KeyChain{}
    appinfo = AppInfo{}
)

func init_appinfo() {
    keymap, err := helpers.LoadYAMLFile("keys.yaml")
    if err != nil {
        panic(err)
    }

    appmap, err := helpers.LoadYAMLFile("apps.yaml")
    if err != nil {
        panic(err)
    }

    // Application-id
    appinfo.aid, err = helpers.String2aid(appmap["hacklab_acl"].(map[interface{}]interface{})["aid"].(string))
    if err != nil {
        panic(err)
    }

    // Needed for diversification
    appinfo.aidbytes = helpers.Aid2bytes(appinfo.aid)
    appinfo.sysid, err = hex.DecodeString(appmap["hacklab_acl"].(map[interface{}]interface{})["sysid"].(string))
    if err != nil {
        panic(err)
    }

    appinfo.acl_file_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["acl_file_id"].(string))
    if err != nil {
        panic(err)
    }

    // Key id numbers from config
    keychain.uid_read_key_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["uid_read_key_id"].(string))
    if err != nil {
        panic(err)
    }
    keychain.acl_read_key_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["acl_read_key_id"].(string))
    if err != nil {
        panic(err)
    }
    keychain.acl_write_key_id, err = helpers.String2byte(appmap["hacklab_acl"].(map[interface{}]interface{})["acl_write_key_id"].(string))
    if err != nil {
        panic(err)
    }

    // The static app key to read UID
    keychain.uid_read_key, err = helpers.String2aeskey(keymap["uid_read_key"].(string))
    if err != nil {
        panic(err)
    }

    // Bases for the diversified keys
    appinfo.acl_read_base, err = hex.DecodeString(keymap["acl_read_key"].(string))
    if err != nil {
        panic(err)
    }
    appinfo.acl_write_base, err = hex.DecodeString(keymap["acl_write_key"].(string))
    if err != nil {
        panic(err)
    }

}

func recalculate_diversified_keys(realuid []byte) error {
    acl_read_bytes, err := keydiversification.AES128(appinfo.acl_read_base[:], appinfo.aidbytes[:], realuid[:], appinfo.sysid[:])
    if err != nil {
        return err
    }
    acl_write_bytes, err := keydiversification.AES128(appinfo.acl_write_base[:], appinfo.aidbytes[:], realuid[:], appinfo.sysid[:])
    if err != nil {
        return err
    }
    keychain.acl_read_key = helpers.Bytes2aeskey(acl_read_bytes)
    keychain.acl_write_key = helpers.Bytes2aeskey(acl_write_bytes)
    return nil
}

func handle_tag(desfiretag *freefare.DESFireTag) {
    desfiretag.Connect()

    uid_str := desfiretag.UID()

    fmt.Printf("Found tag %s\n", uid_str)

    fmt.Println("Selecting application");
    err := desfiretag.SelectApplication(appinfo.aid);
    if err != nil {
        panic(err)
    }
    fmt.Println("Done");

    fmt.Println("Authenticating");
    err = desfiretag.Authenticate(keychain.uid_read_key_id,*keychain.uid_read_key)
    if err != nil {
        panic(err)
    }
    fmt.Println("Done");

    realuid_str, err := desfiretag.CardUID()
    if err != nil {
        panic(err)
    }

    realuid, err := hex.DecodeString(realuid_str)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed to parse real UID (%s), skipping tag", err))
        return
    }
    fmt.Println("Got UID: ", hex.EncodeToString(realuid));

    // Calculate the diversified keys
    err = recalculate_diversified_keys(realuid)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed to get diversified ACL keys (%s), skipping tag", err))
        return
    }

    fmt.Println("Got real ACL read key: ", keychain.acl_read_key)

    fmt.Print("Re-auth with ACL read key, ")
    err = desfiretag.Authenticate(keychain.acl_read_key_id,*keychain.acl_read_key)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed (%s), skipping tag", err))
        return
    }
    fmt.Println("Done")

    aclbytes := make([]byte, 8)
    fmt.Print("Reading ACL data file, ")
    bytesread, err := desfiretag.ReadData(appinfo.acl_file_id, 0, aclbytes)
    if err != nil {
        fmt.Println(fmt.Sprintf("ERROR: Failed (%s), skipping tag", err))
        return
    }
    if (bytesread < 8) {
        fmt.Println(fmt.Sprintf("WARNING: ReadData read %d bytes, 8 expected", bytesread))
    }
    acl, n := binary.Uvarint(aclbytes)
    if n <= 0 {
        fmt.Println(fmt.Sprintf("ERROR: binary.Uvarint returned %d, skipping tag", n))
        return
    }
    fmt.Println("Done")
    fmt.Println("acl:", acl)

    desfiretag.Disconnect()
}

func main() {

    init_appinfo();

    d, err := nfc.Open("");
    if err != nil {
        panic(err)
    }

    for {
        var tags []freefare.Tag
        for {
            tags, err = freefare.GetTags(d);
            if err != nil {
                continue
            }
            if len(tags) > 0 {
                break
            }
            time.Sleep(100 * time.Millisecond)
            //fmt.Println("...polling")
        }

        for i := 0; i < len(tags); i++ {
            tag := tags[i]
            desfiretag := tag.(freefare.DESFireTag)
            handle_tag(&desfiretag)
        }
        //runtime.GC()
    }

}

<|endoftext|>


<commit_before>// package test contains utilities to test topo.Server
// implementations. If you are testing your implementation, you will
// want to call CheckAll in your test method. For an example, look at
// the tests in github.com/youtube/vitess/go/vt/zktopo.
package test

import (
	"fmt"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/youtube/vitess/go/vt/topo"
)

func CheckActions(t *testing.T, ts topo.Server) {
	cell := getLocalCell(t, ts)
	tablet := &topo.Tablet{
		Alias:    topo.TabletAlias{Cell: cell, Uid: 1},
		Hostname: "localhost",
		IPAddr:   "10.11.12.13",
		Portmap: map[string]int{
			"vt":    3333,
			"mysql": 3334,
		},
		Parent:   topo.TabletAlias{},
		Keyspace: "test_keyspace",
		Type:     topo.TYPE_MASTER,
		State:    topo.STATE_READ_WRITE,
		KeyRange: newKeyRange("-10"),
	}
	if err := ts.CreateTablet(tablet); err != nil {
		t.Fatalf("CreateTablet: %v", err)
	}

	actionPath, err := ts.WriteTabletAction(tablet.Alias, "contents1")
	if err != nil {
		t.Fatalf("WriteTabletAction: %v", err)
	}

	interrupted := make(chan struct{}, 1)
	if _, err := ts.WaitForTabletAction(actionPath, time.Second/100, interrupted); err != topo.ErrTimeout {
		t.Errorf("WaitForTabletAction returned %v", err)
	}
	go func() {
		time.Sleep(time.Second / 10)
		close(interrupted)
	}()
	if _, err := ts.WaitForTabletAction(actionPath, time.Second*5, interrupted); err != topo.ErrInterrupted {
		t.Errorf("WaitForTabletAction returned %v", err)
	}

	// wait for the result in one thread
	wg1 := sync.WaitGroup{}
	wg1.Add(1)
	go func() {
		defer wg1.Done()
		interrupted := make(chan struct{}, 1)
		var err error
		for i := 0; i <= 30; i++ {
			var result string
			result, err = ts.WaitForTabletAction(actionPath, time.Second, interrupted)
			if err == topo.ErrTimeout {
				// we waited for one second, didn't see the
				// result, try again up to 30 seconds.
				t.Logf("WaitForTabletAction timed out at try %v/30", i)
				continue
			}
			if err != nil {
				t.Errorf("WaitForTabletAction returned: %v", err)
			}
			if result != "contents3" {
				t.Errorf("WaitForTabletAction returned bad result: %v", result)
			}
			return
		}
		t.Errorf("WaitForTabletAction timed out: %v", err)
	}()

	// process the action in another thread
	done := make(chan struct{}, 1)
	wg2 := sync.WaitGroup{}
	wg2.Add(1)
	go ts.ActionEventLoop(tablet.Alias, func(ap, data string) error {
		// the actionPath sent back to the action processor
		// is the exact one we have in normal cases,
		// but for the tee, we add extra information.
		if ap != actionPath && !strings.HasSuffix(ap, actionPath) {
			t.Errorf("Bad action path: %v", ap)
		}
		if data != "contents1" {
			t.Errorf("Bad data: %v", data)
		}
		ta, contents, version, err := ts.ReadTabletActionPath(ap)
		if err != nil {
			t.Errorf("Error from ReadTabletActionPath: %v", err)
		}
		if contents != data {
			t.Errorf("Bad contents: %v", contents)
		}
		if ta != tablet.Alias {
			t.Errorf("Bad tablet alias: %v", ta)
		}

		if err := ts.UpdateTabletAction(ap, "contents2", version); err != nil {
			t.Errorf("UpdateTabletAction failed: %v", err)
		}
		if err := ts.StoreTabletActionResponse(ap, "contents3"); err != nil {
			t.Errorf("StoreTabletActionResponse failed: %v", err)
		}
		if err := ts.UnblockTabletAction(ap); err != nil {
			t.Errorf("UnblockTabletAction failed: %v", err)
		}

		wg2.Done()
		return nil
	}, done)

	// first wait for the processing to be done, then close the
	// action loop, then wait for the response to be received.
	wg2.Wait()
	close(done)
	wg1.Wait()

	// start an action, and try to purge all actions, to test
	// PurgeTabletActions
	actionPath, err = ts.WriteTabletAction(tablet.Alias, "contents2")
	if err != nil {
		t.Fatalf("WriteTabletAction(contents2): %v", err)
	}
	if err := ts.PurgeTabletActions(tablet.Alias, func(data string) bool {
		return true
	}); err != nil {
		t.Fatalf("PurgeTabletActions(contents2) failed: %v", err)
	}
	if _, _, _, err := ts.ReadTabletActionPath(actionPath); err != topo.ErrNoNode {
		t.Fatalf("ReadTabletActionPath(contents2) should have failed with ErrNoNode: %v", err)
	}
}

func CheckLotsOfActions(t *testing.T, ts topo.Server) {
	cell := getLocalCell(t, ts)
	tablet := &topo.Tablet{
		Alias:    topo.TabletAlias{Cell: cell, Uid: 1},
		Hostname: "localhost",
		IPAddr:   "10.11.12.13",
		Portmap: map[string]int{
			"vt":    3333,
			"mysql": 3334,
		},
		Parent:   topo.TabletAlias{},
		Keyspace: "test_keyspace",
		Type:     topo.TYPE_MASTER,
		State:    topo.STATE_READ_WRITE,
		KeyRange: newKeyRange("-10"),
	}
	if err := ts.CreateTablet(tablet); err != nil {
		t.Fatalf("CreateTablet: %v", err)
	}

	nActions := 100
	paths := make([]string, nActions)

	// write the actions in parallel, save the paths
	writeWG := sync.WaitGroup{}
	for i := 0; i < nActions; i++ {
		writeWG.Add(1)
		go func(i int) {
			var err error
			paths[i], err = ts.WriteTabletAction(tablet.Alias, fmt.Sprintf("contents %v", i))
			if err != nil {
				t.Fatalf("WriteTabletAction(%v): %v", i, err)
			}
			t.Logf("WriteTabletAction(%v) done", i)
			writeWG.Done()
		}(i)
	}
	writeWG.Wait()

	// wait for all actions in parallel
	waitWG := sync.WaitGroup{}
	interrupted := make(chan struct{}, 1)
	for i := 0; i < nActions; i++ {
		waitWG.Add(1)
		go func(i int) {
			if _, err := ts.WaitForTabletAction(paths[i], time.Second*30, interrupted); err != nil {
				t.Fatalf("WaitForTabletAction returned %v", err)
			}
			t.Logf("WaitForTabletAction(%v) done", i)
			waitWG.Done()
		}(i)
	}

	// and unblock them all in parallel
	unblockWG := sync.WaitGroup{}
	for i := 0; i < nActions; i++ {
		unblockWG.Add(1)
		go func(i int) {
			defer unblockWG.Done()
			ta, contents, _, err := ts.ReadTabletActionPath(paths[i])
			if err != nil {
				t.Errorf("Error from ReadTabletActionPath: %v", err)
			}
			t.Logf("ReadTabletActionPath(%v) done", i)
			if contents != fmt.Sprintf("contents %v", i) {
				t.Errorf("Bad contents: %v", contents)
			}
			if ta != tablet.Alias {
				t.Errorf("Bad tablet alias: %v", ta)
			}

			if err := ts.StoreTabletActionResponse(paths[i], fmt.Sprintf("contents after %v", i)); err != nil {
				t.Errorf("StoreTabletActionResponse failed: %v", err)
			}
			t.Logf("StoreTabletActionResponse(%v) done", i)
			if err := ts.UnblockTabletAction(paths[i]); err != nil {
				t.Errorf("UnblockTabletAction failed: %v", err)
			}
			t.Logf("UnblockTabletAction(%v) done", i)
		}(i)
	}
	unblockWG.Wait()

	waitWG.Wait()
}
<commit_msg>Lowering the number of concurrent aciton nodes.<commit_after>// package test contains utilities to test topo.Server
// implementations. If you are testing your implementation, you will
// want to call CheckAll in your test method. For an example, look at
// the tests in github.com/youtube/vitess/go/vt/zktopo.
package test

import (
	"fmt"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/youtube/vitess/go/vt/topo"
)

func CheckActions(t *testing.T, ts topo.Server) {
	cell := getLocalCell(t, ts)
	tablet := &topo.Tablet{
		Alias:    topo.TabletAlias{Cell: cell, Uid: 1},
		Hostname: "localhost",
		IPAddr:   "10.11.12.13",
		Portmap: map[string]int{
			"vt":    3333,
			"mysql": 3334,
		},
		Parent:   topo.TabletAlias{},
		Keyspace: "test_keyspace",
		Type:     topo.TYPE_MASTER,
		State:    topo.STATE_READ_WRITE,
		KeyRange: newKeyRange("-10"),
	}
	if err := ts.CreateTablet(tablet); err != nil {
		t.Fatalf("CreateTablet: %v", err)
	}

	actionPath, err := ts.WriteTabletAction(tablet.Alias, "contents1")
	if err != nil {
		t.Fatalf("WriteTabletAction: %v", err)
	}

	interrupted := make(chan struct{}, 1)
	if _, err := ts.WaitForTabletAction(actionPath, time.Second/100, interrupted); err != topo.ErrTimeout {
		t.Errorf("WaitForTabletAction returned %v", err)
	}
	go func() {
		time.Sleep(time.Second / 10)
		close(interrupted)
	}()
	if _, err := ts.WaitForTabletAction(actionPath, time.Second*5, interrupted); err != topo.ErrInterrupted {
		t.Errorf("WaitForTabletAction returned %v", err)
	}

	// wait for the result in one thread
	wg1 := sync.WaitGroup{}
	wg1.Add(1)
	go func() {
		defer wg1.Done()
		interrupted := make(chan struct{}, 1)
		var err error
		for i := 0; i <= 30; i++ {
			var result string
			result, err = ts.WaitForTabletAction(actionPath, time.Second, interrupted)
			if err == topo.ErrTimeout {
				// we waited for one second, didn't see the
				// result, try again up to 30 seconds.
				t.Logf("WaitForTabletAction timed out at try %v/30", i)
				continue
			}
			if err != nil {
				t.Errorf("WaitForTabletAction returned: %v", err)
			}
			if result != "contents3" {
				t.Errorf("WaitForTabletAction returned bad result: %v", result)
			}
			return
		}
		t.Errorf("WaitForTabletAction timed out: %v", err)
	}()

	// process the action in another thread
	done := make(chan struct{}, 1)
	wg2 := sync.WaitGroup{}
	wg2.Add(1)
	go ts.ActionEventLoop(tablet.Alias, func(ap, data string) error {
		// the actionPath sent back to the action processor
		// is the exact one we have in normal cases,
		// but for the tee, we add extra information.
		if ap != actionPath && !strings.HasSuffix(ap, actionPath) {
			t.Errorf("Bad action path: %v", ap)
		}
		if data != "contents1" {
			t.Errorf("Bad data: %v", data)
		}
		ta, contents, version, err := ts.ReadTabletActionPath(ap)
		if err != nil {
			t.Errorf("Error from ReadTabletActionPath: %v", err)
		}
		if contents != data {
			t.Errorf("Bad contents: %v", contents)
		}
		if ta != tablet.Alias {
			t.Errorf("Bad tablet alias: %v", ta)
		}

		if err := ts.UpdateTabletAction(ap, "contents2", version); err != nil {
			t.Errorf("UpdateTabletAction failed: %v", err)
		}
		if err := ts.StoreTabletActionResponse(ap, "contents3"); err != nil {
			t.Errorf("StoreTabletActionResponse failed: %v", err)
		}
		if err := ts.UnblockTabletAction(ap); err != nil {
			t.Errorf("UnblockTabletAction failed: %v", err)
		}

		wg2.Done()
		return nil
	}, done)

	// first wait for the processing to be done, then close the
	// action loop, then wait for the response to be received.
	wg2.Wait()
	close(done)
	wg1.Wait()

	// start an action, and try to purge all actions, to test
	// PurgeTabletActions
	actionPath, err = ts.WriteTabletAction(tablet.Alias, "contents2")
	if err != nil {
		t.Fatalf("WriteTabletAction(contents2): %v", err)
	}
	if err := ts.PurgeTabletActions(tablet.Alias, func(data string) bool {
		return true
	}); err != nil {
		t.Fatalf("PurgeTabletActions(contents2) failed: %v", err)
	}
	if _, _, _, err := ts.ReadTabletActionPath(actionPath); err != topo.ErrNoNode {
		t.Fatalf("ReadTabletActionPath(contents2) should have failed with ErrNoNode: %v", err)
	}
}

func CheckLotsOfActions(t *testing.T, ts topo.Server) {
	cell := getLocalCell(t, ts)
	tablet := &topo.Tablet{
		Alias:    topo.TabletAlias{Cell: cell, Uid: 1},
		Hostname: "localhost",
		IPAddr:   "10.11.12.13",
		Portmap: map[string]int{
			"vt":    3333,
			"mysql": 3334,
		},
		Parent:   topo.TabletAlias{},
		Keyspace: "test_keyspace",
		Type:     topo.TYPE_MASTER,
		State:    topo.STATE_READ_WRITE,
		KeyRange: newKeyRange("-10"),
	}
	if err := ts.CreateTablet(tablet); err != nil {
		t.Fatalf("CreateTablet: %v", err)
	}

	nActions := 64
	paths := make([]string, nActions)

	// write the actions in parallel, save the paths
	writeWG := sync.WaitGroup{}
	for i := 0; i < nActions; i++ {
		writeWG.Add(1)
		go func(i int) {
			var err error
			paths[i], err = ts.WriteTabletAction(tablet.Alias, fmt.Sprintf("contents %v", i))
			if err != nil {
				t.Fatalf("WriteTabletAction(%v): %v", i, err)
			}
			t.Logf("WriteTabletAction(%v) done", i)
			writeWG.Done()
		}(i)
	}
	writeWG.Wait()

	// wait for all actions in parallel
	waitWG := sync.WaitGroup{}
	interrupted := make(chan struct{}, 1)
	for i := 0; i < nActions; i++ {
		waitWG.Add(1)
		go func(i int) {
			if _, err := ts.WaitForTabletAction(paths[i], time.Second*30, interrupted); err != nil {
				t.Fatalf("WaitForTabletAction returned %v", err)
			}
			t.Logf("WaitForTabletAction(%v) done", i)
			waitWG.Done()
		}(i)
	}

	// and unblock them all in parallel
	unblockWG := sync.WaitGroup{}
	for i := 0; i < nActions; i++ {
		unblockWG.Add(1)
		go func(i int) {
			defer unblockWG.Done()
			ta, contents, _, err := ts.ReadTabletActionPath(paths[i])
			if err != nil {
				t.Errorf("Error from ReadTabletActionPath: %v", err)
			}
			t.Logf("ReadTabletActionPath(%v) done", i)
			if contents != fmt.Sprintf("contents %v", i) {
				t.Errorf("Bad contents: %v", contents)
			}
			if ta != tablet.Alias {
				t.Errorf("Bad tablet alias: %v", ta)
			}

			if err := ts.StoreTabletActionResponse(paths[i], fmt.Sprintf("contents after %v", i)); err != nil {
				t.Errorf("StoreTabletActionResponse failed: %v", err)
			}
			t.Logf("StoreTabletActionResponse(%v) done", i)
			if err := ts.UnblockTabletAction(paths[i]); err != nil {
				t.Errorf("UnblockTabletAction failed: %v", err)
			}
			t.Logf("UnblockTabletAction(%v) done", i)
		}(i)
	}
	unblockWG.Wait()

	waitWG.Wait()
}
<|endoftext|>


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

/*
Package topotools contains high level functions based on vt/topo and
vt/actionnode. It should not depend on anything else that's higher
level. In particular, it cannot depend on:
- vt/wrangler: much higher level, wrangler depends on topotools.
- vt/tabletmanager/initiator: we don't want the various remote
  protocol dependencies here.

topotools is used by wrangler, so it ends up in all tools using
wrangler (vtctl, vtctld, ...). It is also included by vttablet, so it contains:
- most of the logic to rebuild a shard serving graph (helthcheck module)
- some of the logic to perform a TabletExternallyReparented (RPC call
  to master vttablet to let it know it's the master).

*/
package topotools

// This file contains utility functions for tablets

import (
	"fmt"

	"golang.org/x/net/context"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/vt/hook"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/topo/topoproto"

	pb "github.com/youtube/vitess/go/vt/proto/topodata"
)

// ConfigureTabletHook configures the right parameters for a hook
// running locally on a tablet.
func ConfigureTabletHook(hk *hook.Hook, tabletAlias *pb.TabletAlias) {
	if hk.ExtraEnv == nil {
		hk.ExtraEnv = make(map[string]string, 1)
	}
	hk.ExtraEnv["TABLET_ALIAS"] = topoproto.TabletAliasString(tabletAlias)
}

// Scrap will update the tablet type to 'Scrap', and remove it from
// the serving graph.
//
// 'force' means we are not on the tablet being scrapped, so it is
// probably dead. So if 'force' is true, we will also remove pending
// remote actions.  And if 'force' is false, we also run an optional
// hook.
func Scrap(ctx context.Context, ts topo.Server, tabletAlias *pb.TabletAlias, force bool) error {
	// Update the tablet first, since that is canonical.
	var wasAssigned bool
	var tablet *pb.Tablet
	err := ts.UpdateTabletFields(ctx, tabletAlias, func(t *pb.Tablet) error {
		wasAssigned = topoproto.TabletIsAssigned(t)
		t.Type = pb.TabletType_SCRAP
		tablet = t
		return nil
	})
	if err != nil {
		return err
	}

	// If you are already scrap, skip updating replication data. It won't
	// be there anyway.
	if wasAssigned {
		err = topo.DeleteTabletReplicationData(ctx, ts, tablet)
		if err != nil {
			if err == topo.ErrNoNode {
				log.V(6).Infof("no ShardReplication object for cell %v", tablet.Alias.Cell)
				err = nil
			}
			if err != nil {
				log.Warningf("remove replication data for %v failed: %v", tablet.Alias, err)
			}
		}
	}

	// run a hook for final cleanup, only in non-force mode.
	// (force mode executes on the vtctl side, not on the vttablet side)
	if !force {
		hk := hook.NewSimpleHook("postflight_scrap")
		ConfigureTabletHook(hk, tablet.Alias)
		if hookErr := hk.ExecuteOptional(); hookErr != nil {
			// we don't want to return an error, the server
			// is already in bad shape probably.
			log.Warningf("Scrap: postflight_scrap failed: %v", hookErr)
		}
	}

	return nil
}

// ChangeType changes the type of the tablet and possibly also updates
// the health informaton for it. Make this external, since these
// transitions need to be forced from time to time.
//
// - if health is nil, we don't touch the Tablet's Health record.
// - if health is an empty map, we clear the Tablet's Health record.
// - if health has values, we overwrite the Tablet's Health record.
func ChangeType(ctx context.Context, ts topo.Server, tabletAlias *pb.TabletAlias, newType pb.TabletType, health map[string]string) error {
	tablet, err := ts.GetTablet(ctx, tabletAlias)
	if err != nil {
		return err
	}

	if !topo.IsTrivialTypeChange(tablet.Type, newType) {
		return fmt.Errorf("cannot change tablet type %v -> %v %v", tablet.Type, newType, tabletAlias)
	}

	tablet.Type = newType
	if newType == pb.TabletType_IDLE {
		tablet.Keyspace = ""
		tablet.Shard = ""
		tablet.KeyRange = nil
		tablet.HealthMap = health
	}
	if health != nil {
		if len(health) == 0 {
			tablet.HealthMap = nil
		} else {
			tablet.HealthMap = health
		}
	}
	return ts.UpdateTablet(ctx, tablet)
}
<commit_msg>topotools.ChangeType: Retry on bad node version.<commit_after>// Copyright 2014, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package topotools contains high level functions based on vt/topo and
vt/actionnode. It should not depend on anything else that's higher
level. In particular, it cannot depend on:
- vt/wrangler: much higher level, wrangler depends on topotools.
- vt/tabletmanager/initiator: we don't want the various remote
  protocol dependencies here.

topotools is used by wrangler, so it ends up in all tools using
wrangler (vtctl, vtctld, ...). It is also included by vttablet, so it contains:
- most of the logic to rebuild a shard serving graph (helthcheck module)
- some of the logic to perform a TabletExternallyReparented (RPC call
  to master vttablet to let it know it's the master).

*/
package topotools

// This file contains utility functions for tablets

import (
	"fmt"

	"golang.org/x/net/context"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/vt/hook"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/topo/topoproto"

	pb "github.com/youtube/vitess/go/vt/proto/topodata"
)

// ConfigureTabletHook configures the right parameters for a hook
// running locally on a tablet.
func ConfigureTabletHook(hk *hook.Hook, tabletAlias *pb.TabletAlias) {
	if hk.ExtraEnv == nil {
		hk.ExtraEnv = make(map[string]string, 1)
	}
	hk.ExtraEnv["TABLET_ALIAS"] = topoproto.TabletAliasString(tabletAlias)
}

// Scrap will update the tablet type to 'Scrap', and remove it from
// the serving graph.
//
// 'force' means we are not on the tablet being scrapped, so it is
// probably dead. So if 'force' is true, we will also remove pending
// remote actions.  And if 'force' is false, we also run an optional
// hook.
func Scrap(ctx context.Context, ts topo.Server, tabletAlias *pb.TabletAlias, force bool) error {
	// Update the tablet first, since that is canonical.
	var wasAssigned bool
	var tablet *pb.Tablet
	err := ts.UpdateTabletFields(ctx, tabletAlias, func(t *pb.Tablet) error {
		wasAssigned = topoproto.TabletIsAssigned(t)
		t.Type = pb.TabletType_SCRAP
		tablet = t
		return nil
	})
	if err != nil {
		return err
	}

	// If you are already scrap, skip updating replication data. It won't
	// be there anyway.
	if wasAssigned {
		err = topo.DeleteTabletReplicationData(ctx, ts, tablet)
		if err != nil {
			if err == topo.ErrNoNode {
				log.V(6).Infof("no ShardReplication object for cell %v", tablet.Alias.Cell)
				err = nil
			}
			if err != nil {
				log.Warningf("remove replication data for %v failed: %v", tablet.Alias, err)
			}
		}
	}

	// run a hook for final cleanup, only in non-force mode.
	// (force mode executes on the vtctl side, not on the vttablet side)
	if !force {
		hk := hook.NewSimpleHook("postflight_scrap")
		ConfigureTabletHook(hk, tablet.Alias)
		if hookErr := hk.ExecuteOptional(); hookErr != nil {
			// we don't want to return an error, the server
			// is already in bad shape probably.
			log.Warningf("Scrap: postflight_scrap failed: %v", hookErr)
		}
	}

	return nil
}

// ChangeType changes the type of the tablet and possibly also updates
// the health informaton for it. Make this external, since these
// transitions need to be forced from time to time.
//
// - if health is nil, we don't touch the Tablet's Health record.
// - if health is an empty map, we clear the Tablet's Health record.
// - if health has values, we overwrite the Tablet's Health record.
func ChangeType(ctx context.Context, ts topo.Server, tabletAlias *pb.TabletAlias, newType pb.TabletType, health map[string]string) error {
	return ts.UpdateTabletFields(ctx, tabletAlias, func(tablet *pb.Tablet) error {
		if !topo.IsTrivialTypeChange(tablet.Type, newType) {
			return fmt.Errorf("cannot change tablet type %v -> %v %v", tablet.Type, newType, tabletAlias)
		}

		tablet.Type = newType
		if newType == pb.TabletType_IDLE {
			tablet.Keyspace = ""
			tablet.Shard = ""
			tablet.KeyRange = nil
			tablet.HealthMap = health
		}
		if health != nil {
			if len(health) == 0 {
				tablet.HealthMap = nil
			} else {
				tablet.HealthMap = health
			}
		}
		return nil
	})
}
<|endoftext|>


<commit_before>package authkit

import (
	"html/template"
	"strings"
)

var funcMap = template.FuncMap{
	"join": strings.Join,
}

var loginTemplate = template.Must(template.New("login").Funcs(funcMap).Parse(loginJS))
var loginJS = `
var __authkit__state__ = {
  authCallback : "__authkit__"+parseInt(Math.random()*1e12,10).toString(36),
  providers : {}
};

{{ $base := .Base }}
{{ range $key, $value := .Providers }}
__authkit__state__.providers["{{ $value.Network }}"] = {
  network : "{{ $value.Network }}",
  authurl : "{{ $value.AuthURL }}",
  client_id : "{{ $value.ClientID }}",
  scopes : "{{ join $value.Scopes " " }}",
  redirect: "{{ $base }}redirect"
};
{{ end }}

var authkit = {
  providers : function () {
    return __authkit__state__.providers;
  },
  provider : function (p) {
    return __authkit__state__.providers[p];
  },
  login : function (provider,base) {
    var p = authkit.provider(provider);
    if (!p) return;
    if (!base) base = window.location.origin;
    var scopes = encodeURIComponent(p.scopes);
    var redir = encodeURIComponent(base+p.redirect);
    var state = encodeURIComponent(JSON.stringify({network:p.network,redirect_uri:redir,cbid:__authkit__state__.authCallback}));
    var authU = p.authurl+"?redirect_uri="+redir+"&response_type=code&client_id="+p.client_id+"&state="+state;
    if (p.scopes) {
      authU = authU + "&scope="+scopes;
    }
    var self = this;
    self.user = function (cb) { this.authCB = cb; };
    window[__authkit__state__.authCallback] = function (token, usr) {
      console.log(usr);
      self.authCB(usr, token)
    }
    popup(authU, 500, 600);
    return self;
  }
};

function popup (u, w, h) {
  var documentElement = document.documentElement;

	// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050)
	//   Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
	// Fixes dual-screen position                         Most browsers      Firefox
	var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
	var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

	var width = window.innerWidth || documentElement.clientWidth || screen.width;
	var height = window.innerHeight || documentElement.clientHeight || screen.height;

	var left = ((width - w) / 2) + dualScreenLeft;
	var top  = ((height - h) / 2) + dualScreenTop;
	var feat = "resizeable=true,height=" + h + ",width=" + w + ",left=" + left + ",top="  + top
	window.open(u, "_blank", feat);
}
`
var redirectTemplate = template.Must(template.New("redirect").Funcs(funcMap).Parse(redirect))
var redirect = `
<html>
<body>
  <script>
	  // First, parse the query string
    var params = {}, queryString = location.search.substring(1),
        regex = /([^&=]+)=([^&]*)/g, m;
    while (m = regex.exec(queryString)) {
      params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    }

    var req = new XMLHttpRequest();
    var cbid = JSON.parse(params.state).cbid;
    req.open('GET', '{{ .Base }}auth?code='+params.code+"&state="+params.state, true);

    req.onreadystatechange = function (e) {
      if (req.readyState == 4) {
        if(req.status == 200){
           var tok = req.getResponseHeader("Authorization");
           var usr = JSON.parse(req.responseText);
           window.opener[cbid](tok, usr);
           window.close();
        }
        else if(req.status == 400) {
            console.log('There was an error processing the access code:',req.responseText)
        }
        else {
          console.log('something other than 200 was returned:',req.responseText)
        }
      }
    };
    req.send(null);
	</script>
</body>
</html>
`
<commit_msg>transport error to callback as third parameter<commit_after>package authkit

import (
	"html/template"
	"strings"
)

var funcMap = template.FuncMap{
	"join": strings.Join,
}

var loginTemplate = template.Must(template.New("login").Funcs(funcMap).Parse(loginJS))
var loginJS = `
var __authkit__state__ = {
  authCallback : "__authkit__"+parseInt(Math.random()*1e12,10).toString(36),
  providers : {}
};

{{ $base := .Base }}
{{ range $key, $value := .Providers }}
__authkit__state__.providers["{{ $value.Network }}"] = {
  network : "{{ $value.Network }}",
  authurl : "{{ $value.AuthURL }}",
  client_id : "{{ $value.ClientID }}",
  scopes : "{{ join $value.Scopes " " }}",
  redirect: "{{ $base }}redirect"
};
{{ end }}

var authkit = {
  providers : function () {
    return __authkit__state__.providers;
  },
  provider : function (p) {
    return __authkit__state__.providers[p];
  },
  login : function (provider,base) {
    var p = authkit.provider(provider);
    if (!p) return;
    if (!base) base = window.location.origin;
    var scopes = encodeURIComponent(p.scopes);
    var redir = encodeURIComponent(base+p.redirect);
    var state = encodeURIComponent(JSON.stringify({network:p.network,redirect_uri:redir,cbid:__authkit__state__.authCallback}));
    var authU = p.authurl+"?redirect_uri="+redir+"&response_type=code&client_id="+p.client_id+"&state="+state;
    if (p.scopes) {
      authU = authU + "&scope="+scopes;
    }
    var self = this;
    self.user = function (cb) { this.authCB = cb; };
    window[__authkit__state__.authCallback] = function (token, usr) {
      console.log(usr);
      self.authCB(usr, token)
    }
    popup(authU, 500, 600);
    return self;
  }
};

function popup (u, w, h) {
  var documentElement = document.documentElement;

	// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050)
	//   Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
	// Fixes dual-screen position                         Most browsers      Firefox
	var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
	var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

	var width = window.innerWidth || documentElement.clientWidth || screen.width;
	var height = window.innerHeight || documentElement.clientHeight || screen.height;

	var left = ((width - w) / 2) + dualScreenLeft;
	var top  = ((height - h) / 2) + dualScreenTop;
	var feat = "resizeable=true,height=" + h + ",width=" + w + ",left=" + left + ",top="  + top
	window.open(u, "_blank", feat);
}
`
var redirectTemplate = template.Must(template.New("redirect").Funcs(funcMap).Parse(redirect))
var redirect = `
<html>
<body>
  <script>
	  // First, parse the query string
    var params = {}, queryString = location.search.substring(1),
        regex = /([^&=]+)=([^&]*)/g, m;
    while (m = regex.exec(queryString)) {
      params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    }

    var req = new XMLHttpRequest();
    var cbid = JSON.parse(params.state).cbid;
    req.open('GET', '{{ .Base }}auth?code='+params.code+"&state="+params.state, true);

    req.onreadystatechange = function (e) {
      if (req.readyState == 4) {
        if(req.status == 200){
           var tok = req.getResponseHeader("Authorization");
           var usr = JSON.parse(req.responseText);
           window.opener[cbid](tok, usr);
           window.close();
        }
        else if(req.status == 400) {
            console.log('There was an error processing the access code:',req.responseText)
            window.opener[cbid](null, null, req);
            window.close();
        }
        else {
          console.log('something other than 200 was returned:',req.responseText)
          window.opener[cbid](null, null, req);
          window.close();
        }
      }
    };
    req.send(null);
	</script>
</body>
</html>
`
<|endoftext|>


<commit_before>package main

/*
<form  action="/login">
   User<input type="text"     id="user" name="user">

   Pass<input type="password" id="pass" name="pass">

   Host<input type="text"     id="host" name="host" value="localhost">

   Port<input type="text"     id="port" name="port" value="3306">

   <button type="submit">Query</button>
</form>
*/

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type CContext struct {
	Number    string
	Name      string
	Label     string
	IsNumeric string
	IsString  string
	Value     string
	Readonly  string
}

type FContext struct {
	CSS      string
	Action   string
	Selector string
	Button   string
	Database string
	Table    string
	Key      string
	Value    string
	Back     string
	Columns  []CContext
	Trail    []Entry
}

func shipErrorPage(w http.ResponseWriter, cred Access, db string, t string, cols []CContext) {

	c := FContext{
		CSS:      CSS_FILE,
		Action:   "BACK",
		Selector: "",
		Button:   "Back",
		Database: db,
		Table:    t,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Columns:  cols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateError.Execute(w, c)
	checkY(err)
}

func shipError(w http.ResponseWriter, cred Access, db string, t string, query string, e error) {
	cols := []CContext{CContext{"1", "", "Query", "", "", query, ""}, CContext{"2", "", "Result", "", "", fmt.Sprint(e), ""}}
	shipErrorPage(w, cred, db, t, cols)
}

func shipMessage(w http.ResponseWriter, cred Access, db string, msg string) {
	cols := []CContext{CContext{"1", "", "Message", "", "", msg, ""}}
	shipErrorPage(w, cred, db, "", cols)
}

func shipForm(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string, action string, button string, selector string, fieldmap map[string]string) {

	cols := getColumnInfo(cred, db, t)
	primary := getPrimary(cred, db, t)
	newcols := []CContext{}

	for _, col := range cols {
		label := col.Name
		readonly := ""
		if label == primary {
			label = label + " (ID)"
			readonly = fieldmap[col.Name]
		}
		newcols = append(newcols, CContext{col.Number, col.Name, label, col.IsNumeric, col.IsString, fieldmap[col.Name], readonly})
	}

	q := r.URL.Query()
	q.Del("action")
	linkback := q.Encode()

	c := FContext{
		CSS:      CSS_FILE,
		Action:   action,
		Selector: selector,
		Button:   button,
		Database: db,
		Table:    t,
		Key:      k,
		Value:    v,
		Back:     linkback,
		Columns:  newcols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateFormFields.Execute(w, c)
	checkY(err)
}

func actionSubset(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {
	shipForm(w, r, cred, db, t, "", "", "QUERY", "Query", "true", make(map[string]string) )
}

func actionDelete(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {
	shipForm(w, r, cred, db, t, "","", "DELETEEXEC", "Delete", "true", make(map[string]string) )
}

func actionAdd(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {
	shipForm(w, r, cred, db, t, "","","INSERT", "Insert", "", make(map[string]string) )
}

func actionEdit(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {
	query := "select * from `" + t + "` where `" + k + "` = ?"
	conn := getConnection(cred, db)
	defer conn.Close()
	stmt, err := conn.Prepare(query)
	checkY(err)
	rows, err := stmt.Query(v)
	checkY(err)
	shipForm(w, r, cred, db, t, k, v, "EDITEXEC", "Submit", "", getFieldMap(w, db, t, cred, rows))
}

func collectClauses(r *http.Request, cred Access, db string, t string, set string) []string {

	var clauses []string
	cols := getCols(cred, db, t)
	for _, col := range cols {
		val := sqlProtectString(r.FormValue(col + "C"))
		if val != "" {
			comparator := sqlProtectString(r.FormValue(col + "O"))
			if comparator == "" {
				if set == "" {
					clauses = append(clauses, "`" + col + "`" + sqlProtectNumericComparison(val))
				} else {
					clauses = append(clauses, "`" + col + "` " + set + " \"" + val + "\"")
				}
			} else {
				clauses = append(clauses, "`" + col + "`" + comparator + "\"" + val + "\"")
			}
		}
	}
	return clauses
}

func collectSet(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "="), " , ")
}

func collectWhere(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "")," && ")
}

func actionQuery(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		query := "SELECT * FROM `" + t + "` WHERE " + where
		dumpRows(w, db, t, "", "", cred, query, where)
	}
}

func actionDeleteExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		stmt := "DELETE FROM `" + t + "` WHERE " + where
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		if err != nil {
			shipError(w, cred, db, t, query, err)
		}
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionDeleteWhere(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, where string) {

	stmt := "DELETE FROM `" + t + "` WHERE " + where
	log.Println("[SQL]", stmt)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec()
	if err != nil {
		shipError(w, cred, db, t, query, err)
	}
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
}

func actionInsert(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "INSERT INTO `" + t + "` SET " + clauses
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		if err != nil {
			shipError(w, cred, db, t, query, err)
		}
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionEditExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "UPDATE `" + t + "` SET " + clauses + " WHERE `" + k + "` = ?"
		log.Println("[SQL]", stmt, v)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec(v)
		if err != nil {
			shipError(w, cred, db, t, query, err)
		}
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionRemove(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	stmt := "DELETE FROM `" + t + "` WHERE `" + k + "` = ?"
	log.Println("[SQL]", stmt, v)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, _ := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec(v)
	if err != nil {
		shipError(w, cred, db, t, query, err)
	}
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t+"&k"+k, 302)
}

/*
 show columns from posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| title | varchar(64) | YES  |     | NULL    |       |
| start | date        | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
*/

func actionInfo(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	rows, err := getRows(cred, db, "show columns from `" + t + "`")
	checkY(err)
	defer rows.Close()

	q := url.Values{}
	q.Add("db", db)
	q.Add("t", t)

	menu := []Entry{}
	q.Set("action", "ADD")
	linkinsert := "/?" + q.Encode()
	menu = append(menu, Entry{linkinsert, "+"})

	records := [][]string{}
	head := []string{"#", "Field", "Type", "Null", "Key", "Default", "Extra"}

	var i int = 1
	for rows.Next() {
		var f, t, n, k, e string
		var d []byte // or use http://golang.org/pkg/database/sql/#NullString
		err := rows.Scan(&f, &t, &n, &k, &d, &e)
		checkY(err)
		records = append(records, []string{strconv.Itoa(i), f, t, n, k, string(d), e})
		i = i + 1
	}
	tableOutSimple(w, cred, db, t, head, records, menu)
}
<commit_msg>Bug fixes<commit_after>package main

/*
<form  action="/login">
   User<input type="text"     id="user" name="user">

   Pass<input type="password" id="pass" name="pass">

   Host<input type="text"     id="host" name="host" value="localhost">

   Port<input type="text"     id="port" name="port" value="3306">

   <button type="submit">Query</button>
</form>
*/

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type CContext struct {
	Number    string
	Name      string
	Label     string
	IsNumeric string
	IsString  string
	Value     string
	Readonly  string
}

type FContext struct {
	CSS      string
	Action   string
	Selector string
	Button   string
	Database string
	Table    string
	Key      string
	Value    string
	Back     string
	Columns  []CContext
	Trail    []Entry
}

func shipErrorPage(w http.ResponseWriter, cred Access, db string, t string, cols []CContext) {

	c := FContext{
		CSS:      CSS_FILE,
		Action:   "BACK",
		Selector: "",
		Button:   "Back",
		Database: db,
		Table:    t,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Columns:  cols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateError.Execute(w, c)
	checkY(err)
}

func shipError(w http.ResponseWriter, cred Access, db string, t string, query string, e error) {
	cols := []CContext{CContext{"1", "", "Query", "", "", query, ""}, CContext{"2", "", "Result", "", "", fmt.Sprint(e), ""}}
	shipErrorPage(w, cred, db, t, cols)
}

func shipMessage(w http.ResponseWriter, cred Access, db string, msg string) {
	cols := []CContext{CContext{"1", "", "Message", "", "", msg, ""}}
	shipErrorPage(w, cred, db, "", cols)
}

func shipForm(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string, action string, button string, selector string, fieldmap map[string]string) {

	cols := getColumnInfo(cred, db, t)
	primary := getPrimary(cred, db, t)
	newcols := []CContext{}

	for _, col := range cols {
		label := col.Name
		readonly := ""
		if label == primary {
			label = label + " (ID)"
			readonly = fieldmap[col.Name]
		}
		newcols = append(newcols, CContext{col.Number, col.Name, label, col.IsNumeric, col.IsString, fieldmap[col.Name], readonly})
	}

	q := r.URL.Query()
	q.Del("action")
	linkback := q.Encode()

	c := FContext{
		CSS:      CSS_FILE,
		Action:   action,
		Selector: selector,
		Button:   button,
		Database: db,
		Table:    t,
		Key:      k,
		Value:    v,
		Back:     linkback,
		Columns:  newcols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateFormFields.Execute(w, c)
	checkY(err)
}

func actionSubset(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {
	shipForm(w, r, cred, db, t, "", "", "QUERY", "Query", "true", make(map[string]string) )
}

func actionDelete(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {
	shipForm(w, r, cred, db, t, "","", "DELETEEXEC", "Delete", "true", make(map[string]string) )
}

func actionAdd(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {
	shipForm(w, r, cred, db, t, "","","INSERT", "Insert", "", make(map[string]string) )
}

func actionEdit(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {
	query := "select * from `" + t + "` where `" + k + "` = ?"
	conn := getConnection(cred, db)
	defer conn.Close()
	stmt, err := conn.Prepare(query)
	checkY(err)
	rows, err := stmt.Query(v)
	checkY(err)
	shipForm(w, r, cred, db, t, k, v, "EDITEXEC", "Submit", "", getFieldMap(w, db, t, cred, rows))
}

func collectClauses(r *http.Request, cred Access, db string, t string, set string) []string {

	var clauses []string
	cols := getCols(cred, db, t)
	for _, col := range cols {
		val := sqlProtectString(r.FormValue(col + "C"))
		if val != "" {
			comparator := sqlProtectString(r.FormValue(col + "O"))
			if comparator == "" {
				if set == "" {
					clauses = append(clauses, "`" + col + "`" + sqlProtectNumericComparison(val))
				} else {
					clauses = append(clauses, "`" + col + "` " + set + " \"" + val + "\"")
				}
			} else {
				clauses = append(clauses, "`" + col + "`" + comparator + "\"" + val + "\"")
			}
		}
	}
	return clauses
}

func collectSet(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "="), " , ")
}

func collectWhere(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "")," && ")
}

func actionQuery(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		query := "SELECT * FROM `" + t + "` WHERE " + where
		dumpRows(w, db, t, "", "", cred, query, where)
	}
}

func actionDeleteExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		stmt := "DELETE FROM `" + t + "` WHERE " + where
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		if err != nil {
			shipError(w, cred, db, t, stmt, err)
		}
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionDeleteWhere(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, where string) {

	stmt := "DELETE FROM `" + t + "` WHERE " + where
	log.Println("[SQL]", stmt)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec()
	if err != nil {
		shipError(w, cred, db, t, stmt, err)
	}
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
}

func actionInsert(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "INSERT INTO `" + t + "` SET " + clauses
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		if err != nil {
			shipError(w, cred, db, t, stmt, err)
		}
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionEditExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "UPDATE `" + t + "` SET " + clauses + " WHERE `" + k + "` = ?"
		log.Println("[SQL]", stmt, v)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec(v)
		if err != nil {
			shipError(w, cred, db, t, stmt, err)
		}
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionRemove(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	stmt := "DELETE FROM `" + t + "` WHERE `" + k + "` = ?"
	log.Println("[SQL]", stmt, v)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec(v)
	if err != nil {
		shipError(w, cred, db, t, stmt, err)
	}
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t+"&k"+k, 302)
}

/*
 show columns from posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| title | varchar(64) | YES  |     | NULL    |       |
| start | date        | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
*/

func actionInfo(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	rows, err := getRows(cred, db, "show columns from `" + t + "`")
	checkY(err)
	defer rows.Close()

	q := url.Values{}
	q.Add("db", db)
	q.Add("t", t)

	menu := []Entry{}
	q.Set("action", "ADD")
	linkinsert := "/?" + q.Encode()
	menu = append(menu, Entry{linkinsert, "+"})

	records := [][]string{}
	head := []string{"#", "Field", "Type", "Null", "Key", "Default", "Extra"}

	var i int = 1
	for rows.Next() {
		var f, t, n, k, e string
		var d []byte // or use http://golang.org/pkg/database/sql/#NullString
		err := rows.Scan(&f, &t, &n, &k, &d, &e)
		checkY(err)
		records = append(records, []string{strconv.Itoa(i), f, t, n, k, string(d), e})
		i = i + 1
	}
	tableOutSimple(w, cred, db, t, head, records, menu)
}
<|endoftext|>