<commit_before>// Copyright (c) 2016, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package csv

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
)

// A Marshaler specifies directly how the data is marshaled to CSV.
// The result should include the header as well as the data.
type Marshaler interface {
	MarshalCSV() ([]byte, error)
}

// A Recorder specifies the Header that a file returns, as well as the string
// representation of the values. The lengths of elements returned by Header()
// and Record() should be the same.
//
// The reason for implementing Recorder is that a slice or array of Recorder
// can be marshaled.
type Recorder interface {
	Header() []string
	Record() []string
}

var (
	marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
	recorderType  = reflect.TypeOf(new(Recorder)).Elem()
)

// Marshal takes a type that implements Marshaler, Recorder, or a slice or array
// of types that implement Recorder and returns a CSV byte slice.
//
// Even a slice of interface{} can be marshaled, provided that the type of every
// value in the slice is the same, and each type implements Recorder.
func Marshal(v interface{}) ([]byte, error) {
	vt := reflect.TypeOf(v)

	// Check right away if it implements Marshaler or Recorder.
	if vt.Implements(marshalerType) {
		t := v.(Marshaler)
		return t.MarshalCSV()
	}
	if vt.Implements(recorderType) {
		t := v.(Recorder)
		return marshalRecorder(t), nil
	}

	// Any of the other checks only make sense on non-pointers.
	if vt.Kind() == reflect.Ptr {
		return Marshal(reflect.ValueOf(v).Elem().Interface())
	}

	// We also support a slice or array of Recorder, but not of MarshalCSV, because
	// semantically, anything could be in MarshalCSV, especially the header, and
	// we don't want to try to guess.
	if vt.Kind() == reflect.Slice || vt.Kind() == reflect.Array {
		// Even if the slice has element type *Type, the following should still work.
		if vt.Elem().Implements(recorderType) {
			return marshalRecorderSlice(v)
		}

		// We might get a slice of some kind of interface, in which case we require that
		// each element is of the same type.
		if vt.Elem().Kind() == reflect.Interface {
			return marshalInterfaceSlice(v)
		}

		return nil, fmt.Errorf("csv: slice element type %s does not implement Recorder", vt.Elem())
	}

	return nil, fmt.Errorf("csv: cannot marshal %s", vt)
}

func marshalRecorder(v Recorder) []byte {
	var buf bytes.Buffer
	writeRecord(&buf, v.Header())
	writeRecord(&buf, v.Record())
	return buf.Bytes()
}

func marshalRecorderSlice(v interface{}) ([]byte, error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	get := func(i int) Recorder {
		return vv.Index(i).Interface().(Recorder)
	}

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

// marshalInterfaceSlice takes a slice or array of an interface type.
//
// We require that each type in the slice/array is of the same type.
func marshalInterfaceSlice(v interface{}) (bs []byte, err error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	t := reflect.TypeOf(vv.Index(0).Interface())
	get := func(i int) Recorder {
		r, ok := vv.Index(i).Interface().(Recorder)
		if !ok {
			panic(fmt.Errorf("csv: slice element %T does not implement Recorder", vv.Index(i).Interface()))
		}
		if rt := reflect.TypeOf(r); rt != t {
			panic(fmt.Errorf("csv: expecting slice element type %s, got %s", t, rt))
		}
		return r
	}

	defer func() {
		if r := recover(); r != nil {
			if e, ok := r.(error); ok {
				err = e
				return
			}
			// It's not an error, so continue panicking.
			panic(r)
		}
	}()

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

func writeRecord(buf *bytes.Buffer, slice []string) {
	m := len(slice) - 1
	for _, s := range slice[:m] {
		buf.WriteString(s)
		buf.WriteRune(',')
	}
	buf.WriteString(slice[m])
	buf.WriteRune('\n')
}
<commit_msg>Bugfix: taking ",\n into account<commit_after>// Copyright (c) 2016, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package csv

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// A Marshaler specifies directly how the data is marshaled to CSV.
// The result should include the header as well as the data.
type Marshaler interface {
	MarshalCSV() ([]byte, error)
}

// A Recorder specifies the Header that a file returns, as well as the string
// representation of the values. The lengths of elements returned by Header()
// and Record() should be the same.
//
// The reason for implementing Recorder is that a slice or array of Recorder
// can be marshaled.
type Recorder interface {
	Header() []string
	Record() []string
}

var (
	marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
	recorderType  = reflect.TypeOf(new(Recorder)).Elem()
	strsliceType  = reflect.TypeOf([]string{})
)

// Marshal takes a type that implements Marshaler, Recorder, or a slice or array
// of types that implement Recorder and returns a CSV byte slice.
//
// Even a slice of interface{} can be marshaled, provided that the type of every
// value in the slice is the same, and each type implements Recorder.
func Marshal(v interface{}) ([]byte, error) {
	vt := reflect.TypeOf(v)

	// Check right away if it implements Marshaler or Recorder.
	if vt.Implements(marshalerType) {
		t := v.(Marshaler)
		return t.MarshalCSV()
	}
	if vt.Implements(recorderType) {
		t := v.(Recorder)
		return marshalRecorder(t), nil
	}

	// Any of the other checks only make sense on non-pointers.
	if vt.Kind() == reflect.Ptr {
		return Marshal(reflect.ValueOf(v).Elem().Interface())
	}

	// We also support a slice or array of Recorder, but not of MarshalCSV, because
	// semantically, anything could be in MarshalCSV, especially the header, and
	// we don't want to try to guess.
	if vt.Kind() == reflect.Slice || vt.Kind() == reflect.Array {
		// Even if the slice has element type *Type, the following should still work.
		if vt.Elem().Implements(recorderType) {
			return marshalRecorderSlice(v)
		}

		// We might get a slice of some kind of interface, in which case we require that
		// each element is of the same type.
		if vt.Elem().Kind() == reflect.Interface {
			return marshalInterfaceSlice(v)
		}

		// As a special case, if it is a slice of strings, print each string as a line.
		// No extra header will be printed however, this is up to the user.
		if vt == strsliceType {
			return marshalStringSlice(v.([]string))
		}

		return nil, fmt.Errorf("csv: slice element type %s does not implement Recorder", vt.Elem())
	}

	return nil, fmt.Errorf("csv: cannot marshal %s", vt)
}

func marshalStringSlice(xs []string) ([]byte, error) {
	var buf bytes.Buffer
	for _, x := range xs {
		buf.WriteString(Escape(x))
		buf.WriteRune('\n')
	}
	return buf.Bytes(), nil
}

func marshalRecorder(v Recorder) []byte {
	var buf bytes.Buffer
	writeRecord(&buf, v.Header())
	writeRecord(&buf, v.Record())
	return buf.Bytes()
}

func marshalRecorderSlice(v interface{}) ([]byte, error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	get := func(i int) Recorder {
		return vv.Index(i).Interface().(Recorder)
	}

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

// marshalInterfaceSlice takes a slice or array of an interface type.
//
// We require that each type in the slice/array is of the same type.
func marshalInterfaceSlice(v interface{}) (bs []byte, err error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	t := reflect.TypeOf(vv.Index(0).Interface())
	get := func(i int) Recorder {
		r, ok := vv.Index(i).Interface().(Recorder)
		if !ok {
			panic(fmt.Errorf("csv: slice element %T does not implement Recorder", vv.Index(i).Interface()))
		}
		if rt := reflect.TypeOf(r); rt != t {
			panic(fmt.Errorf("csv: expecting slice element type %s, got %s", t, rt))
		}
		return r
	}

	defer func() {
		if r := recover(); r != nil {
			if e, ok := r.(error); ok {
				err = e
				return
			}
			// It's not an error, so continue panicking.
			panic(r)
		}
	}()

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

// Escape formats a string so that it complies with the CSV conventions.
//
// If the string contains any double-quote characters, comma characters or
// newlines then the entire string is put in double-quotes and double-quotes
// are replaced with double-double-quotes.
//
// See: http://stackoverflow.com/questions/769621/dealing-with-commas-in-a-csv-file
func Escape(s string) string {
	if !strings.ContainsAny(s, "\n,\"") {
		return s
	}

	var buf bytes.Buffer
	buf.WriteRune('"')
	for _, r := range s {
		switch r {
		case '"':
			buf.WriteString(`""`)
		default:
			buf.WriteRune(r)
		}
	}
	buf.WriteRune('"')
	return buf.String()
}

func writeRecord(buf *bytes.Buffer, slice []string) {
	m := len(slice) - 1
	for _, s := range slice[:m] {
		buf.WriteString(Escape(s))
		buf.WriteRune(',')
	}
	buf.WriteString(Escape(slice[m]))
	buf.WriteRune('\n')
}
<|endoftext|>


<commit_before>package network

import (
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/proxy"
	"github.com/micro/go-micro/router"
	pb "github.com/micro/go-micro/router/proto"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/tunnel"
	tr "github.com/micro/go-micro/tunnel/transport"
	"github.com/micro/go-micro/util/log"
)

var (
	// ControlChannel is the name of the tunnel channel for passing contron message
	ControlChannel = "control-msg"
)

// network implements Network interface
type network struct {
	// options configure the network
	// TODO: we might end up embedding
	options Options
	// rtr is network router
	router.Router
	// prx is network proxy
	proxy.Proxy
	// tun is network tunnel
	tunnel.Tunnel
	// srv is network server
	srv server.Server
	// client is network client
	client client.Client

	sync.RWMutex
	// connected marks the network as connected
	connected bool
	// closed closes the network
	closed chan bool
}

// newNetwork returns a new network node
func newNetwork(opts ...Option) Network {
	options := DefaultOptions()

	for _, o := range opts {
		o(&options)
	}

	// init tunnel address to the network bind address
	options.Tunnel.Init(
		tunnel.Address(options.Address),
	)

	// create tunnel client with tunnel transport
	tunTransport := transport.NewTransport(
		tr.WithTunnel(options.Tunnel),
	)

	// srv is network server
	srv := server.NewServer(
		server.Transport(tunTransport),
	)

	// client is network client
	client := client.NewClient(
		client.Transport(tunTransport),
	)

	return &network{
		options: options,
		Router:  options.Router,
		Proxy:   options.Proxy,
		Tunnel:  options.Tunnel,
		srv:     srv,
		client:  client,
	}
}

// Name returns network name
func (n *network) Name() string {
	return n.options.Name
}

// Address returns network bind address
func (n *network) Address() string {
	return n.options.Address
}

func (n *network) resolveNodes() ([]string, error) {
	// resolve the network address to network nodes
	records, err := n.options.Resolver.Resolve(n.options.Name)
	if err != nil {
		return nil, err
	}

	// collect network node addresses
	nodes := make([]string, len(records))
	for i, record := range records {
		nodes[i] = record.Address
	}

	return nodes, nil
}

func (n *network) resolve() {
	resolve := time.NewTicker(ResolveTime)
	defer resolve.Stop()

	for {
		select {
		case <-n.closed:
			return
		case <-resolve.C:
			nodes, err := n.resolveNodes()
			if err != nil {
				log.Debugf("Network failed to resolve nodes: %v", err)
				continue
			}
			// initialize the tunnel
			n.Tunnel.Init(
				tunnel.Nodes(nodes...),
			)
		}
	}
}

func (n *network) process(client transport.Client) {
	for {
		m := new(transport.Message)
		if err := client.Recv(m); err != nil {
			// TODO: should we bail here?
			log.Debugf("Network advert receive error: %v", err)
			return
		}

		// switch on type of message and take action
		switch m.Header["Micro-Tunnel"] {
		case n.Router.Options().Id:
			// NOTE: this should not happen
			// skip local adverts
			continue
		default:
			pbAdvert := &pb.Advert{}
			if err := proto.Unmarshal(m.Body, pbAdvert); err != nil {
				continue
			}

			var events []*router.Event
			for _, event := range pbAdvert.Events {
				route := router.Route{
					Service: event.Route.Service,
					Address: event.Route.Address,
					Gateway: event.Route.Gateway,
					Network: event.Route.Network,
					Link:    event.Route.Link,
					Metric:  int(event.Route.Metric),
				}
				e := &router.Event{
					Type:      router.EventType(event.Type),
					Timestamp: time.Unix(0, pbAdvert.Timestamp),
					Route:     route,
				}
				events = append(events, e)
			}
			advert := &router.Advert{
				Id:        pbAdvert.Id,
				Type:      router.AdvertType(pbAdvert.Type),
				Timestamp: time.Unix(0, pbAdvert.Timestamp),
				TTL:       time.Duration(pbAdvert.Ttl),
				Events:    events,
			}

			if err := n.Router.Process(advert); err != nil {
				log.Debugf("Network failed to process advert %s: %v", advert.Id, err)
				continue
			}
		}
	}
}

// advertise advertises routes to the network
func (n *network) advertise(client transport.Client, advertChan <-chan *router.Advert) {
	for {
		select {
		// process local adverts and randomly fire them at other nodes
		case advert := <-advertChan:
			// create a proto advert
			var events []*pb.Event
			for _, event := range advert.Events {
				route := &pb.Route{
					Service: event.Route.Service,
					Address: event.Route.Address,
					Gateway: event.Route.Gateway,
					Network: event.Route.Network,
					Link:    event.Route.Link,
					Metric:  int64(event.Route.Metric),
				}
				e := &pb.Event{
					Type:      pb.EventType(event.Type),
					Timestamp: event.Timestamp.UnixNano(),
					Route:     route,
				}
				events = append(events, e)
			}
			pbAdvert := &pb.Advert{
				Id:        advert.Id,
				Type:      pb.AdvertType(advert.Type),
				Timestamp: advert.Timestamp.UnixNano(),
				Events:    events,
			}
			body, err := proto.Marshal(pbAdvert)
			if err != nil {
				// TODO: should we bail here?
				log.Debugf("Network failed to marshal message: %v", err)
				continue
			}
			// create transport message and chuck it down the pipe
			m := transport.Message{
				Header: map[string]string{
					"Micro-Method": "advert",
				},
				Body: body,
			}
			if err := client.Send(&m); err != nil {
				log.Debugf("Network failed to send advert %s: %v", pbAdvert.Id, err)
				continue
			}
		case <-n.closed:
			return
		}
	}
}

// Connect connects the network
func (n *network) Connect() error {
	n.Lock()
	defer n.Unlock()

	// return if already connected
	if n.connected {
		return nil
	}

	// try to resolve network nodes
	nodes, err := n.resolveNodes()
	if err != nil {
		return err
	}

	// connect network tunnel
	if err := n.Tunnel.Connect(); err != nil {
		return err
	}

	// initialize the tunnel to resolved nodes
	n.Tunnel.Init(
		tunnel.Nodes(nodes...),
	)

	// dial into ControlChannel to send route adverts
	client, err := n.Tunnel.Dial(ControlChannel)
	if err != nil {
		// TODO: should we stop the tunnel here?
		return err
	}

	// create closed channel
	n.closed = make(chan bool)

	// keep resolving network nodes
	go n.resolve()

	// TODO: do we assume the router has been started?
	// start advertising routes
	advertChan, err := n.options.Router.Advertise()
	if err != nil {
		return err
	}

	// advertise routes
	go n.advertise(client, advertChan)
	// process routes
	go n.process(client)

	// set connected to true
	n.connected = true

	return nil
}

func (n *network) close() error {
	// stop the router
	if err := n.Router.Stop(); err != nil {
		return err
	}

	// close the tunnel
	if err := n.Tunnel.Close(); err != nil {
		return err
	}

	return nil
}

// Close closes network connection
func (n *network) Close() error {
	n.Lock()
	defer n.Unlock()

	if !n.connected {
		return nil
	}

	select {
	case <-n.closed:
		return nil
	default:
		close(n.closed)
		// set connected to false
		n.connected = false
	}

	return n.close()
}

// Client returns network client
func (n *network) Client() client.Client {
	return n.client
}

// Server returns network server
func (n *network) Server() server.Server {
	return n.srv
}
<commit_msg>Replaced incorrect proto import path<commit_after>package network

import (
	"sync"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/proxy"
	"github.com/micro/go-micro/router"
	pb "github.com/micro/go-micro/router/proto"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/tunnel"
	tr "github.com/micro/go-micro/tunnel/transport"
	"github.com/micro/go-micro/util/log"
)

var (
	// ControlChannel is the name of the tunnel channel for passing contron message
	ControlChannel = "control-msg"
)

// network implements Network interface
type network struct {
	// options configure the network
	// TODO: we might end up embedding
	options Options
	// rtr is network router
	router.Router
	// prx is network proxy
	proxy.Proxy
	// tun is network tunnel
	tunnel.Tunnel
	// srv is network server
	srv server.Server
	// client is network client
	client client.Client

	sync.RWMutex
	// connected marks the network as connected
	connected bool
	// closed closes the network
	closed chan bool
}

// newNetwork returns a new network node
func newNetwork(opts ...Option) Network {
	options := DefaultOptions()

	for _, o := range opts {
		o(&options)
	}

	// init tunnel address to the network bind address
	options.Tunnel.Init(
		tunnel.Address(options.Address),
	)

	// create tunnel client with tunnel transport
	tunTransport := transport.NewTransport(
		tr.WithTunnel(options.Tunnel),
	)

	// srv is network server
	srv := server.NewServer(
		server.Transport(tunTransport),
	)

	// client is network client
	client := client.NewClient(
		client.Transport(tunTransport),
	)

	return &network{
		options: options,
		Router:  options.Router,
		Proxy:   options.Proxy,
		Tunnel:  options.Tunnel,
		srv:     srv,
		client:  client,
	}
}

// Name returns network name
func (n *network) Name() string {
	return n.options.Name
}

// Address returns network bind address
func (n *network) Address() string {
	return n.options.Address
}

func (n *network) resolveNodes() ([]string, error) {
	// resolve the network address to network nodes
	records, err := n.options.Resolver.Resolve(n.options.Name)
	if err != nil {
		return nil, err
	}

	// collect network node addresses
	nodes := make([]string, len(records))
	for i, record := range records {
		nodes[i] = record.Address
	}

	return nodes, nil
}

func (n *network) resolve() {
	resolve := time.NewTicker(ResolveTime)
	defer resolve.Stop()

	for {
		select {
		case <-n.closed:
			return
		case <-resolve.C:
			nodes, err := n.resolveNodes()
			if err != nil {
				log.Debugf("Network failed to resolve nodes: %v", err)
				continue
			}
			// initialize the tunnel
			n.Tunnel.Init(
				tunnel.Nodes(nodes...),
			)
		}
	}
}

func (n *network) process(client transport.Client) {
	for {
		m := new(transport.Message)
		if err := client.Recv(m); err != nil {
			// TODO: should we bail here?
			log.Debugf("Network advert receive error: %v", err)
			return
		}

		// switch on type of message and take action
		switch m.Header["Micro-Tunnel"] {
		case n.Router.Options().Id:
			// NOTE: this should not happen
			// skip local adverts
			continue
		default:
			pbAdvert := &pb.Advert{}
			if err := proto.Unmarshal(m.Body, pbAdvert); err != nil {
				continue
			}

			var events []*router.Event
			for _, event := range pbAdvert.Events {
				route := router.Route{
					Service: event.Route.Service,
					Address: event.Route.Address,
					Gateway: event.Route.Gateway,
					Network: event.Route.Network,
					Link:    event.Route.Link,
					Metric:  int(event.Route.Metric),
				}
				e := &router.Event{
					Type:      router.EventType(event.Type),
					Timestamp: time.Unix(0, pbAdvert.Timestamp),
					Route:     route,
				}
				events = append(events, e)
			}
			advert := &router.Advert{
				Id:        pbAdvert.Id,
				Type:      router.AdvertType(pbAdvert.Type),
				Timestamp: time.Unix(0, pbAdvert.Timestamp),
				TTL:       time.Duration(pbAdvert.Ttl),
				Events:    events,
			}

			if err := n.Router.Process(advert); err != nil {
				log.Debugf("Network failed to process advert %s: %v", advert.Id, err)
				continue
			}
		}
	}
}

// advertise advertises routes to the network
func (n *network) advertise(client transport.Client, advertChan <-chan *router.Advert) {
	for {
		select {
		// process local adverts and randomly fire them at other nodes
		case advert := <-advertChan:
			// create a proto advert
			var events []*pb.Event
			for _, event := range advert.Events {
				route := &pb.Route{
					Service: event.Route.Service,
					Address: event.Route.Address,
					Gateway: event.Route.Gateway,
					Network: event.Route.Network,
					Link:    event.Route.Link,
					Metric:  int64(event.Route.Metric),
				}
				e := &pb.Event{
					Type:      pb.EventType(event.Type),
					Timestamp: event.Timestamp.UnixNano(),
					Route:     route,
				}
				events = append(events, e)
			}
			pbAdvert := &pb.Advert{
				Id:        advert.Id,
				Type:      pb.AdvertType(advert.Type),
				Timestamp: advert.Timestamp.UnixNano(),
				Events:    events,
			}
			body, err := proto.Marshal(pbAdvert)
			if err != nil {
				// TODO: should we bail here?
				log.Debugf("Network failed to marshal message: %v", err)
				continue
			}
			// create transport message and chuck it down the pipe
			m := transport.Message{
				Header: map[string]string{
					"Micro-Method": "advert",
				},
				Body: body,
			}
			if err := client.Send(&m); err != nil {
				log.Debugf("Network failed to send advert %s: %v", pbAdvert.Id, err)
				continue
			}
		case <-n.closed:
			return
		}
	}
}

// Connect connects the network
func (n *network) Connect() error {
	n.Lock()
	defer n.Unlock()

	// return if already connected
	if n.connected {
		return nil
	}

	// try to resolve network nodes
	nodes, err := n.resolveNodes()
	if err != nil {
		return err
	}

	// connect network tunnel
	if err := n.Tunnel.Connect(); err != nil {
		return err
	}

	// initialize the tunnel to resolved nodes
	n.Tunnel.Init(
		tunnel.Nodes(nodes...),
	)

	// dial into ControlChannel to send route adverts
	client, err := n.Tunnel.Dial(ControlChannel)
	if err != nil {
		// TODO: should we stop the tunnel here?
		return err
	}

	// create closed channel
	n.closed = make(chan bool)

	// keep resolving network nodes
	go n.resolve()

	// TODO: do we assume the router has been started?
	// start advertising routes
	advertChan, err := n.options.Router.Advertise()
	if err != nil {
		return err
	}

	// advertise routes
	go n.advertise(client, advertChan)
	// process routes
	go n.process(client)

	// set connected to true
	n.connected = true

	return nil
}

func (n *network) close() error {
	// stop the router
	if err := n.Router.Stop(); err != nil {
		return err
	}

	// close the tunnel
	if err := n.Tunnel.Close(); err != nil {
		return err
	}

	return nil
}

// Close closes network connection
func (n *network) Close() error {
	n.Lock()
	defer n.Unlock()

	if !n.connected {
		return nil
	}

	select {
	case <-n.closed:
		return nil
	default:
		close(n.closed)
		// set connected to false
		n.connected = false
	}

	return n.close()
}

// Client returns network client
func (n *network) Client() client.Client {
	return n.client
}

// Server returns network server
func (n *network) Server() server.Server {
	return n.srv
}
<|endoftext|>


<commit_before>package dns

// Truncate ensures the reply message will fit into the requested buffer
// size by removing records that exceed the requested size.
//
// It will first check if the reply fits without compression and then with
// compression. If it won't fit with compression, Truncate then walks the
// record adding as many records as possible without exceeding the
// requested buffer size.
//
// The TC bit will be set if any records were excluded from the message.
// This indicates to that the client should retry over TCP.
//
// According to RFC 2181, the TC bit should only be set if not all of the
// "required" RRs can be included in the response. Unfortunately, we have
// no way of knowing which RRs are required so we set the TC bit if any RR
// had to be omitted from the response.
//
// The appropriate buffer size can be retrieved from the requests OPT
// record, if present, and is transport specific otherwise. dns.MinMsgSize
// should be used for UDP requests without an OPT record, and
// dns.MaxMsgSize for TCP requests without an OPT record.
func (dns *Msg) Truncate(size int) {
	if dns.IsTsig() != nil {
		// To simplify this implementation, we don't perform
		// truncation on responses with a TSIG record.
		return
	}

	// RFC 6891 mandates that the payload size in an OPT record
	// less than 512 bytes must be treated as equal to 512 bytes.
	//
	// For ease of use, we impose that restriction here.
	if size < 512 {
		size = 512
	}

	l := msgLenWithCompressionMap(dns, nil) // uncompressed length
	if l <= size {
		// Don't waste effort compressing this message.
		dns.Compress = false
		return
	}

	dns.Compress = true

	edns0 := dns.popEdns0()
	if edns0 != nil {
		// Account for the OPT record that gets added at the end,
		// by subtracting that length from our budget.
		//
		// The EDNS(0) OPT record must have the root domain and
		// it's length is thus unaffected by compression.
		size -= Len(edns0)
	}

	compression := make(map[string]struct{})

	l = headerSize
	for _, r := range dns.Question {
		l += r.len(l, compression)
	}

	var numAnswer int
	if l < size {
		l, numAnswer = truncateLoop(dns.Answer, size, l, compression)
	}

	var numNS int
	if l < size {
		l, numNS = truncateLoop(dns.Ns, size, l, compression)
	}

	var numExtra int
	if l < size {
		_, numExtra = truncateLoop(dns.Extra, size, l, compression)
	}

	// See the function documentation for when we set this.
	dns.Truncated = len(dns.Answer) > numAnswer ||
		len(dns.Ns) > numNS || len(dns.Extra) > numExtra

	dns.Answer = dns.Answer[:numAnswer]
	dns.Ns = dns.Ns[:numNS]
	dns.Extra = dns.Extra[:numExtra]

	if edns0 != nil {
		// Add the OPT record back onto the additional section.
		dns.Extra = append(dns.Extra, edns0)
	}
}

func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {
	for i, r := range rrs {
		if r == nil {
			continue
		}

		l += r.len(l, compression)
		if l > size {
			// Return size, rather than l prior to this record,
			// to prevent any further records being added.
			return size, i
		}
		if l == size {
			return l, i + 1
		}
	}

	return l, len(rrs)
}
<commit_msg>dont unset TC on a message that is already truncated (#1171)<commit_after>package dns

// Truncate ensures the reply message will fit into the requested buffer
// size by removing records that exceed the requested size.
//
// It will first check if the reply fits without compression and then with
// compression. If it won't fit with compression, Truncate then walks the
// record adding as many records as possible without exceeding the
// requested buffer size.
//
// The TC bit will be set if any records were excluded from the message.
// This indicates to that the client should retry over TCP.
//
// According to RFC 2181, the TC bit should only be set if not all of the
// "required" RRs can be included in the response. Unfortunately, we have
// no way of knowing which RRs are required so we set the TC bit if any RR
// had to be omitted from the response.
//
// The appropriate buffer size can be retrieved from the requests OPT
// record, if present, and is transport specific otherwise. dns.MinMsgSize
// should be used for UDP requests without an OPT record, and
// dns.MaxMsgSize for TCP requests without an OPT record.
func (dns *Msg) Truncate(size int) {
	if dns.IsTsig() != nil {
		// To simplify this implementation, we don't perform
		// truncation on responses with a TSIG record.
		return
	}

	// RFC 6891 mandates that the payload size in an OPT record
	// less than 512 bytes must be treated as equal to 512 bytes.
	//
	// For ease of use, we impose that restriction here.
	if size < 512 {
		size = 512
	}

	l := msgLenWithCompressionMap(dns, nil) // uncompressed length
	if l <= size {
		// Don't waste effort compressing this message.
		dns.Compress = false
		return
	}

	dns.Compress = true

	edns0 := dns.popEdns0()
	if edns0 != nil {
		// Account for the OPT record that gets added at the end,
		// by subtracting that length from our budget.
		//
		// The EDNS(0) OPT record must have the root domain and
		// it's length is thus unaffected by compression.
		size -= Len(edns0)
	}

	compression := make(map[string]struct{})

	l = headerSize
	for _, r := range dns.Question {
		l += r.len(l, compression)
	}

	var numAnswer int
	if l < size {
		l, numAnswer = truncateLoop(dns.Answer, size, l, compression)
	}

	var numNS int
	if l < size {
		l, numNS = truncateLoop(dns.Ns, size, l, compression)
	}

	var numExtra int
	if l < size {
		_, numExtra = truncateLoop(dns.Extra, size, l, compression)
	}

	// See the function documentation for when we set this.
	dns.Truncated = dns.Truncated || len(dns.Answer) > numAnswer ||
		len(dns.Ns) > numNS || len(dns.Extra) > numExtra

	dns.Answer = dns.Answer[:numAnswer]
	dns.Ns = dns.Ns[:numNS]
	dns.Extra = dns.Extra[:numExtra]

	if edns0 != nil {
		// Add the OPT record back onto the additional section.
		dns.Extra = append(dns.Extra, edns0)
	}
}

func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {
	for i, r := range rrs {
		if r == nil {
			continue
		}

		l += r.len(l, compression)
		if l > size {
			// Return size, rather than l prior to this record,
			// to prevent any further records being added.
			return size, i
		}
		if l == size {
			return l, i + 1
		}
	}

	return l, len(rrs)
}
<|endoftext|>


<commit_before>package wbrules

import (
	"fmt"
	"log"
	"strings"
	"io/ioutil"
	"github.com/stretchr/objx"
	"github.com/GeertJohan/go.rice"
	duktape "github.com/ivan4th/go-duktape"
)

const DEFAULT_CELL_MAX = 255.0

type LogFunc func (string)
type RuleEngine struct {
	model *CellModel
	ctx *duktape.Context
	logFunc LogFunc
	cellChange chan string
	scriptBox *rice.Box
}

func NewRuleEngine(model *CellModel) (engine *RuleEngine) {
	engine = &RuleEngine{
		model: model,
		ctx: duktape.NewContext(),
		logFunc: func (message string) {
			log.Printf("RULE: %s\n", message)
		},
		scriptBox: rice.MustFindBox("scripts"),
	}
	engine.ctx.PushGlobalObject()
	engine.defineEngineFunctions(map[string]func() int {
		"defineVirtualDevice": engine.esDefineVirtualDevice,
		"log": engine.esLog,
		"_wbDevObject": engine.esWbDevObject,
		"_wbCellObject": engine.esWbCellObject,
	})
	engine.ctx.Pop()
	if err := engine.loadLib(); err != nil {
		log.Panicf("failed to load runtime library: %s", err)
	}
	return
}

func (engine *RuleEngine) loadLib() error {
	libStr, err := engine.scriptBox.String("lib.js")
	if err != nil {
		return  err
	}
	if r := engine.ctx.PevalString(libStr); r != 0 {
		return fmt.Errorf("failed to load lib.js: %s", engine.ctx.SafeToString(-1))
	}
	return nil
}

func (engine *RuleEngine) SetLogFunc(logFunc LogFunc) {
	engine.logFunc = logFunc
}

func (engine *RuleEngine) esDefineVirtualDevice() int {
	if engine.ctx.GetTop() != 2 || !engine.ctx.IsString(-2) || !engine.ctx.IsObject(-1) {
		return duktape.DUK_RET_ERROR
	}
	name := engine.ctx.GetString(-2)
	title := name
	obj := GetJSObject(engine.ctx, -1).(objx.Map)
	if obj.Has("title") {
		title = obj.Get("title").Str(name)
	}
	dev := engine.model.EnsureLocalDevice(name, title)
	if obj.Has("cells") {
		if v := obj.Get("cells"); !v.IsMSI() {
			return duktape.DUK_RET_ERROR
		} else {
			for cellName, maybeCellDef := range v.MSI() {
				cellDef, ok := maybeCellDef.(map[string]interface{})
				if !ok {
					return duktape.DUK_RET_ERROR
				}
				cellType, ok := cellDef["type"]
				if !ok {
					return duktape.DUK_RET_ERROR
				}
				cellValue, ok := cellDef["value"]
				if !ok {
					return duktape.DUK_RET_ERROR
				}
				log.Printf("cellType=%v", cellType)
				if cellType == "range" {
					fmax := DEFAULT_CELL_MAX
					max, ok := cellDef["max"]
					if ok {
						log.Printf("--- max: %v", max)
						fmax, ok = max.(float64)
						if !ok {
							return duktape.DUK_RET_ERROR
						}
					}
					// FIXME: can be float
					log.Printf("SetRangeCell: %s", cellName)
					dev.SetRangeCell(cellName, cellValue, fmax)
				} else {
					dev.SetCell(cellName, cellType.(string), cellValue)
				}
			}
		}
	}
	return 0
}

func (engine *RuleEngine) esLog() int {
	strs := make([]string, 0, 100)
	for n := -engine.ctx.GetTop(); n < 0; n++ {
		strs = append(strs, engine.ctx.SafeToString(n))
	}
	if len(strs) > 0 {
		engine.logFunc(strings.Join(strs, " "))
	}
	return 0
}

func (engine *RuleEngine) esWbDevObject() int {
	log.Printf("esWbDevObject(): top=%d isString=%v", engine.ctx.GetTop(), engine.ctx.IsString(-1))
	if engine.ctx.GetTop() != 1 || !engine.ctx.IsString(-1) {
		return duktape.DUK_RET_ERROR
	}
	dev := engine.model.EnsureDevice(engine.ctx.GetString(-1))
	engine.ctx.PushGoObject(dev)
	return 1
}

func (engine *RuleEngine) esWbCellObject() int {
	if engine.ctx.GetTop() != 2 || !engine.ctx.IsString(-1) || !engine.ctx.IsObject(-2) {
		return duktape.DUK_RET_ERROR
	}
	dev, ok := engine.ctx.GetGoObject(-2).(CellModelDevice)
	if !ok {
		log.Printf("WARNING: invalid _wbCellObject call")
		return duktape.DUK_RET_TYPE_ERROR
	}
	cell := dev.EnsureCell(engine.ctx.GetString(-1))
	engine.ctx.PushGoObject(cell)
	engine.defineEngineFunctions(map[string]func() int {
		"rawValue": func () int {
			engine.ctx.PushString(cell.RawValue())
			return 1
		},
		"value": func () int {
			m := objx.New(map[string]interface{} {
				"v": cell.Value(),
			})
			PushJSObject(engine.ctx, m)
			return 1
		},
		"setValue": func () int {
			if engine.ctx.GetTop() != 1 || !engine.ctx.IsObject(-1) {
				return duktape.DUK_RET_ERROR
			}
			m, ok := GetJSObject(engine.ctx, -1).(objx.Map)
			if !ok || !m.Has("v") {
				log.Printf("WARNING: invalid cell definition")
				return duktape.DUK_RET_TYPE_ERROR
			}
			cell.SetValue(m["v"])
			return 1
		},
		"isComplete": func () int {
			engine.ctx.PushBoolean(cell.IsComplete())
			return 1
		},
	})
	return 1
}

func (engine *RuleEngine) defineEngineFunctions(fns map[string]func() int) {
	for name, fn := range fns {
		f := fn
		engine.ctx.PushGoFunc(func (*duktape.Context) int {
			return f()
		})
		engine.ctx.PutPropString(-2, name)
	}
}

func (engine *RuleEngine) RunRules() error {
	engine.ctx.PushGlobalObject()
	engine.ctx.PushString("runRules")
	defer engine.ctx.Pop2()
	if r := engine.ctx.PcallProp(-2, 0); r != 0 {
		return fmt.Errorf("failed to run rules: %s", engine.ctx.SafeToString(-1))
	}
	return nil
}

func (engine *RuleEngine) LoadScript(path string) error {
	bs, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	defer engine.ctx.Pop()
	if r := engine.ctx.PevalString(string(bs)); r != 0 {
		return fmt.Errorf("failed to load %s: %s", path, engine.ctx.SafeToString(-1))
	}
	return nil
}

func (engine *RuleEngine) Start() {
	if engine.cellChange != nil {
		return
	}
	engine.cellChange = engine.model.AcquireCellChangeChannel()
	go func () {
		for {
			select {
			case cellName, ok := <- engine.cellChange:
				if ok {
					log.Printf(
						"rule engine: running rules after cell change: %s",
						cellName)
					engine.model.CallSync(func () {
						engine.RunRules()
					})
				} else {
					log.Printf("engine stopped")
					engine.cellChange = nil
				}
			}
		}
	}()
}
<commit_msg>Keep file name / line number info when loading scripts.<commit_after>package wbrules

import (
	"fmt"
	"log"
	"strings"
	"io/ioutil"
	"github.com/stretchr/objx"
	"github.com/GeertJohan/go.rice"
	duktape "github.com/ivan4th/go-duktape"
)

const (
	LIB_FILE = "lib.js"
	DEFAULT_CELL_MAX = 255.0
)

type LogFunc func (string)
type RuleEngine struct {
	model *CellModel
	ctx *duktape.Context
	logFunc LogFunc
	cellChange chan string
	scriptBox *rice.Box
}

func NewRuleEngine(model *CellModel) (engine *RuleEngine) {
	engine = &RuleEngine{
		model: model,
		ctx: duktape.NewContext(),
		logFunc: func (message string) {
			log.Printf("RULE: %s\n", message)
		},
		scriptBox: rice.MustFindBox("scripts"),
	}
	engine.ctx.PushGlobalObject()
	engine.defineEngineFunctions(map[string]func() int {
		"defineVirtualDevice": engine.esDefineVirtualDevice,
		"log": engine.esLog,
		"_wbDevObject": engine.esWbDevObject,
		"_wbCellObject": engine.esWbCellObject,
	})
	engine.ctx.Pop()
	if err := engine.loadLib(); err != nil {
		log.Panicf("failed to load runtime library: %s", err)
	}
	return
}

func (engine *RuleEngine) loadLib() error {
	libStr, err := engine.scriptBox.String(LIB_FILE)
	if err != nil {
		return  err
	}
	engine.ctx.PushString(LIB_FILE)
	// we use PcompileStringFilename here to get readable stacktraces
	if r := engine.ctx.PcompileStringFilename(0, libStr); r != 0 {
		defer engine.ctx.Pop()
		return fmt.Errorf("failed to compile lib.js: %s", engine.ctx.SafeToString(-1))
	}
	defer engine.ctx.Pop()
	if r := engine.ctx.Pcall(0); r != 0 {
		return fmt.Errorf("failed to run lib.js: %s", engine.ctx.SafeToString(-1))
	}
	return nil
}

func (engine *RuleEngine) SetLogFunc(logFunc LogFunc) {
	engine.logFunc = logFunc
}

func (engine *RuleEngine) esDefineVirtualDevice() int {
	if engine.ctx.GetTop() != 2 || !engine.ctx.IsString(-2) || !engine.ctx.IsObject(-1) {
		return duktape.DUK_RET_ERROR
	}
	name := engine.ctx.GetString(-2)
	title := name
	obj := GetJSObject(engine.ctx, -1).(objx.Map)
	if obj.Has("title") {
		title = obj.Get("title").Str(name)
	}
	dev := engine.model.EnsureLocalDevice(name, title)
	if obj.Has("cells") {
		if v := obj.Get("cells"); !v.IsMSI() {
			return duktape.DUK_RET_ERROR
		} else {
			for cellName, maybeCellDef := range v.MSI() {
				cellDef, ok := maybeCellDef.(map[string]interface{})
				if !ok {
					return duktape.DUK_RET_ERROR
				}
				cellType, ok := cellDef["type"]
				if !ok {
					return duktape.DUK_RET_ERROR
				}
				cellValue, ok := cellDef["value"]
				if !ok {
					return duktape.DUK_RET_ERROR
				}
				log.Printf("cellType=%v", cellType)
				if cellType == "range" {
					fmax := DEFAULT_CELL_MAX
					max, ok := cellDef["max"]
					if ok {
						log.Printf("--- max: %v", max)
						fmax, ok = max.(float64)
						if !ok {
							return duktape.DUK_RET_ERROR
						}
					}
					// FIXME: can be float
					log.Printf("SetRangeCell: %s", cellName)
					dev.SetRangeCell(cellName, cellValue, fmax)
				} else {
					dev.SetCell(cellName, cellType.(string), cellValue)
				}
			}
		}
	}
	return 0
}

func (engine *RuleEngine) esLog() int {
	strs := make([]string, 0, 100)
	for n := -engine.ctx.GetTop(); n < 0; n++ {
		strs = append(strs, engine.ctx.SafeToString(n))
	}
	if len(strs) > 0 {
		engine.logFunc(strings.Join(strs, " "))
	}
	return 0
}

func (engine *RuleEngine) esWbDevObject() int {
	log.Printf("esWbDevObject(): top=%d isString=%v", engine.ctx.GetTop(), engine.ctx.IsString(-1))
	if engine.ctx.GetTop() != 1 || !engine.ctx.IsString(-1) {
		return duktape.DUK_RET_ERROR
	}
	dev := engine.model.EnsureDevice(engine.ctx.GetString(-1))
	engine.ctx.PushGoObject(dev)
	return 1
}

func (engine *RuleEngine) esWbCellObject() int {
	if engine.ctx.GetTop() != 2 || !engine.ctx.IsString(-1) || !engine.ctx.IsObject(-2) {
		return duktape.DUK_RET_ERROR
	}
	dev, ok := engine.ctx.GetGoObject(-2).(CellModelDevice)
	if !ok {
		log.Printf("WARNING: invalid _wbCellObject call")
		return duktape.DUK_RET_TYPE_ERROR
	}
	cell := dev.EnsureCell(engine.ctx.GetString(-1))
	engine.ctx.PushGoObject(cell)
	engine.defineEngineFunctions(map[string]func() int {
		"rawValue": func () int {
			engine.ctx.PushString(cell.RawValue())
			return 1
		},
		"value": func () int {
			m := objx.New(map[string]interface{} {
				"v": cell.Value(),
			})
			PushJSObject(engine.ctx, m)
			return 1
		},
		"setValue": func () int {
			if engine.ctx.GetTop() != 1 || !engine.ctx.IsObject(-1) {
				return duktape.DUK_RET_ERROR
			}
			m, ok := GetJSObject(engine.ctx, -1).(objx.Map)
			if !ok || !m.Has("v") {
				log.Printf("WARNING: invalid cell definition")
				return duktape.DUK_RET_TYPE_ERROR
			}
			cell.SetValue(m["v"])
			return 1
		},
		"isComplete": func () int {
			engine.ctx.PushBoolean(cell.IsComplete())
			return 1
		},
	})
	return 1
}

func (engine *RuleEngine) defineEngineFunctions(fns map[string]func() int) {
	for name, fn := range fns {
		f := fn
		engine.ctx.PushGoFunc(func (*duktape.Context) int {
			return f()
		})
		engine.ctx.PutPropString(-2, name)
	}
}

func (engine *RuleEngine) RunRules() error {
	engine.ctx.PushGlobalObject()
	engine.ctx.PushString("runRules")
	defer engine.ctx.Pop2()
	if r := engine.ctx.PcallProp(-2, 0); r != 0 {
		return fmt.Errorf("failed to run rules: %s", engine.ctx.SafeToString(-1))
	}
	return nil
}

func (engine *RuleEngine) LoadScript(path string) error {
	defer engine.ctx.Pop()
	if r := engine.ctx.PevalFile(path); r != 0 {
		return fmt.Errorf("failed to load lib.js: %s", engine.ctx.SafeToString(-1))
	}
	return nil
}

func (engine *RuleEngine) Start() {
	if engine.cellChange != nil {
		return
	}
	engine.cellChange = engine.model.AcquireCellChangeChannel()
	go func () {
		for {
			select {
			case cellName, ok := <- engine.cellChange:
				if ok {
					log.Printf(
						"rule engine: running rules after cell change: %s",
						cellName)
					engine.model.CallSync(func () {
						engine.RunRules()
					})
				} else {
					log.Printf("engine stopped")
					engine.cellChange = nil
				}
			}
		}
	}()
}
<|endoftext|>


<commit_before>package cli

// Funtionality to emulate line-based TCP server

import (
	"bufio"
	"github.com/ActiveState/log"
	"io"
	"net"
)

// LineServer is a line-based server à la `nc -l`. Ch channel will
// receive incoming lines from all clients.
type LineServer struct {
	listener net.Listener
	Ch       chan []byte
}

func NewLineServer(proto, laddr string) (*LineServer, error) {
	l, err := net.Listen(proto, laddr)
	if err != nil {
		return nil, err
	}
	return &LineServer{l, make(chan []byte)}, nil
}

// Start starts the server. Call as a goroutine.
func (srv *LineServer) Start() {
	for {
		conn, err := srv.listener.Accept()
		if err != nil {
			log.Fatal(err)
		}
		go func(conn net.Conn) {
			reader := bufio.NewReader(conn)
			for {
				line, isPrefix, err := reader.ReadLine()
				if isPrefix {
					log.Warnf("Ignoring a very long line beginning with: %s", line)
					for isPrefix {
						line, isPrefix, err = reader.ReadLine()
					}
				}

				if err == io.EOF {
					// Exit silently if a client disconnects.
					return
				}

				if err != nil {
					log.Fatal(err)
				}
				srv.Ch <- line
			}
		}(conn)
	}
}
<commit_msg>[103625] skip the last part of a long line<commit_after>package cli

// Funtionality to emulate line-based TCP server

import (
	"bufio"
	"github.com/ActiveState/log"
	"io"
	"net"
)

// LineServer is a line-based server à la `nc -l`. Ch channel will
// receive incoming lines from all clients.
type LineServer struct {
	listener net.Listener
	Ch       chan []byte
}

func NewLineServer(proto, laddr string) (*LineServer, error) {
	l, err := net.Listen(proto, laddr)
	if err != nil {
		return nil, err
	}
	return &LineServer{l, make(chan []byte)}, nil
}

// Start starts the server. Call as a goroutine.
func (srv *LineServer) Start() {
	for {
		conn, err := srv.listener.Accept()
		if err != nil {
			log.Fatal(err)
		}
		go func(conn net.Conn) {
			reader := bufio.NewReader(conn)
			for {
				line, isPrefix, err := reader.ReadLine()
				if isPrefix {
					log.Warnf("Ignoring a very long line beginning with: %s", line)
					parts := 1
					for isPrefix {
						line, isPrefix, err = reader.ReadLine()
						log.Warnf("Ignoring next part: %s", line)
						parts++
					}
					log.Infof("Ignored %d parts", parts)
					continue
				}

				if err == io.EOF {
					// Exit silently if a client disconnects.
					return
				}

				if err != nil {
					log.Fatal(err)
				}
				srv.Ch <- line
			}
		}(conn)
	}
}
<|endoftext|>


<commit_before>package run

import (
	"debug/elf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"

	"github.com/tsavola/wag"
	"github.com/tsavola/wag/types"
	"github.com/tsavola/wag/wasm"

	"github.com/tsavola/gate/internal/memfd"
)

var (
	pageSize = uint32(os.Getpagesize())
)

func roundToPage(size int) uint32 {
	mask := pageSize - 1
	return (uint32(size) + mask) &^ mask
}

type envFunc struct {
	addr uint64
	sig  types.Function
}

type Environment struct {
	executor string
	loader   *os.File
	funcs    map[string]envFunc
}

func NewEnvironment(executor, loader string) (env *Environment, err error) {
	f, err := os.Open(loader)
	if err != nil {
		return
	}

	elff, err := elf.NewFile(f)
	if err != nil {
		f.Close()
		return
	}

	symbols, err := elff.Symbols()
	if err != nil {
		f.Close()
		return
	}

	funcs := make(map[string]envFunc)

	for _, s := range symbols {
		switch s.Name {
		case "__gate_get_abi_version", "__gate_get_max_packet_size":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Result: types.I32,
			}}

		case "__gate_func_ptr":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Args:   []types.T{types.I32},
				Result: types.I32,
			}}

		case "__gate_exit":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Args: []types.T{types.I32},
			}}

		case "__gate_recv_full", "__gate_send_full":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Args: []types.T{types.I32, types.I32},
			}}
		}
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		f.Close()
		return
	}

	env = &Environment{
		executor: executor,
		loader:   f,
		funcs:    funcs,
	}
	return
}

func (env *Environment) ImportFunction(module, field string, sig types.Function) (variadic bool, addr uint64, err error) {
	if module == "env" {
		if f, found := env.funcs[field]; found {
			if !f.sig.Equal(sig) {
				err = fmt.Errorf("function %s %s imported with wrong signature: %s", field, f.sig, sig)
				return
			}

			addr = f.addr
			return
		}
	}

	err = fmt.Errorf("imported function not found: %s %s %s", module, field, sig)
	return
}

func (env *Environment) ImportGlobal(module, field string, t types.T) (value uint64, err error) {
	if module == "env" {
		switch field {
		case "__gate_abi_version":
			value = abiVersion
			return

		case "__gate_max_packet_size":
			value = maxPacketSize
			return
		}
	}

	err = fmt.Errorf("imported global not found: %s %s %s", module, field, t)
	return
}

type payloadInfo struct {
	PageSize       uint32
	RODataSize     uint32
	TextSize       uint32
	MemoryOffset   uint32
	InitMemorySize uint32
	GrowMemorySize uint32
	StackSize      uint32
}

type Payload struct {
	maps *os.File
	info payloadInfo
}

func NewPayload(m *wag.Module, growMemorySize wasm.MemorySize, stackSize int32) (payload *Payload, err error) {
	initMemorySize, _ := m.MemoryLimits()

	if initMemorySize > growMemorySize {
		err = fmt.Errorf("initial memory size %d exceeds maximum memory size %d", initMemorySize, growMemorySize)
		return
	}

	roData := m.ROData()
	text := m.Text()
	data, memoryOffset := m.Data()

	memory := data[memoryOffset:]
	binary.LittleEndian.PutUint32(data[4:], uint32(len(memory))) // stack ptr?

	fd, err := memfd.Create("payload", memfd.CLOEXEC|memfd.ALLOW_SEALING)
	if err != nil {
		return
	}

	f := os.NewFile(uintptr(fd), "memfd")

	_, err = f.Write(roData)
	if err != nil {
		f.Close()
		return
	}

	roDataSize := roundToPage(len(roData))

	_, err = f.WriteAt(text, int64(roDataSize))
	if err != nil {
		f.Close()
		return
	}

	textSize := roundToPage(len(text))

	_, err = f.WriteAt(data, int64(roDataSize)+int64(textSize))
	if err != nil {
		f.Close()
		return
	}

	globalsMemorySize := roundToPage(memoryOffset + int(growMemorySize))
	totalSize := int64(roDataSize) + int64(textSize) + int64(globalsMemorySize) + int64(stackSize)

	err = f.Truncate(totalSize)
	if err != nil {
		f.Close()
		return
	}

	_, err = memfd.Fcntl(fd, memfd.F_ADD_SEALS, memfd.F_SEAL_SHRINK|memfd.F_SEAL_GROW)
	if err != nil {
		f.Close()
		return
	}

	payload = &Payload{
		maps: f,
		info: payloadInfo{
			PageSize:       pageSize,
			RODataSize:     roDataSize,
			TextSize:       textSize,
			MemoryOffset:   uint32(memoryOffset),
			InitMemorySize: uint32(initMemorySize),
			GrowMemorySize: uint32(growMemorySize),
			StackSize:      uint32(stackSize),
		},
	}
	return
}

func (payload *Payload) Close() (err error) {
	err = payload.maps.Close()
	payload.maps = nil
	return
}

func Run(env *Environment, payload *Payload) (output []byte, err error) {
	cmd := exec.Cmd{
		Path: env.executor,
		Args: []string{},
		Env:  []string{},
		Dir:  "/",
		ExtraFiles: []*os.File{
			payload.maps,
			env.loader,
		},
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		stdin.Close()
		return
	}

	err = cmd.Start()
	if err != nil {
		stdin.Close()
		stdout.Close()
		return
	}

	err = binary.Write(stdin, nativeEndian, payload.info)
	if err != nil {
		cmd.Process.Kill()
		cmd.Wait()
		return
	}

	output, _ = ioutil.ReadAll(stdout)

	err = cmd.Wait()
	if err != nil {
		return
	}

	if !cmd.ProcessState.Success() {
		err = errors.New(cmd.ProcessState.String())
	}
	return
}
<commit_msg>run: execute in dedicated user namespace<commit_after>package run

import (
	"debug/elf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"

	"github.com/tsavola/wag"
	"github.com/tsavola/wag/types"
	"github.com/tsavola/wag/wasm"

	"github.com/tsavola/gate/internal/memfd"
)

var (
	pageSize = uint32(os.Getpagesize())
)

func roundToPage(size int) uint32 {
	mask := pageSize - 1
	return (uint32(size) + mask) &^ mask
}

type envFunc struct {
	addr uint64
	sig  types.Function
}

type Environment struct {
	executor string
	loader   *os.File
	funcs    map[string]envFunc
}

func NewEnvironment(executor, loader string) (env *Environment, err error) {
	f, err := os.Open(loader)
	if err != nil {
		return
	}

	elff, err := elf.NewFile(f)
	if err != nil {
		f.Close()
		return
	}

	symbols, err := elff.Symbols()
	if err != nil {
		f.Close()
		return
	}

	funcs := make(map[string]envFunc)

	for _, s := range symbols {
		switch s.Name {
		case "__gate_get_abi_version", "__gate_get_max_packet_size":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Result: types.I32,
			}}

		case "__gate_func_ptr":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Args:   []types.T{types.I32},
				Result: types.I32,
			}}

		case "__gate_exit":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Args: []types.T{types.I32},
			}}

		case "__gate_recv_full", "__gate_send_full":
			funcs[s.Name] = envFunc{s.Value, types.Function{
				Args: []types.T{types.I32, types.I32},
			}}
		}
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		f.Close()
		return
	}

	env = &Environment{
		executor: executor,
		loader:   f,
		funcs:    funcs,
	}
	return
}

func (env *Environment) ImportFunction(module, field string, sig types.Function) (variadic bool, addr uint64, err error) {
	if module == "env" {
		if f, found := env.funcs[field]; found {
			if !f.sig.Equal(sig) {
				err = fmt.Errorf("function %s %s imported with wrong signature: %s", field, f.sig, sig)
				return
			}

			addr = f.addr
			return
		}
	}

	err = fmt.Errorf("imported function not found: %s %s %s", module, field, sig)
	return
}

func (env *Environment) ImportGlobal(module, field string, t types.T) (value uint64, err error) {
	if module == "env" {
		switch field {
		case "__gate_abi_version":
			value = abiVersion
			return

		case "__gate_max_packet_size":
			value = maxPacketSize
			return
		}
	}

	err = fmt.Errorf("imported global not found: %s %s %s", module, field, t)
	return
}

type payloadInfo struct {
	PageSize       uint32
	RODataSize     uint32
	TextSize       uint32
	MemoryOffset   uint32
	InitMemorySize uint32
	GrowMemorySize uint32
	StackSize      uint32
}

type Payload struct {
	maps *os.File
	info payloadInfo
}

func NewPayload(m *wag.Module, growMemorySize wasm.MemorySize, stackSize int32) (payload *Payload, err error) {
	initMemorySize, _ := m.MemoryLimits()

	if initMemorySize > growMemorySize {
		err = fmt.Errorf("initial memory size %d exceeds maximum memory size %d", initMemorySize, growMemorySize)
		return
	}

	roData := m.ROData()
	text := m.Text()
	data, memoryOffset := m.Data()

	memory := data[memoryOffset:]
	binary.LittleEndian.PutUint32(data[4:], uint32(len(memory))) // stack ptr?

	fd, err := memfd.Create("payload", memfd.CLOEXEC|memfd.ALLOW_SEALING)
	if err != nil {
		return
	}

	f := os.NewFile(uintptr(fd), "memfd")

	_, err = f.Write(roData)
	if err != nil {
		f.Close()
		return
	}

	roDataSize := roundToPage(len(roData))

	_, err = f.WriteAt(text, int64(roDataSize))
	if err != nil {
		f.Close()
		return
	}

	textSize := roundToPage(len(text))

	_, err = f.WriteAt(data, int64(roDataSize)+int64(textSize))
	if err != nil {
		f.Close()
		return
	}

	globalsMemorySize := roundToPage(memoryOffset + int(growMemorySize))
	totalSize := int64(roDataSize) + int64(textSize) + int64(globalsMemorySize) + int64(stackSize)

	err = f.Truncate(totalSize)
	if err != nil {
		f.Close()
		return
	}

	_, err = memfd.Fcntl(fd, memfd.F_ADD_SEALS, memfd.F_SEAL_SHRINK|memfd.F_SEAL_GROW)
	if err != nil {
		f.Close()
		return
	}

	payload = &Payload{
		maps: f,
		info: payloadInfo{
			PageSize:       pageSize,
			RODataSize:     roDataSize,
			TextSize:       textSize,
			MemoryOffset:   uint32(memoryOffset),
			InitMemorySize: uint32(initMemorySize),
			GrowMemorySize: uint32(growMemorySize),
			StackSize:      uint32(stackSize),
		},
	}
	return
}

func (payload *Payload) Close() (err error) {
	err = payload.maps.Close()
	payload.maps = nil
	return
}

func Run(env *Environment, payload *Payload) (output []byte, err error) {
	cmd := exec.Cmd{
		Path: env.executor,
		Args: []string{},
		Env:  []string{},
		Dir:  "/",
		ExtraFiles: []*os.File{
			payload.maps,
			env.loader,
		},
		SysProcAttr: &syscall.SysProcAttr{
			Cloneflags: syscall.CLONE_NEWUSER,
		},
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		stdin.Close()
		return
	}

	err = cmd.Start()
	if err != nil {
		stdin.Close()
		stdout.Close()
		return
	}

	err = binary.Write(stdin, nativeEndian, payload.info)
	if err != nil {
		cmd.Process.Kill()
		cmd.Wait()
		return
	}

	output, _ = ioutil.ReadAll(stdout)

	err = cmd.Wait()
	if err != nil {
		return
	}

	if !cmd.ProcessState.Success() {
		err = errors.New(cmd.ProcessState.String())
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

const DDG_URL = "http://api.duckduckgo.com/"

func QueryDDG(query string) (*DDGResponse, error) {
	queryURL := addArgToURL(DDG_URL, "q", query)
	queryURL = addArgToURL(queryURL, "format", "json")
	queryURL = addArgToURL(queryURL, "pretty", "1")
	resp, err := http.Get(queryURL)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return NewDDGResponse(body)
}

func addArgToURL(baseURL string, key string, value string) string {
	u, err := url.Parse(baseURL)

	if err != nil {
		log.Fatal(err)
		return ""
	}

	q := u.Query()
	q.Set(url.QueryEscape(key), url.QueryEscape(value))
	u.RawQuery = q.Encode()

	return u.String()
}
<commit_msg>fixes an issue with url encoding. '+' characters are now interpreted as spaces in accordance with the RFC<commit_after>package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

const DDG_URL = "http://api.duckduckgo.com/"

func QueryDDG(query string) (*DDGResponse, error) {
	queryURL := addArgToURL(DDG_URL, "q", query)
	queryURL = addArgToURL(queryURL, "format", "json")
	resp, err := http.Get(queryURL)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return NewDDGResponse(body)
}

func addArgToURL(baseURL string, key string, value string) string {
	u, err := url.Parse(baseURL)

	if err != nil {
		log.Fatal(err)
		return ""
	}

	q := u.Query()
	q.Set(key, value)
	u.RawQuery = q.Encode()

	return u.String()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"crypto/sha256"
	"database/sql"
	"encoding/json"
	"fmt"
	"image"
	"image/png"
	"mime/multipart"
	"os"
	"strconv"
	"strings"
	"time"

	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/nfnt/resize"
	"zxq.co/ripple/rippleapi/common"
	"zxq.co/x/rs"
)

func createAPIToken(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/tokens")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	privileges := common.Privileges(common.Int(c.PostForm("privileges"))).CanOnly(ctx.User.Privileges)
	description := c.PostForm("description")

	var (
		tokenStr string
		tokenMD5 string
	)

	for {
		tokenStr = common.RandomString(32)
		tokenMD5 = fmt.Sprintf("%x", md5.Sum([]byte(tokenStr)))

		var id int
		err := db.QueryRow("SELECT id FROM tokens WHERE token = ? LIMIT 1", tokenMD5).Scan(&id)
		if err == sql.ErrNoRows {
			break
		}
		if err != nil {
			c.Error(err)
			resp500(c)
			return
		}
	}

	_, err := db.Exec("INSERT INTO tokens(user, privileges, description, token, private, last_updated) VALUES (?, ?, ?, ?, '0', ?)",
		ctx.User.ID, privileges, description, tokenMD5, time.Now().Unix())
	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	addMessage(c, successMessage{
		fmt.Sprintf("Your token has been created successfully! Your token is: %s.
Keep it safe, don't show it around, and store it now! We won't show it to you again.", tokenStr),
	})
}

func deleteAPIToken(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/tokens")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	db.Exec("DELETE FROM tokens WHERE id = ? AND user = ? AND private = 0", c.PostForm("id"), ctx.User.ID)
	addMessage(c, successMessage{"That token has been deleted successfully."})
}

func editAPIToken(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/tokens/edit?id="+c.PostForm("id"))
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	privileges := common.Privileges(common.Int(c.PostForm("privileges"))).CanOnly(ctx.User.Privileges)
	description := c.PostForm("description")

	_, err := db.Exec("UPDATE tokens SET privileges = ?, description = ? WHERE user = ? AND id = ? AND private = 0",
		privileges, description, ctx.User.ID, c.PostForm("id"))
	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	addMessage(c, successMessage{
		"Your token has been edited successfully!",
	})
}

type oAuthClient struct {
	ID          string
	Extra       string
	RedirectURI string
}

// Name retrieves the name of an oAuthClient
func (o oAuthClient) Name() string {
	var x [3]string
	json.Unmarshal([]byte(o.Extra), &x)
	return x[0]
}

// Avatar retrieves the avatar of an oAuthClient
func (o oAuthClient) Avatar() string {
	var x [3]string
	json.Unmarshal([]byte(o.Extra), &x)
	return x[2]
}

// Owner retrieves the ID of the owner of an oAuthClient
func (o oAuthClient) Owner() int {
	var x [3]string
	json.Unmarshal([]byte(o.Extra), &x)
	u, _ := strconv.Atoi(x[1])
	return u
}

func getOAuthApplications(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	var apps []oAuthClient
	err := db.Select(&apps, `SELECT c.id, c.extra, c.redirect_uri as redirecturi
FROM osin_client_user cu
INNER JOIN osin_client c ON c.id = cu.client_id
WHERE cu.user = ?`, ctx.User.ID)

	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	simple(c, getSimpleByFilename("dev/apps.html"), nil, map[string]interface{}{
		"apps": apps,
	})
}

func editOAuthApplication(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	app := new(oAuthClient)
	if c.Query("id") != "new" {
		err := db.Get(app, `SELECT c.id, c.extra, c.redirect_uri as redirecturi
FROM osin_client_user cu
INNER JOIN osin_client c ON c.id = cu.client_id
WHERE cu.user = ? AND cu.client_id = ?`, ctx.User.ID, c.Query("id"))
		switch err {
		case nil:
			break
		case sql.ErrNoRows:
			app = nil
		default:
			c.Error(err)
			resp500(c)
			return
		}
	} else {
		app = new(oAuthClient)
	}

	tpl := getSimpleByFilename("dev/edit_app.html")
	if c.Query("id") == "new" {
		tpl.TitleBar = "Create OAuth 2 application"
	}
	simple(c, tpl, nil, map[string]interface{}{
		"app": app,
	})
}

func editOAuthApplicationSubmit(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	c.Request.ParseMultipartForm(32 << 10)
	id := c.PostForm("id")

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/apps/edit?id="+id)
	}()

	var avatarFilename string
	if id != "new" {
		var previousExtra string
		db.Get(&previousExtra, "SELECT extra FROM osin_client WHERE id = ?", id)
		oClient := oAuthClient{Extra: previousExtra}
		if previousExtra == "" || oClient.Owner() != ctx.User.ID {
			fmt.Println(previousExtra, oClient.Owner(), ctx.User.ID)
			// user will be returned to /dev/apps/edit?id=whatever,
			// which will say the token could not be found.
			return
		}
		avatarFilename = oClient.Avatar()
	}

	avatar, _, err := c.Request.FormFile("avatar")

	if err != nil && err != http.ErrMissingFile {
		c.Error(err)
		resp500(c)
		return
	}

	if avatar != nil {
		if avatarFilename != "" {
			err := deletePreviousAvatar(avatarFilename)
			if err != nil {
				c.Error(err)
				resp500(c)
				return
			}
		}
		avatarFilename, err = storeNewAvatar(avatar)
		if err != nil {
			c.Error(err)
			resp500(c)
			return
		}
	}

	name := common.SanitiseString(c.PostForm("name"))
	if len(name) > 25 {
		name = name[:25]
	}
	redirectURI := common.SanitiseString(c.PostForm("redirect_uri"))
	extra, _ := json.Marshal([3]string{
		name,
		strconv.Itoa(ctx.User.ID),
		avatarFilename,
	})

	if id == "new" {
		id = rs.StringFromChars(32, oAuthRandomChars)
		secret := rs.StringFromChars(64, oAuthRandomChars)
		secretSha := fmt.Sprintf("%x", sha256.Sum256([]byte(secret)))
		db.Exec("INSERT INTO osin_client(id, secret, extra, redirect_uri) VALUES (?, ?, ?, ?)", id, secretSha, string(extra), redirectURI)
		db.Exec("INSERT INTO osin_client_user(client_id, user) VALUES (?, ?)", id, ctx.User.ID)
		addMessage(c, successMessage{fmt.Sprintf(createClientMessage, id, secret)})
	} else {
		db.Exec("UPDATE osin_client SET extra = ?, redirect_uri = ? WHERE id = ?", string(extra), redirectURI, id)
		addMessage(c, successMessage{"Your application has been saved."})
	}
}

const createClientMessage = `
You can now get going integrating Ripple in your super cool project!
Here's what you need:

client_id     = "%s"
client_secret = "%s"

As always: keep it safe, don't show it around, and store it now!
We won't show you the client_secret again.`

func deletePreviousAvatar(s string) error {
	return os.Remove("static/oauth-apps/" + s)
}

const oAuthRandomChars = "qwertyuiopasdfghjklzxcvbnm"

func storeNewAvatar(file multipart.File) (string, error) {
	img, _, err := image.Decode(file)
	if err != nil {
		return "", err
	}
	img = resize.Thumbnail(256, 256, img, resize.Bilinear)
	newFilename := rs.StringFromChars(32, oAuthRandomChars) + ".png"
	f, err := os.Create(fmt.Sprintf("static/oauth-apps/%s", newFilename))
	defer f.Close()
	if err != nil {
		return "", err
	}
	err = png.Encode(f, img)
	return newFilename, err
}

func deleteOAuthApplication(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/apps")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	var x oAuthClient
	db.Get(&x, "SELECT extra FROM osin_client WHERE id = ?", c.PostForm("id"))
	if x.Owner() != ctx.User.ID {
		addMessage(c, errorMessage{"y u do dis"})
		return
	}

	if x.Avatar() != "" {
		os.Remove("static/oauth-apps/" + x.Avatar())
	}

	clientID := c.PostForm("id")

	db.Exec("DELETE FROM osin_access WHERE client = ?", clientID)
	db.Exec("DELETE FROM osin_client_user WHERE client_id = ?", clientID)
	db.Exec("DELETE FROM osin_client WHERE id = ?", clientID)

	addMessage(c, successMessage{"poof"})
}

type authorization struct {
	oAuthClient
	Scope     string
	CreatedAt time.Time
	Client    string
}

var scopeMap = map[string]string{
	"identify":          "Identify",
	"read_confidential": "Read private information",
	"write":             "Write",
}

func (a authorization) Scopes(c *gin.Context) string {
	if a.Scope == "" {
		return T(c, "Identify")
	}
	scopes := strings.Split(a.Scope, " ")
	scopes = append([]string{"identify"}, scopes...)
	for i, val := range scopes {
		scopes[i] = T(c, scopeMap[val])
	}
	return strings.Join(scopes, ", ")
}

func authorizedApplications(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
	}

	var apps []authorization
	err := db.Select(&apps, `
SELECT c.extra, a.scope, a.created_at AS createdat, a.client
FROM osin_access a
INNER JOIN osin_client c ON c.id = a.client
WHERE a.extra = ?
GROUP BY a.client
ORDER BY a.created_at DESC`, ctx.User.ID)

	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	simple(
		c, getSimpleByFilename("settings/authorized_applications.html"), nil,
		map[string]interface{}{
			"apps": apps,
		},
	)
}

func revokeAuthorization(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/settings/authorized_applications")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	db.Exec("DELETE FROM osin_access WHERE access_token = ? AND extra = ?", c.PostForm("access_token"), ctx.User.ID)
	addMessage(c, successMessage{T(c, "That authorization has been successfully revoked.")})
}
<commit_msg>Ha! I'm dumb.<commit_after>package main

import (
	"crypto/md5"
	"crypto/sha256"
	"database/sql"
	"encoding/json"
	"fmt"
	"image"
	"image/png"
	"mime/multipart"
	"os"
	"strconv"
	"strings"
	"time"

	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/nfnt/resize"
	"zxq.co/ripple/rippleapi/common"
	"zxq.co/x/rs"
)

func createAPIToken(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/tokens")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	privileges := common.Privileges(common.Int(c.PostForm("privileges"))).CanOnly(ctx.User.Privileges)
	description := c.PostForm("description")

	var (
		tokenStr string
		tokenMD5 string
	)

	for {
		tokenStr = common.RandomString(32)
		tokenMD5 = fmt.Sprintf("%x", md5.Sum([]byte(tokenStr)))

		var id int
		err := db.QueryRow("SELECT id FROM tokens WHERE token = ? LIMIT 1", tokenMD5).Scan(&id)
		if err == sql.ErrNoRows {
			break
		}
		if err != nil {
			c.Error(err)
			resp500(c)
			return
		}
	}

	_, err := db.Exec("INSERT INTO tokens(user, privileges, description, token, private, last_updated) VALUES (?, ?, ?, ?, '0', ?)",
		ctx.User.ID, privileges, description, tokenMD5, time.Now().Unix())
	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	addMessage(c, successMessage{
		fmt.Sprintf("Your token has been created successfully! Your token is: %s.
Keep it safe, don't show it around, and store it now! We won't show it to you again.", tokenStr),
	})
}

func deleteAPIToken(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/tokens")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	db.Exec("DELETE FROM tokens WHERE id = ? AND user = ? AND private = 0", c.PostForm("id"), ctx.User.ID)
	addMessage(c, successMessage{"That token has been deleted successfully."})
}

func editAPIToken(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/tokens/edit?id="+c.PostForm("id"))
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	privileges := common.Privileges(common.Int(c.PostForm("privileges"))).CanOnly(ctx.User.Privileges)
	description := c.PostForm("description")

	_, err := db.Exec("UPDATE tokens SET privileges = ?, description = ? WHERE user = ? AND id = ? AND private = 0",
		privileges, description, ctx.User.ID, c.PostForm("id"))
	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	addMessage(c, successMessage{
		"Your token has been edited successfully!",
	})
}

type oAuthClient struct {
	ID          string
	Extra       string
	RedirectURI string
}

// Name retrieves the name of an oAuthClient
func (o oAuthClient) Name() string {
	var x [3]string
	json.Unmarshal([]byte(o.Extra), &x)
	return x[0]
}

// Avatar retrieves the avatar of an oAuthClient
func (o oAuthClient) Avatar() string {
	var x [3]string
	json.Unmarshal([]byte(o.Extra), &x)
	return x[2]
}

// Owner retrieves the ID of the owner of an oAuthClient
func (o oAuthClient) Owner() int {
	var x [3]string
	json.Unmarshal([]byte(o.Extra), &x)
	u, _ := strconv.Atoi(x[1])
	return u
}

func getOAuthApplications(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	var apps []oAuthClient
	err := db.Select(&apps, `SELECT c.id, c.extra, c.redirect_uri as redirecturi
FROM osin_client_user cu
INNER JOIN osin_client c ON c.id = cu.client_id
WHERE cu.user = ?`, ctx.User.ID)

	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	simple(c, getSimpleByFilename("dev/apps.html"), nil, map[string]interface{}{
		"apps": apps,
	})
}

func editOAuthApplication(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	app := new(oAuthClient)
	if c.Query("id") != "new" {
		err := db.Get(app, `SELECT c.id, c.extra, c.redirect_uri as redirecturi
FROM osin_client_user cu
INNER JOIN osin_client c ON c.id = cu.client_id
WHERE cu.user = ? AND cu.client_id = ?`, ctx.User.ID, c.Query("id"))
		switch err {
		case nil:
			break
		case sql.ErrNoRows:
			app = nil
		default:
			c.Error(err)
			resp500(c)
			return
		}
	} else {
		app = new(oAuthClient)
	}

	tpl := getSimpleByFilename("dev/edit_app.html")
	if c.Query("id") == "new" {
		tpl.TitleBar = "Create OAuth 2 application"
	}
	simple(c, tpl, nil, map[string]interface{}{
		"app": app,
	})
}

func editOAuthApplicationSubmit(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	c.Request.ParseMultipartForm(32 << 10)
	id := c.PostForm("id")

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/apps/edit?id="+id)
	}()

	var avatarFilename string
	if id != "new" {
		var previousExtra string
		db.Get(&previousExtra, "SELECT extra FROM osin_client WHERE id = ?", id)
		oClient := oAuthClient{Extra: previousExtra}
		if previousExtra == "" || oClient.Owner() != ctx.User.ID {
			fmt.Println(previousExtra, oClient.Owner(), ctx.User.ID)
			// user will be returned to /dev/apps/edit?id=whatever,
			// which will say the token could not be found.
			return
		}
		avatarFilename = oClient.Avatar()
	}

	avatar, _, err := c.Request.FormFile("avatar")

	if err != nil && err != http.ErrMissingFile {
		c.Error(err)
		resp500(c)
		return
	}

	if avatar != nil {
		if avatarFilename != "" {
			err := deletePreviousAvatar(avatarFilename)
			if err != nil {
				c.Error(err)
				resp500(c)
				return
			}
		}
		avatarFilename, err = storeNewAvatar(avatar)
		if err != nil {
			c.Error(err)
			resp500(c)
			return
		}
	}

	name := common.SanitiseString(c.PostForm("name"))
	if len(name) > 25 {
		name = name[:25]
	}
	redirectURI := common.SanitiseString(c.PostForm("redirect_uri"))
	extra, _ := json.Marshal([3]string{
		name,
		strconv.Itoa(ctx.User.ID),
		avatarFilename,
	})

	if id == "new" {
		id = rs.StringFromChars(32, oAuthRandomChars)
		secret := rs.StringFromChars(64, oAuthRandomChars)
		secretSha := fmt.Sprintf("%x", sha256.Sum256([]byte(secret)))
		db.Exec("INSERT INTO osin_client(id, secret, extra, redirect_uri) VALUES (?, ?, ?, ?)", id, secretSha, string(extra), redirectURI)
		db.Exec("INSERT INTO osin_client_user(client_id, user) VALUES (?, ?)", id, ctx.User.ID)
		addMessage(c, successMessage{fmt.Sprintf(createClientMessage, id, secret)})
	} else {
		db.Exec("UPDATE osin_client SET extra = ?, redirect_uri = ? WHERE id = ?", string(extra), redirectURI, id)
		addMessage(c, successMessage{"Your application has been saved."})
	}
}

const createClientMessage = `
You can now get going integrating Ripple in your super cool project!
Here's what you need:

client_id     = "%s"
client_secret = "%s"

As always: keep it safe, don't show it around, and store it now!
We won't show you the client_secret again.`

func deletePreviousAvatar(s string) error {
	return os.Remove("static/oauth-apps/" + s)
}

const oAuthRandomChars = "qwertyuiopasdfghjklzxcvbnm"

func storeNewAvatar(file multipart.File) (string, error) {
	img, _, err := image.Decode(file)
	if err != nil {
		return "", err
	}
	img = resize.Thumbnail(256, 256, img, resize.Bilinear)
	newFilename := rs.StringFromChars(32, oAuthRandomChars) + ".png"
	f, err := os.Create(fmt.Sprintf("static/oauth-apps/%s", newFilename))
	defer f.Close()
	if err != nil {
		return "", err
	}
	err = png.Encode(f, img)
	return newFilename, err
}

func deleteOAuthApplication(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/dev/apps")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	var x oAuthClient
	db.Get(&x, "SELECT extra FROM osin_client WHERE id = ?", c.PostForm("id"))
	if x.Owner() != ctx.User.ID {
		addMessage(c, errorMessage{"y u do dis"})
		return
	}

	if x.Avatar() != "" {
		os.Remove("static/oauth-apps/" + x.Avatar())
	}

	clientID := c.PostForm("id")

	db.Exec("DELETE FROM osin_access WHERE client = ?", clientID)
	db.Exec("DELETE FROM osin_client_user WHERE client_id = ?", clientID)
	db.Exec("DELETE FROM osin_client WHERE id = ?", clientID)

	addMessage(c, successMessage{"poof"})
}

type authorization struct {
	oAuthClient
	Scope     string
	CreatedAt time.Time
	Client    string
}

var scopeMap = map[string]string{
	"identify":          "Identify",
	"read_confidential": "Read private information",
	"write":             "Write",
}

func (a authorization) Scopes(c *gin.Context) string {
	if a.Scope == "" {
		return T(c, "Identify")
	}
	scopes := strings.Split(a.Scope, " ")
	scopes = append([]string{"identify"}, scopes...)
	for i, val := range scopes {
		scopes[i] = T(c, scopeMap[val])
	}
	return strings.Join(scopes, ", ")
}

func authorizedApplications(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
	}

	var apps []authorization
	err := db.Select(&apps, `
SELECT c.extra, a.scope, a.created_at AS createdat, a.client
FROM osin_access a
INNER JOIN osin_client c ON c.id = a.client
WHERE a.extra = ?
GROUP BY a.client
ORDER BY a.created_at DESC`, ctx.User.ID)

	if err != nil {
		c.Error(err)
		resp500(c)
		return
	}

	simple(
		c, getSimpleByFilename("settings/authorized_applications.html"), nil,
		map[string]interface{}{
			"apps": apps,
		},
	)
}

func revokeAuthorization(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	sess := getSession(c)
	defer func() {
		sess.Save()
		c.Redirect(302, "/settings/authorized_applications")
	}()

	if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
		addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
		return
	}

	db.Exec("DELETE FROM osin_access WHERE client = ? AND extra = ?", c.PostForm("client_id"), ctx.User.ID)
	addMessage(c, successMessage{T(c, "That authorization has been successfully revoked.")})
}
<|endoftext|>


<commit_before>package ether

type Dev interface {
	Reader() <-chan *FrameWithTime
	Writer() chan<- *FrameBuf
	Name() string
	GetMTU() (int, error)
}
<commit_msg>add GetHardwareAddr(0 in dev.go<commit_after>package ether

import "net"

type Dev interface {
	Reader() <-chan *FrameWithTime
	Writer() chan<- *FrameBuf
	Name() string
	GetMTU() (int, error)
	GetHardwareAddr() (net.HardwareAddr, error)
}
<|endoftext|>