<commit_before>package server

import (
	"fmt"
	"net"
	"sync"

	"github.com/peer-calls/peer-calls/server/promise"
	"github.com/peer-calls/peer-calls/server/stringmux"
	"github.com/peer-calls/peer-calls/server/udpmux"
	"github.com/pion/sctp"
)

// TransportManager is in charge of managing server-to-server transports.
type TransportManager struct {
	params        *TransportManagerParams
	udpMux        *udpmux.UDPMux
	closeChan     chan struct{}
	factoriesChan chan *ServerTransportFactory
	closeOnce     sync.Once
	mu            sync.Mutex
	wg            sync.WaitGroup
	logger        Logger

	factories map[*stringmux.StringMux]*ServerTransportFactory
}

type StreamTransport struct {
	Transport
	StreamID string

	association *sctp.Association
	stringMux   *stringmux.StringMux
}

func (st *StreamTransport) Close() error {
	err := st.Transport.Close()

	_ = st.association.Close()

	_ = st.stringMux.Close()

	return err
}

type TransportManagerParams struct {
	Conn          net.PacketConn
	LoggerFactory LoggerFactory
}

func NewTransportManager(params TransportManagerParams) *TransportManager {
	udpMux := udpmux.New(udpmux.Params{
		Conn:          params.Conn,
		MTU:           uint32(receiveMTU),
		LoggerFactory: params.LoggerFactory,
		ReadChanSize:  100,
	})

	t := &TransportManager{
		params:        &params,
		udpMux:        udpMux,
		closeChan:     make(chan struct{}),
		factoriesChan: make(chan *ServerTransportFactory),
		factories:     make(map[*stringmux.StringMux]*ServerTransportFactory),
		logger:        params.LoggerFactory.GetLogger("transportmanager"),
	}

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()
		t.start()
	}()

	return t
}

func (t *TransportManager) start() {
	for {
		conn, err := t.udpMux.AcceptConn()
		if err != nil {
			t.logger.Printf("Error accepting udpMux conn: %s", err)
			return
		}

		factory, err := t.createServerTransportFactory(conn)
		if err != nil {
			t.logger.Printf("Error creating transport factory: %s", err)
			return
		}

		t.factoriesChan <- factory
	}
}

func (t *TransportManager) create(conn udpmux.Conn) {
	t.createServerTransportFactory(conn)
}

// createServerTransportFactory creates a new ServerTransportFactory for the
// provided association.
func (t *TransportManager) createServerTransportFactory(conn udpmux.Conn) (*ServerTransportFactory, error) {
	t.mu.Lock()
	defer t.mu.Unlock()

	stringMux := stringmux.New(stringmux.Params{
		LoggerFactory: t.params.LoggerFactory,
		Conn:          conn,
		MTU:           uint32(receiveMTU), // TODO not sure if this is ok
		ReadChanSize:  100,
	})

	factory := NewServerTransportFactory(t.params.LoggerFactory, &t.wg, stringMux)
	t.factories[stringMux] = factory

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()
		<-stringMux.CloseChannel()

		t.mu.Lock()
		defer t.mu.Unlock()

		delete(t.factories, stringMux)
	}()

	return factory, nil
}

func (t *TransportManager) AcceptTransportFactory() (*ServerTransportFactory, error) {
	factory, ok := <-t.factoriesChan
	if !ok {
		return nil, fmt.Errorf("TransportManager is tearing down")
	}
	return factory, nil
}

func (t *TransportManager) GetTransportFactory(raddr net.Addr) (*ServerTransportFactory, error) {
	conn, err := t.udpMux.GetConn(raddr)
	if err != nil {
		return nil, fmt.Errorf("Error getting conn for raddr %s: %w", raddr, err)
	}

	return t.createServerTransportFactory(conn)
}

func (t *TransportManager) Close() error {
	err := t.close()

	t.wg.Wait()

	return err
}

func (t *TransportManager) CloseChannel() <-chan struct{} {
	return t.closeChan
}

func (t *TransportManager) close() error {
	t.mu.Lock()
	defer t.mu.Unlock()

	err := t.udpMux.Close()

	t.closeOnce.Do(func() {
		close(t.factoriesChan)

		for stringMux, factory := range t.factories {
			_ = stringMux.Close()

			factory.Close()

			delete(t.factories, stringMux)
		}

		close(t.closeChan)
	})

	return err
}

type ServerTransportFactory struct {
	loggerFactory  LoggerFactory
	stringMux      *stringmux.StringMux
	transportsChan chan *StreamTransport
	transports     map[string]*StreamTransport
	mu             sync.Mutex
	wg             *sync.WaitGroup
}

func NewServerTransportFactory(
	loggerFactory LoggerFactory,
	wg *sync.WaitGroup,
	stringMux *stringmux.StringMux,
) *ServerTransportFactory {
	return &ServerTransportFactory{
		loggerFactory:  loggerFactory,
		stringMux:      stringMux,
		transportsChan: make(chan *StreamTransport),
		transports:     map[string]*StreamTransport{},
		wg:             wg,
	}
}

// AcceptTransport returns a TransportPromise. This promise can be either
// canceled by using the Cancel method, or it can be Waited for by using the
// Wait method. The Wait() method must be called and the error must be checked
// and handled.
func (t *ServerTransportFactory) AcceptTransport() *TransportPromise {
	conn, err := t.stringMux.AcceptConn()

	tp := NewTransportPromise(t.wg)

	if err != nil {
		tp.reject(fmt.Errorf("Error AcceptTransport: %w", err))
		return tp
	}

	t.createTransportAsync(tp, conn)

	return tp
}

func (t *ServerTransportFactory) createTransportAsync(tp *TransportPromise, conn stringmux.Conn) {
	raddr := conn.RemoteAddr()
	streamID := conn.StreamID()

	// This can be optimized in the future since a StringMux has a minimal
	// overhead of 3 bytes, and only a single bit is needed.
	localMux := stringmux.New(stringmux.Params{
		Conn:          conn,
		LoggerFactory: t.loggerFactory,
		MTU:           uint32(receiveMTU),
		ReadChanSize:  100,
	})

	// TODO maybe we'll need to handle localMux Accept as well

	sctpConn, err := localMux.GetConn("s")
	if err != nil {
		localMux.Close()
		tp.done(nil, fmt.Errorf("Error creating 's' conn for raddr: %s %s: %w", raddr, streamID, err))
		return
	}

	mediaConn, err := localMux.GetConn("m")
	if err != nil {
		sctpConn.Close()
		localMux.Close()
		tp.done(nil, fmt.Errorf("Error creating 'm' conn for raddr: %s %s: %w", raddr, streamID, err))
		return
	}

	// Ensure we don't get stuck at sctp.Client() forever.
	tp.onCancel(func() {
		_ = localMux.Close()
	})

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()

		transport, err := t.createTransport(conn.RemoteAddr(), conn.StreamID(), localMux, mediaConn, sctpConn)
		if err != nil {
			mediaConn.Close()
			sctpConn.Close()
			localMux.Close()
		}

		tp.done(transport, err)
	}()
}

func (t *ServerTransportFactory) createTransport(
	raddr net.Addr, streamID string,
	localMux *stringmux.StringMux,
	mediaConn net.Conn,
	sctpConn net.Conn,
) (*StreamTransport, error) {
	association, err := sctp.Client(sctp.Config{
		NetConn:       sctpConn,
		LoggerFactory: NewPionLoggerFactory(t.loggerFactory),
		// MaxReceiveBufferSize: uint32(receiveMTU),
	})

	if err != nil {
		return nil, fmt.Errorf("Error creating sctp association for raddr: %s %s: %w", raddr, streamID, err)
	}

	// TODO check if handling association.Accept is necessary since OpenStream
	// can return an error. Perhaps we need to wait for Accept as well, check the
	// StreamIdentifier and log stream IDs we are not expecting.

	metadataStream, err := association.OpenStream(0, sctp.PayloadTypeWebRTCBinary)
	if err != nil {
		association.Close()
		return nil, fmt.Errorf("Error creating metadata sctp stream for raddr: %s %s: %w", raddr, streamID, err)
	}

	dataStream, err := association.OpenStream(1, sctp.PayloadTypeWebRTCBinary)
	if err != nil {
		metadataStream.Close()
		association.Close()
		return nil, fmt.Errorf("Error creating data sctp stream for raddr: %s %s: %w", raddr, streamID, err)
	}

	transport := NewServerTransport(t.loggerFactory, mediaConn, dataStream, metadataStream)

	streamTransport := &StreamTransport{transport, streamID, association, localMux}
	t.transports[streamID] = streamTransport

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()
		<-transport.CloseChannel()

		t.mu.Lock()
		defer t.mu.Unlock()

		delete(t.transports, streamID)
	}()

	return streamTransport, nil
}

// NewTransport returns a TransportPromise. This promise can be either canceled
// by using the Cancel method, or it can be Waited for by using the Wait
// method. The Wait() method must be called and the error must be checked and
// handled.
func (t *ServerTransportFactory) NewTransport(streamID string) *TransportPromise {
	t.mu.Lock()
	defer t.mu.Unlock()

	tp := NewTransportPromise(t.wg)

	if _, ok := t.transports[streamID]; ok {
		tp.reject(fmt.Errorf("Transport already exists: %s", streamID))
		return tp
	}

	conn, err := t.stringMux.GetConn(streamID)

	if err != nil {
		tp.reject(fmt.Errorf("Error retrieving transport conn: %s: %s", streamID, err))
		return tp
	}

	t.createTransportAsync(tp, conn)

	return tp
}

func (t *ServerTransportFactory) Close() error {
	t.mu.Lock()
	defer t.mu.Unlock()

	for streamID, transport := range t.transports {
		transport.Close()
		delete(t.transports, streamID)
	}

	return nil
}

type TransportPromise struct {
	promise      promise.Promise
	cancelChan   chan struct{}
	transport    *StreamTransport
	resolveOnce  sync.Once
	cancelOnce   sync.Once
	onCancelOnce sync.Once
	onCancelHdlr func()
	wg           *sync.WaitGroup
}

func NewTransportPromise(wg *sync.WaitGroup) *TransportPromise {
	return &TransportPromise{
		promise:    promise.New(),
		cancelChan: make(chan struct{}),
		transport:  nil,
		wg:         wg,
	}
}

func (t *TransportPromise) done(transport *StreamTransport, err error) {
	t.resolveOnce.Do(func() {
		if err != nil {
			t.promise.Reject(err)
		} else {
			t.transport = transport
			t.promise.Resolve()
		}
	})
}

func (t *TransportPromise) resolve(transport *StreamTransport) {
	t.done(transport, nil)
}

func (t *TransportPromise) reject(err error) {
	t.done(nil, err)
}

func (t *TransportPromise) onCancel(handleClose func()) {
	t.onCancelOnce.Do(func() {
		t.onCancelHdlr = handleClose
	})
}

var ErrCanceled = fmt.Errorf("Canceled")

// Cancel waits for the transport in another goroutine and closes it as soon as
// the promise resolves.
func (t *TransportPromise) Cancel() {
	t.wg.Add(1)

	go func() {
		defer t.wg.Done()

		t.cancelOnce.Do(func() {
			if t.onCancelHdlr != nil {
				t.onCancelHdlr()
			}

			close(t.cancelChan)
			_ = t.promise.Wait()
			if t.transport != nil {
				t.transport.Close()
			}
		})
	}()
}

// Wait returns the Transport or error after the promise is resolved or
// rejected. Promise can be rejected if an error occurs or if a promise is
// canceled using the Cancel function.
func (t *TransportPromise) Wait() (*StreamTransport, error) {
	select {
	case <-t.promise.WaitChannel():
		err := t.promise.Wait()
		return t.transport, err
	case <-t.cancelChan:
		return nil, ErrCanceled
	}
}
<commit_msg>Attempt to fix CI fail<commit_after>package server

import (
	"fmt"
	"net"
	"sync"

	"github.com/peer-calls/peer-calls/server/promise"
	"github.com/peer-calls/peer-calls/server/stringmux"
	"github.com/peer-calls/peer-calls/server/udpmux"
	"github.com/pion/sctp"
)

// TransportManager is in charge of managing server-to-server transports.
type TransportManager struct {
	params        *TransportManagerParams
	udpMux        *udpmux.UDPMux
	closeChan     chan struct{}
	factoriesChan chan *ServerTransportFactory
	closeOnce     sync.Once
	mu            sync.Mutex
	wg            sync.WaitGroup
	logger        Logger

	factories map[*stringmux.StringMux]*ServerTransportFactory
}

type StreamTransport struct {
	Transport
	StreamID string

	association *sctp.Association
	stringMux   *stringmux.StringMux
}

func (st *StreamTransport) Close() error {
	err := st.Transport.Close()

	_ = st.association.Close()

	_ = st.stringMux.Close()

	return err
}

type TransportManagerParams struct {
	Conn          net.PacketConn
	LoggerFactory LoggerFactory
}

func NewTransportManager(params TransportManagerParams) *TransportManager {
	udpMux := udpmux.New(udpmux.Params{
		Conn:          params.Conn,
		MTU:           uint32(receiveMTU),
		LoggerFactory: params.LoggerFactory,
		ReadChanSize:  100,
	})

	t := &TransportManager{
		params:        &params,
		udpMux:        udpMux,
		closeChan:     make(chan struct{}),
		factoriesChan: make(chan *ServerTransportFactory),
		factories:     make(map[*stringmux.StringMux]*ServerTransportFactory),
		logger:        params.LoggerFactory.GetLogger("transportmanager"),
	}

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()
		t.start()
	}()

	return t
}

func (t *TransportManager) start() {
	for {
		conn, err := t.udpMux.AcceptConn()
		if err != nil {
			t.logger.Printf("Error accepting udpMux conn: %s", err)
			return
		}

		factory, err := t.createServerTransportFactory(conn)
		if err != nil {
			t.logger.Printf("Error creating transport factory: %s", err)
			return
		}

		t.factoriesChan <- factory
	}
}

func (t *TransportManager) create(conn udpmux.Conn) {
	t.createServerTransportFactory(conn)
}

// createServerTransportFactory creates a new ServerTransportFactory for the
// provided association.
func (t *TransportManager) createServerTransportFactory(conn udpmux.Conn) (*ServerTransportFactory, error) {
	t.mu.Lock()
	defer t.mu.Unlock()

	stringMux := stringmux.New(stringmux.Params{
		LoggerFactory: t.params.LoggerFactory,
		Conn:          conn,
		MTU:           uint32(receiveMTU), // TODO not sure if this is ok
		ReadChanSize:  100,
	})

	factory := NewServerTransportFactory(t.params.LoggerFactory, &t.wg, stringMux)
	t.factories[stringMux] = factory

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()
		<-stringMux.CloseChannel()

		t.mu.Lock()
		defer t.mu.Unlock()

		delete(t.factories, stringMux)
	}()

	return factory, nil
}

func (t *TransportManager) AcceptTransportFactory() (*ServerTransportFactory, error) {
	factory, ok := <-t.factoriesChan
	if !ok {
		return nil, fmt.Errorf("TransportManager is tearing down")
	}
	return factory, nil
}

func (t *TransportManager) GetTransportFactory(raddr net.Addr) (*ServerTransportFactory, error) {
	conn, err := t.udpMux.GetConn(raddr)
	if err != nil {
		return nil, fmt.Errorf("Error getting conn for raddr %s: %w", raddr, err)
	}

	return t.createServerTransportFactory(conn)
}

func (t *TransportManager) Close() error {
	err := t.close()

	t.wg.Wait()

	return err
}

func (t *TransportManager) CloseChannel() <-chan struct{} {
	return t.closeChan
}

func (t *TransportManager) close() error {
	t.mu.Lock()
	defer t.mu.Unlock()

	err := t.udpMux.Close()

	t.closeOnce.Do(func() {
		close(t.factoriesChan)

		for stringMux, factory := range t.factories {
			_ = stringMux.Close()

			factory.Close()

			delete(t.factories, stringMux)
		}

		close(t.closeChan)
	})

	return err
}

type ServerTransportFactory struct {
	loggerFactory  LoggerFactory
	stringMux      *stringmux.StringMux
	transportsChan chan *StreamTransport
	transports     map[string]*StreamTransport
	mu             sync.Mutex
	wg             *sync.WaitGroup
}

func NewServerTransportFactory(
	loggerFactory LoggerFactory,
	wg *sync.WaitGroup,
	stringMux *stringmux.StringMux,
) *ServerTransportFactory {
	return &ServerTransportFactory{
		loggerFactory:  loggerFactory,
		stringMux:      stringMux,
		transportsChan: make(chan *StreamTransport),
		transports:     map[string]*StreamTransport{},
		wg:             wg,
	}
}

// AcceptTransport returns a TransportPromise. This promise can be either
// canceled by using the Cancel method, or it can be Waited for by using the
// Wait method. The Wait() method must be called and the error must be checked
// and handled.
func (t *ServerTransportFactory) AcceptTransport() *TransportPromise {
	conn, err := t.stringMux.AcceptConn()

	tp := NewTransportPromise(t.wg)

	if err != nil {
		tp.reject(fmt.Errorf("Error AcceptTransport: %w", err))
		return tp
	}

	t.createTransportAsync(tp, conn, true)

	return tp
}

func (t *ServerTransportFactory) createTransportAsync(tp *TransportPromise, conn stringmux.Conn, server bool) {
	raddr := conn.RemoteAddr()
	streamID := conn.StreamID()

	// This can be optimized in the future since a StringMux has a minimal
	// overhead of 3 bytes, and only a single bit is needed.
	localMux := stringmux.New(stringmux.Params{
		Conn:          conn,
		LoggerFactory: t.loggerFactory,
		MTU:           uint32(receiveMTU),
		ReadChanSize:  100,
	})

	// TODO maybe we'll need to handle localMux Accept as well

	sctpConn, err := localMux.GetConn("s")
	if err != nil {
		localMux.Close()
		tp.done(nil, fmt.Errorf("Error creating 's' conn for raddr: %s %s: %w", raddr, streamID, err))
		return
	}

	mediaConn, err := localMux.GetConn("m")
	if err != nil {
		sctpConn.Close()
		localMux.Close()
		tp.done(nil, fmt.Errorf("Error creating 'm' conn for raddr: %s %s: %w", raddr, streamID, err))
		return
	}

	// Ensure we don't get stuck at sctp.Client() forever.
	tp.onCancel(func() {
		_ = localMux.Close()
	})

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()

		transport, err := t.createTransport(conn.RemoteAddr(), conn.StreamID(), localMux, mediaConn, sctpConn, server)
		if err != nil {
			mediaConn.Close()
			sctpConn.Close()
			localMux.Close()
		}

		tp.done(transport, err)
	}()
}

func (t *ServerTransportFactory) createTransport(
	raddr net.Addr, streamID string,
	localMux *stringmux.StringMux,
	mediaConn net.Conn,
	sctpConn net.Conn,
	server bool,
) (*StreamTransport, error) {
	sctpConfig := sctp.Config{
		NetConn:       sctpConn,
		LoggerFactory: NewPionLoggerFactory(t.loggerFactory),
	}

	var association *sctp.Association
	var err error

	if server {
		association, err = sctp.Server(sctpConfig)
	} else {
		association, err = sctp.Client(sctpConfig)
	}

	if err != nil {
		return nil, fmt.Errorf("Error creating sctp association for raddr: %s %s: %w", raddr, streamID, err)
	}

	// TODO check if handling association.Accept is necessary since OpenStream
	// can return an error. Perhaps we need to wait for Accept as well, check the
	// StreamIdentifier and log stream IDs we are not expecting.

	metadataStream, err := association.OpenStream(0, sctp.PayloadTypeWebRTCBinary)
	if err != nil {
		association.Close()
		return nil, fmt.Errorf("Error creating metadata sctp stream for raddr: %s %s: %w", raddr, streamID, err)
	}

	dataStream, err := association.OpenStream(1, sctp.PayloadTypeWebRTCBinary)
	if err != nil {
		metadataStream.Close()
		association.Close()
		return nil, fmt.Errorf("Error creating data sctp stream for raddr: %s %s: %w", raddr, streamID, err)
	}

	transport := NewServerTransport(t.loggerFactory, mediaConn, dataStream, metadataStream)

	streamTransport := &StreamTransport{transport, streamID, association, localMux}
	t.transports[streamID] = streamTransport

	t.wg.Add(1)
	go func() {
		defer t.wg.Done()
		<-transport.CloseChannel()

		t.mu.Lock()
		defer t.mu.Unlock()

		delete(t.transports, streamID)
	}()

	return streamTransport, nil
}

// NewTransport returns a TransportPromise. This promise can be either canceled
// by using the Cancel method, or it can be Waited for by using the Wait
// method. The Wait() method must be called and the error must be checked and
// handled.
func (t *ServerTransportFactory) NewTransport(streamID string) *TransportPromise {
	t.mu.Lock()
	defer t.mu.Unlock()

	tp := NewTransportPromise(t.wg)

	if _, ok := t.transports[streamID]; ok {
		tp.reject(fmt.Errorf("Transport already exists: %s", streamID))
		return tp
	}

	conn, err := t.stringMux.GetConn(streamID)

	if err != nil {
		tp.reject(fmt.Errorf("Error retrieving transport conn: %s: %s", streamID, err))
		return tp
	}

	t.createTransportAsync(tp, conn, false)

	return tp
}

func (t *ServerTransportFactory) Close() error {
	t.mu.Lock()
	defer t.mu.Unlock()

	for streamID, transport := range t.transports {
		transport.Close()
		delete(t.transports, streamID)
	}

	return nil
}

type TransportPromise struct {
	promise      promise.Promise
	cancelChan   chan struct{}
	transport    *StreamTransport
	resolveOnce  sync.Once
	cancelOnce   sync.Once
	onCancelOnce sync.Once
	onCancelHdlr func()
	wg           *sync.WaitGroup
}

func NewTransportPromise(wg *sync.WaitGroup) *TransportPromise {
	return &TransportPromise{
		promise:    promise.New(),
		cancelChan: make(chan struct{}),
		transport:  nil,
		wg:         wg,
	}
}

func (t *TransportPromise) done(transport *StreamTransport, err error) {
	t.resolveOnce.Do(func() {
		if err != nil {
			t.promise.Reject(err)
		} else {
			t.transport = transport
			t.promise.Resolve()
		}
	})
}

func (t *TransportPromise) resolve(transport *StreamTransport) {
	t.done(transport, nil)
}

func (t *TransportPromise) reject(err error) {
	t.done(nil, err)
}

func (t *TransportPromise) onCancel(handleClose func()) {
	t.onCancelOnce.Do(func() {
		t.onCancelHdlr = handleClose
	})
}

var ErrCanceled = fmt.Errorf("Canceled")

// Cancel waits for the transport in another goroutine and closes it as soon as
// the promise resolves.
func (t *TransportPromise) Cancel() {
	t.wg.Add(1)

	go func() {
		defer t.wg.Done()

		t.cancelOnce.Do(func() {
			if t.onCancelHdlr != nil {
				t.onCancelHdlr()
			}

			close(t.cancelChan)
			_ = t.promise.Wait()
			if t.transport != nil {
				t.transport.Close()
			}
		})
	}()
}

// Wait returns the Transport or error after the promise is resolved or
// rejected. Promise can be rejected if an error occurs or if a promise is
// canceled using the Cancel function.
func (t *TransportPromise) Wait() (*StreamTransport, error) {
	select {
	case <-t.promise.WaitChannel():
		err := t.promise.Wait()
		return t.transport, err
	case <-t.cancelChan:
		return nil, ErrCanceled
	}
}
<|endoftext|>


<commit_before>// Package padding performs pkcs#7 padding and unpadding
package padding

import (
	"github.com/DonMills/s3encrypt/errorhandle"

	"crypto/aes"
	"errors"
)

//Unpad This function unpads pkcs#7 padding
func Unpad(in []byte) []byte {
	if len(in) == 0 {
		errorhandle.GenError(errors.New("Unpad - No data sent to unpad"))
	}

	padding := in[len(in)-1]
	if int(padding) > len(in) || padding > aes.BlockSize {
		errorhandle.GenError(errors.New("Unpad - Padding larger than BlockSize or data"))
	} else if padding == 0 {
		errorhandle.GenError(errors.New("Unpad - Does not contain proper padding"))
	}

	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
		if in[i] != padding {
			errorhandle.GenError(errors.New("Unpad - Padded value larger than padding"))
		}
	}
	return in[:len(in)-int(padding)]
}

//Pad This function does pkcs#7 padding
func Pad(in []byte) []byte {
	padding := aes.BlockSize - (len(in) % aes.BlockSize)
	for i := 0; i < padding; i++ {
		in = append(in, byte(padding))
	}
	return in
}
<commit_msg>fixing padding imports<commit_after>// Package padding performs pkcs#7 padding and unpadding
package padding

import (
	"github.com/DonMills/kmsencrypt/errorhandle"

	"crypto/aes"
	"errors"
)

//Unpad This function unpads pkcs#7 padding
func Unpad(in []byte) []byte {
	if len(in) == 0 {
		errorhandle.GenError(errors.New("Unpad - No data sent to unpad"))
	}

	padding := in[len(in)-1]
	if int(padding) > len(in) || padding > aes.BlockSize {
		errorhandle.GenError(errors.New("Unpad - Padding larger than BlockSize or data"))
	} else if padding == 0 {
		errorhandle.GenError(errors.New("Unpad - Does not contain proper padding"))
	}

	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
		if in[i] != padding {
			errorhandle.GenError(errors.New("Unpad - Padded value larger than padding"))
		}
	}
	return in[:len(in)-int(padding)]
}

//Pad This function does pkcs#7 padding
func Pad(in []byte) []byte {
	padding := aes.BlockSize - (len(in) % aes.BlockSize)
	for i := 0; i < padding; i++ {
		in = append(in, byte(padding))
	}
	return in
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

type Action interface {
	getName() string
	getHint() string
	firstUp() Action
	getQuestions() Questions
	nextQuestion() *Question
	askQuestion()
}

const (
	start_action, help_action = "/start", "/help"

	say_action      = "/say"
	say_action_hint = "/say <message>"

	chat_action = "/chat"

	review_action      = "/review"
	review_action_hint = "/review <days>"

	remind_action      = "/remind"
	remind_action_hint = "/remind in <days> to <message>"

	default_script = "default"
)

func NewAction(in *Incoming, s *session, actionName string) Action {

	if s != nil {
		s.User = in.msg.Sender
	}

	cmd := actionName

	if in.isCmd() {
		cmd = in.getCmd()
	}

	if cmd == say_action {
		return SayAction{in: in, s: s}
	} else if cmd == remind_action {
		return &RemindAction{in: in, s: s}
	} else if cmd == review_action {
		return &ReviewAction{in: in, s: s}
	} else if cmd == chat_action {
		return &ChatAction{in: in, s: s}
	} else if in.isSticker() {
		return &StickerChatAction{in: in, s: s}
	}
	return &HelpAction{in: in, s: s}
}

func load(name string, q interface{}) {

	if strings.Contains(name, "/") {
		name = strings.Split(name, "/")[1]
	}

	absPath, _ := filepath.Abs(fmt.Sprintf("config/%s.json", name))

	log.Println("QandA", absPath)

	file, err := os.Open(absPath)
	if err != nil {
		log.Println("could not load QandA language file", err.Error())
		absPath, _ = filepath.Abs(fmt.Sprintf("lib/config/%s.json", name))
		log.Println("QandA path ", absPath)

		file, err = os.Open(absPath)
	}

	err = json.NewDecoder(file).Decode(&q)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}

	return
}

type SayAction struct {
	s  *session
	in *Incoming
}

func (a SayAction) getName() string {
	return say_action
}
func (a SayAction) getHint() string {
	return say_action_hint
}
func (a SayAction) firstUp() Action {
	a.s.save(a.in.getNote())
	return a
}

func (a SayAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}

	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a SayAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
}

func (a SayAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)
	return q.Questions
}

type HelpAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a HelpAction) getName() string {
	return say_action
}
func (a HelpAction) getHint() string {
	return say_action_hint
}
func (a HelpAction) firstUp() Action {
	log.Println("help first up")
	helpInfo := fmt.Sprintf("%s %s %s %s ",
		fmt.Sprintf("Hey %s! We can't do it all but we can:\n\n", a.in.sender().Username),
		chat_action+" - to have a *quick chat* about what your up-to \n\n",
		say_action_hint+" - to say *anything* thats on your mind \n\n",
		review_action_hint+" - to review what has been happening \n\n",
	)
	a.s.send(helpInfo)
	return a
}

func (a HelpAction) nextQuestion() *Question {
	return nil
}

func (a HelpAction) getQuestions() Questions {
	return nil
}

func (a HelpAction) askQuestion() {
	return
}

type ChatAction struct {
	s  *session
	in *Incoming
}

func (a ChatAction) getName() string {
	return chat_action
}
func (a ChatAction) getHint() string {
	return ""
}
func (a ChatAction) firstUp() Action {
	//nothing to doFirst
	return a
}

func (a ChatAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)
	return q.Questions
}

func (a ChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ChatAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}

type ReviewAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a ReviewAction) getName() string {
	return review_action
}
func (a ReviewAction) getHint() string {
	return review_action_hint
}
func (a ReviewAction) firstUp() Action {
	log.Println("doFirsting review ...")
	n := a.s.getNotes(a.in.msg)

	saidTxt := fmt.Sprintf("%s you said: \n\n %s", a.in.sender().Username, n.FilterBy(said_tag).ToString())
	a.s.send(saidTxt)
	talkedTxt := fmt.Sprintf("%s we talked about: \n\n %s", a.in.sender().Username, n.FilterBy(chat_tag).ToString())
	a.s.send(talkedTxt)
	return a
}

func (a ReviewAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)

	return q.Questions
}

func (a ReviewAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ReviewAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}

type RemindAction struct {
	s  *session
	in *Incoming
}

func (a RemindAction) getName() string {
	return remind_action
}
func (a RemindAction) getHint() string {
	return remind_action_hint
}
func (a RemindAction) firstUp() Action {
	log.Println("remind me ...")
	a.s.save(a.in.getNote())
	return a
}

func (a RemindAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)

	return q.Questions
}

func (a RemindAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a RemindAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}

type StickerChatAction struct {
	s        *session
	in       *Incoming
	stickers Stickers
}

func (a StickerChatAction) getName() string {
	return chat_action
}
func (a StickerChatAction) getHint() string {
	return ""
}

func (a StickerChatAction) firstUp() Action {
	a.stickers = LoadKnownStickers()
	return a
}

func (a StickerChatAction) getQuestions() Questions {
	var q struct {
		Questions `json:"QandA"`
	}
	load(a.getName(), &q)
	return q.Questions
}

func (a StickerChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isSticker() {

		sticker := a.stickers.FindSticker(a.in.msg.Sticker.FileID)
		a.in.msg.Text = sticker.Meaning
		next, save := q.nextFrom(sticker.Ids...)
		if save {
			a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
		}
		return next
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a StickerChatAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}
<commit_msg>link to bot stickers<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

type Action interface {
	getName() string
	getHint() string
	firstUp() Action
	getQuestions() Questions
	nextQuestion() *Question
	askQuestion()
}

const (
	start_action, help_action = "/start", "/help"

	say_action      = "/say"
	say_action_hint = "/say <message>"

	chat_action = "/chat"

	review_action      = "/review"
	review_action_hint = "/review <days>"

	remind_action      = "/remind"
	remind_action_hint = "/remind in <days> to <message>"

	default_script = "default"
)

func NewAction(in *Incoming, s *session, actionName string) Action {

	if s != nil {
		s.User = in.msg.Sender
	}

	cmd := actionName

	if in.isCmd() {
		cmd = in.getCmd()
	}

	if cmd == say_action {
		return SayAction{in: in, s: s}
	} else if cmd == remind_action {
		return &RemindAction{in: in, s: s}
	} else if cmd == review_action {
		return &ReviewAction{in: in, s: s}
	} else if cmd == chat_action {
		return &ChatAction{in: in, s: s}
	} else if in.isSticker() {
		return &StickerChatAction{in: in, s: s}
	}
	return &HelpAction{in: in, s: s}
}

func load(name string, q interface{}) {

	if strings.Contains(name, "/") {
		name = strings.Split(name, "/")[1]
	}

	absPath, _ := filepath.Abs(fmt.Sprintf("config/%s.json", name))

	log.Println("QandA", absPath)

	file, err := os.Open(absPath)
	if err != nil {
		log.Println("could not load QandA language file", err.Error())
		absPath, _ = filepath.Abs(fmt.Sprintf("lib/config/%s.json", name))
		log.Println("QandA path ", absPath)

		file, err = os.Open(absPath)
	}

	err = json.NewDecoder(file).Decode(&q)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}

	return
}

type SayAction struct {
	s  *session
	in *Incoming
}

func (a SayAction) getName() string {
	return say_action
}
func (a SayAction) getHint() string {
	return say_action_hint
}
func (a SayAction) firstUp() Action {
	a.s.save(a.in.getNote())
	return a
}

func (a SayAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}

	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a SayAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
}

func (a SayAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)
	return q.Questions
}

type HelpAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a HelpAction) getName() string {
	return say_action
}
func (a HelpAction) getHint() string {
	return say_action_hint
}
func (a HelpAction) firstUp() Action {
	log.Println("help first up")
	helpInfo := fmt.Sprintf("%s %s %s %s %s ",
		fmt.Sprintf("Hey %s! We can't do it all but we can:\n\n", a.in.sender().Username),
		chat_action+" - to have a *quick chat* about what your up-to \n\n",
		say_action_hint+" - to say *anything* thats on your mind \n\n",
		review_action_hint+" - to review what has been happening \n\n",
		"Stickers - we have those to help express yourself!! [Get them here](https://telegram.me/addstickers/betes) \n\n",
	)
	a.s.send(helpInfo)
	return a
}

func (a HelpAction) nextQuestion() *Question {
	return nil
}

func (a HelpAction) getQuestions() Questions {
	return nil
}

func (a HelpAction) askQuestion() {
	return
}

type ChatAction struct {
	s  *session
	in *Incoming
}

func (a ChatAction) getName() string {
	return chat_action
}
func (a ChatAction) getHint() string {
	return ""
}
func (a ChatAction) firstUp() Action {
	//nothing to doFirst
	return a
}

func (a ChatAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)
	return q.Questions
}

func (a ChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ChatAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}

type ReviewAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a ReviewAction) getName() string {
	return review_action
}
func (a ReviewAction) getHint() string {
	return review_action_hint
}
func (a ReviewAction) firstUp() Action {
	log.Println("doFirsting review ...")
	n := a.s.getNotes(a.in.msg)

	saidTxt := fmt.Sprintf("%s you said: \n\n %s", a.in.sender().Username, n.FilterBy(said_tag).ToString())
	a.s.send(saidTxt)
	talkedTxt := fmt.Sprintf("%s we talked about: \n\n %s", a.in.sender().Username, n.FilterBy(chat_tag).ToString())
	a.s.send(talkedTxt)
	return a
}

func (a ReviewAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)

	return q.Questions
}

func (a ReviewAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ReviewAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}

type RemindAction struct {
	s  *session
	in *Incoming
}

func (a RemindAction) getName() string {
	return remind_action
}
func (a RemindAction) getHint() string {
	return remind_action_hint
}
func (a RemindAction) firstUp() Action {
	log.Println("remind me ...")
	a.s.save(a.in.getNote())
	return a
}

func (a RemindAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)

	return q.Questions
}

func (a RemindAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a RemindAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}

type StickerChatAction struct {
	s        *session
	in       *Incoming
	stickers Stickers
}

func (a StickerChatAction) getName() string {
	return chat_action
}
func (a StickerChatAction) getHint() string {
	return ""
}

func (a StickerChatAction) firstUp() Action {
	a.stickers = LoadKnownStickers()
	return a
}

func (a StickerChatAction) getQuestions() Questions {
	var q struct {
		Questions `json:"QandA"`
	}
	load(a.getName(), &q)
	return q.Questions
}

func (a StickerChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isSticker() {

		sticker := a.stickers.FindSticker(a.in.msg.Sticker.FileID)
		a.in.msg.Text = sticker.Meaning
		next, save := q.nextFrom(sticker.Ids...)
		if save {
			a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
		}
		return next
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a StickerChatAction) askQuestion() {
	if q := a.nextQuestion(); q != nil {
		a.s.send(q.Context...)
		a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	}
	return
}
<|endoftext|>


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

package geom

import (
	"testing"
)

func TestMat4Id(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	id := Mat4{
		{1, 0, 0, 0},
		{0, 1, 0, 0},
		{0, 0, 1, 0},
		{0, 0, 0, 1},
	}
	mp := m.Id()
	if m != id {
		t.Errorf("m.Id() does not set m to be the identity matrix, got instead: %v", m)
	}
	if mp != &m {
		t.Errorf("m.Id() does not return the pointer to m")
	}
}

func TestMat4Ortho(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, 0, 1},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{0.5, 0, 0, 0},
			{0, 0.5, 0, 0},
			{0, 0, 0.5, 0},
			{0, 0, 0, 1.0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{2, 0, 0, 0},
			{0, 2, 0, 0},
			{0, 0, -2, 0},
			{-3, -7, -11, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Ortho(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Ortho(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Frustum(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -1, 0},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -2, 0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{10, 0, 0, 0},
			{0, 10, 0, 0},
			{3, 7, -11, -1},
			{0, 0, -60, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Frustum(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Frustum(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Perspective(t *testing.T) {
	tests := []struct {
		fovy, a, n, f float32
		want          Mat4
	}{
		{65, 1.5, 5, 10, Mat4{
			{0.35279062, 0, 0, 0},
			{0, 0.52918595, 0, 0},
			{0, 0, -3, -1},
			{0, 0, -20, 0},
		}},
		{70, 1.33, 1, 30, Mat4{
			{1.5868644, 0, 0, 0},
			{0, 2.1105297, 0, 0},
			{0, 0, -1.0689656, -1},
			{0, 0, -2.0689654, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Perspective(tt.fovy, tt.a, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Perspective(%g, %g, %g, %g) = %v, want %v",
				tt.fovy, tt.a, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4LookAt(t *testing.T) {
	tests := []struct {
		eye, center, up Vec3
		want            Mat4
	}{
		{V3(1, 1, 1), V3(1, 1, 0), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{-1, -1, -1, 1},
		}},
		{V3(0, 0, 1), V3(0, 0, -1), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, -1, 1},
		}},
		{V3(20, 80, 15), V3(15, 0, 12), V3(0, -1, 0), Mat4{
			{-0.5144958, 0.8552243, 0.06233464, 0},
			{0, -0.07269406, 0.99735427, 0},
			{0.857493, 0.5131346, 0.037400786, 0},
			{-2.5724783, -18.985981, -81.596054, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.LookAt(tt.eye, tt.center, tt.up)
		if m != tt.want {
			t.Errorf("m.LookAt(%s, %s, %s) = %v, want %v",
				tt.eye, tt.center, tt.up, m, tt.want)
		}
	}
}

func TestMat4Floats(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	want := [16]float32{11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44}
	f := m.Floats()
	if *f != want {
		t.Errorf("%v.Floats() = %v, want %v", m, *f, want)
	}
	f[6] = 99
	if m[1][2] != 99 {
		t.Errorf("Pointer to float32 array returned by Floats() does not point to matrix data.")
	}
}

func TestMat4Mul(t *testing.T) {
	tests := []struct {
		a, b, want Mat4
	}{
		{Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{34, 44, 54, 64},
			{82, 108, 134, 160},
			{34, 44, 54, 64},
			{82, 108, 134, 160},
		}},

		{Mat4{
			{2.1, 3.2, 1.2, 0},
			{4.6, -5.3, 5.4, 8.4},
			{-9.1, 1, 0.2, -7.3},
			{-1.25, 2.2, 2.3, 6.3},
		}, Mat4{
			{-3, 2.4, 8.4, 3.3},
			{0.2, -5, 2.6, 1.2},
			{5.3, 8.1, 4.4, 2.5},
			{4.9, -1, 0, 4},
		}, Mat4{
			{0.7, -1.24, 31.24, 13.77},
			{54.92, 72.88, 48.62, 55.92},
			{-7.21, -17.92, -72.96, -57.53},
			{47.25, -1.67, 5.34, 29.465},
		}},
	}
	for _, tt := range tests {
		var m Mat4
		mp := m.Mul(&tt.a, &tt.b)
		if !tt.want.nearEq(&m) {
			t.Errorf("%v * %v = %v, want %v", tt.a, tt.b, m, tt.want)
		}
		if mp != &m {
			t.Errorf("m.Mul(...) does not return the pointer to m")
		}
	}
}
<commit_msg>add unit test for (*Mat4).Det<commit_after>// Copyright 2013 Frederik Zipp. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package geom

import (
	"testing"
)

func TestMat4Id(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	id := Mat4{
		{1, 0, 0, 0},
		{0, 1, 0, 0},
		{0, 0, 1, 0},
		{0, 0, 0, 1},
	}
	mp := m.Id()
	if m != id {
		t.Errorf("m.Id() does not set m to be the identity matrix, got instead: %v", m)
	}
	if mp != &m {
		t.Errorf("m.Id() does not return the pointer to m")
	}
}

func TestMat4Det(t *testing.T) {
	tests := []struct {
		m    Mat4
		want float32
	}{
		{id, 1},
		{Mat4{
			{-3, 2, 6, 5},
			{4, 1.5, 1, 8},
			{1, 4, 2, 4},
			{5.25, 6, -2, 8},
		}, 348.25},
	}
	for _, tt := range tests {
		if det := tt.m.Det(); det != tt.want {
			t.Errorf("%v.Det() = %g, want %g", tt.m, det, tt.want)
		}
	}
}

func TestMat4Ortho(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, 0, 1},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{0.5, 0, 0, 0},
			{0, 0.5, 0, 0},
			{0, 0, 0.5, 0},
			{0, 0, 0, 1.0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{2, 0, 0, 0},
			{0, 2, 0, 0},
			{0, 0, -2, 0},
			{-3, -7, -11, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Ortho(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Ortho(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Frustum(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -1, 0},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -2, 0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{10, 0, 0, 0},
			{0, 10, 0, 0},
			{3, 7, -11, -1},
			{0, 0, -60, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Frustum(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Frustum(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Perspective(t *testing.T) {
	tests := []struct {
		fovy, a, n, f float32
		want          Mat4
	}{
		{65, 1.5, 5, 10, Mat4{
			{0.35279062, 0, 0, 0},
			{0, 0.52918595, 0, 0},
			{0, 0, -3, -1},
			{0, 0, -20, 0},
		}},
		{70, 1.33, 1, 30, Mat4{
			{1.5868644, 0, 0, 0},
			{0, 2.1105297, 0, 0},
			{0, 0, -1.0689656, -1},
			{0, 0, -2.0689654, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Perspective(tt.fovy, tt.a, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Perspective(%g, %g, %g, %g) = %v, want %v",
				tt.fovy, tt.a, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4LookAt(t *testing.T) {
	tests := []struct {
		eye, center, up Vec3
		want            Mat4
	}{
		{V3(1, 1, 1), V3(1, 1, 0), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{-1, -1, -1, 1},
		}},
		{V3(0, 0, 1), V3(0, 0, -1), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, -1, 1},
		}},
		{V3(20, 80, 15), V3(15, 0, 12), V3(0, -1, 0), Mat4{
			{-0.5144958, 0.8552243, 0.06233464, 0},
			{0, -0.07269406, 0.99735427, 0},
			{0.857493, 0.5131346, 0.037400786, 0},
			{-2.5724783, -18.985981, -81.596054, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.LookAt(tt.eye, tt.center, tt.up)
		if m != tt.want {
			t.Errorf("m.LookAt(%s, %s, %s) = %v, want %v",
				tt.eye, tt.center, tt.up, m, tt.want)
		}
	}
}

func TestMat4Floats(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	want := [16]float32{11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44}
	f := m.Floats()
	if *f != want {
		t.Errorf("%v.Floats() = %v, want %v", m, *f, want)
	}
	f[6] = 99
	if m[1][2] != 99 {
		t.Errorf("Pointer to float32 array returned by Floats() does not point to matrix data.")
	}
}

func TestMat4Mul(t *testing.T) {
	tests := []struct {
		a, b, want Mat4
	}{
		{Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{34, 44, 54, 64},
			{82, 108, 134, 160},
			{34, 44, 54, 64},
			{82, 108, 134, 160},
		}},

		{Mat4{
			{2.1, 3.2, 1.2, 0},
			{4.6, -5.3, 5.4, 8.4},
			{-9.1, 1, 0.2, -7.3},
			{-1.25, 2.2, 2.3, 6.3},
		}, Mat4{
			{-3, 2.4, 8.4, 3.3},
			{0.2, -5, 2.6, 1.2},
			{5.3, 8.1, 4.4, 2.5},
			{4.9, -1, 0, 4},
		}, Mat4{
			{0.7, -1.24, 31.24, 13.77},
			{54.92, 72.88, 48.62, 55.92},
			{-7.21, -17.92, -72.96, -57.53},
			{47.25, -1.67, 5.34, 29.465},
		}},
	}
	for _, tt := range tests {
		var m Mat4
		mp := m.Mul(&tt.a, &tt.b)
		if !tt.want.nearEq(&m) {
			t.Errorf("%v * %v = %v, want %v", tt.a, tt.b, m, tt.want)
		}
		if mp != &m {
			t.Errorf("m.Mul(...) does not return the pointer to m")
		}
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"io"
	"net/http"

	"github.com/pkg/errors"

	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/rs/zerolog/hlog"
)

// StandardResponse is meant to be included in all response bodies
// and includes "standard" response fields
type StandardResponse struct {
	Path      string      `json:"path,omitempty"`
	RequestID string      `json:"request_id,omitempty"`
	Data      interface{} `json:"data"`
}

// NewStandardResponse is an initializer for the StandardResponse struct
func NewStandardResponse(r *http.Request, d interface{}) (*StandardResponse, error) {
	var sr StandardResponse
	sr.Path = r.URL.EscapedPath()
	// gets Trace ID from request
	id, ok := hlog.IDFromRequest(r)
	if !ok {
		return nil, errs.E(errors.New("trace ID not properly set to request context"))
	}
	sr.RequestID = id.String()

	sr.Data = d

	return &sr, nil
}

// DecoderErr handles an error returned by json.NewDecoder(r.Body).Decode(&data)
// this function will determine the appropriate error response
func DecoderErr(err error) error {
	switch {
	// If the request body is empty (io.EOF)
	// return an error
	case err == io.EOF:
		return errs.E(errs.InvalidRequest, errors.New("Request Body cannot be empty"))
	// If the request body has malformed JSON (io.ErrUnexpectedEOF)
	// return an error
	case err == io.ErrUnexpectedEOF:
		return errs.E(errs.InvalidRequest, errors.New("Malformed JSON"))
	// return all other errors
	case err != nil:
		return errs.E(err)
	}
	return nil
}
<commit_msg>comment update<commit_after>package controller

import (
	"io"
	"net/http"

	"github.com/pkg/errors"

	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/rs/zerolog/hlog"
)

// StandardResponse is meant to be included in all non-error
// response bodies and includes "standard" response fields
type StandardResponse struct {
	Path      string      `json:"path,omitempty"`
	RequestID string      `json:"request_id,omitempty"`
	Data      interface{} `json:"data"`
}

// NewStandardResponse is an initializer for the StandardResponse struct
func NewStandardResponse(r *http.Request, d interface{}) (*StandardResponse, error) {
	var sr StandardResponse
	sr.Path = r.URL.EscapedPath()
	// gets Trace ID from request
	id, ok := hlog.IDFromRequest(r)
	if !ok {
		return nil, errs.E(errors.New("trace ID not properly set to request context"))
	}
	sr.RequestID = id.String()

	sr.Data = d

	return &sr, nil
}

// DecoderErr handles an error returned by json.NewDecoder(r.Body).Decode(&data)
// this function will determine the appropriate error response
func DecoderErr(err error) error {
	switch {
	// If the request body is empty (io.EOF)
	// return an error
	case err == io.EOF:
		return errs.E(errs.InvalidRequest, errors.New("Request Body cannot be empty"))
	// If the request body has malformed JSON (io.ErrUnexpectedEOF)
	// return an error
	case err == io.ErrUnexpectedEOF:
		return errs.E(errs.InvalidRequest, errors.New("Malformed JSON"))
	// return all other errors
	case err != nil:
		return errs.E(err)
	}
	return nil
}
<|endoftext|>


<commit_before>package controller

import (
	"log"
	"os"
	"strconv"

	"github.com/coreos/go-etcd/etcd"
	"github.com/taskgraph/taskgraph/pkg/etcdutil"
)

// This is the controller of a job.
// A job needs controller to setup etcd data layout, request
// cluster containers, etc. to setup framework to run.
type Controller struct {
	name           string
	etcdclient     *etcd.Client
	numOfTasks     uint64
	failDetectStop chan bool
	logger         *log.Logger
	jobStatusChan  chan string
	linkTypes      []string
}

func New(name string, etcd *etcd.Client, numOfTasks uint64, pLinkTypes []string) *Controller {
	return &Controller{
		name:       name,
		etcdclient: etcd,
		numOfTasks: numOfTasks,
		logger:     log.New(os.Stdout, "", log.Lshortfile|log.Ltime|log.Ldate),
		linkTypes:  pLinkTypes,
	}
}

// A controller typical workflow:
// 1. controller sets up etcd layout before any task starts running.
// 2. Being ready, controller lets other tasks to run and reports any failure found.
func (c *Controller) Start() error {
	if err := c.InitEtcdLayout(); err != nil {
		return err
	}
	// Currently no previous changes will be watches before watch is setup.
	// We assumes that ttl is usually a few seconds. watch is setup before that.
	go c.startFailureDetection()
	c.logger.Printf("Controller starting, name: %s, numberOfTask: %d\n", c.name, c.numOfTasks)
	return nil
}

func (c *Controller) WaitForJobDone() error {
	<-c.jobStatusChan
	return nil
}

func (c *Controller) Stop() error {
	c.DestroyEtcdLayout()
	c.stopFailureDetection()
	c.logger.Printf("Controller stoping...\n")
	return nil
}

func (c *Controller) InitEtcdLayout() error {
	// Initilize the job epoch to 0
	etcdutil.MustCreate(c.etcdclient, c.logger, etcdutil.EpochPath(c.name), "0", 0)
	c.setupWatchOnJobStatus()
	// initiate etcd data layout for tasks
	// currently it creates as many unassigned tasks as task masters.
	for i := uint64(0); i < c.numOfTasks; i++ {
		key := etcdutil.FreeTaskPath(c.name, strconv.FormatUint(i, 10))
		etcdutil.MustCreate(c.etcdclient, c.logger, key, "", 0)
		for idx := range c.linkTypes {
			key = etcdutil.MetaPath(c.linkTypes[idx], c.name, i)
			etcdutil.MustCreate(c.etcdclient, c.logger, key, "", 0)
		}
	}
	return nil
}

func (c *Controller) DestroyEtcdLayout() error {
	_, err := c.etcdclient.Delete("/", true)
	return err
}

func (c *Controller) startFailureDetection() error {
	c.failDetectStop = make(chan bool, 1)
	err := etcdutil.DetectFailure(c.etcdclient, c.name, c.failDetectStop)
	if err != nil {
		// We currently didn't handle outside. So we do some logging at least.
		c.logger.Printf("DetectFailure returns error: %v", err)
	}
	return err
}

func (c *Controller) setupWatchOnJobStatus() {
	c.jobStatusChan = make(chan string, 1)
	key := etcdutil.JobStatusPath(c.name)
	resp := etcdutil.MustCreate(c.etcdclient, c.logger, key, "", 0)
	go func() {
		resp, err := c.etcdclient.Watch(key, resp.EtcdIndex+1, false, nil, nil)
		if err != nil {
			c.logger.Panicf("Watch on job status (%v) failed: %v", key, err)
		}
		c.jobStatusChan <- resp.Node.Value
	}()
}

func (c *Controller) stopFailureDetection() error {
	c.failDetectStop <- true
	return nil
}
<commit_msg>minor fix<commit_after>package controller

import (
	"log"
	"os"
	"strconv"

	"github.com/coreos/go-etcd/etcd"
	"github.com/taskgraph/taskgraph/pkg/etcdutil"
)

// This is the controller of a job.
// A job needs controller to setup etcd data layout, request
// cluster containers, etc. to setup framework to run.
type Controller struct {
	name           string
	etcdclient     *etcd.Client
	numOfTasks     uint64
	failDetectStop chan bool
	logger         *log.Logger
	jobStatusChan  chan string
	linkTypes      []string
}

func New(name string, etcd *etcd.Client, numOfTasks uint64, pLinkTypes []string) *Controller {
	return &Controller{
		name:       name,
		etcdclient: etcd,
		numOfTasks: numOfTasks,
		logger:     log.New(os.Stdout, "", log.Lshortfile|log.Ltime|log.Ldate),
		linkTypes:  pLinkTypes,
	}
}

// A controller typical workflow:
// 1. controller sets up etcd layout before any task starts running.
// 2. Being ready, controller lets other tasks to run and reports any failure found.
func (c *Controller) Start() error {
	if err := c.InitEtcdLayout(); err != nil {
		return err
	}
	// Currently no previous changes will be watches before watch is setup.
	// We assumes that ttl is usually a few seconds. watch is setup before that.
	go c.startFailureDetection()
	c.logger.Printf("Controller starting, name: %s, numberOfTask: %d\n", c.name, c.numOfTasks)
	return nil
}

func (c *Controller) WaitForJobDone() error {
	<-c.jobStatusChan
	return nil
}

func (c *Controller) Stop() error {
	c.DestroyEtcdLayout()
	c.stopFailureDetection()
	c.logger.Printf("Controller stoping...\n")
	return nil
}

func (c *Controller) InitEtcdLayout() error {
	// Initilize the job epoch to 0
	etcdutil.MustCreate(c.etcdclient, c.logger, etcdutil.EpochPath(c.name), "0", 0)
	c.setupWatchOnJobStatus()
	// initiate etcd data layout for tasks
	// currently it creates as many unassigned tasks as task masters.
	for i := uint64(0); i < c.numOfTasks; i++ {
		key := etcdutil.FreeTaskPath(c.name, strconv.FormatUint(i, 10))
		etcdutil.MustCreate(c.etcdclient, c.logger, key, "", 0)
		for _, linkType := range c.linkTypes {
			key = etcdutil.MetaPath(linkType, c.name, i)
			etcdutil.MustCreate(c.etcdclient, c.logger, key, "", 0)
		}
	}
	return nil
}

func (c *Controller) DestroyEtcdLayout() error {
	_, err := c.etcdclient.Delete("/", true)
	return err
}

func (c *Controller) startFailureDetection() error {
	c.failDetectStop = make(chan bool, 1)
	err := etcdutil.DetectFailure(c.etcdclient, c.name, c.failDetectStop)
	if err != nil {
		// We currently didn't handle outside. So we do some logging at least.
		c.logger.Printf("DetectFailure returns error: %v", err)
	}
	return err
}

func (c *Controller) setupWatchOnJobStatus() {
	c.jobStatusChan = make(chan string, 1)
	key := etcdutil.JobStatusPath(c.name)
	resp := etcdutil.MustCreate(c.etcdclient, c.logger, key, "", 0)
	go func() {
		resp, err := c.etcdclient.Watch(key, resp.EtcdIndex+1, false, nil, nil)
		if err != nil {
			c.logger.Panicf("Watch on job status (%v) failed: %v", key, err)
		}
		c.jobStatusChan <- resp.Node.Value
	}()
}

func (c *Controller) stopFailureDetection() error {
	c.failDetectStop <- true
	return nil
}
<|endoftext|>


<commit_before>// Package explorer provides a glimpse into what the network currently
// looks like.
package explorer

import (
	"errors"

	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/persist"
	"github.com/rivine/rivine/types"
)

const (
	// ActiveBSEstimationBlocks is the number of blocks that are used to
	// estimate the active block stake used to generate blocks.
	ActiveBSEstimationBlocks = 200
)

var (
	errNilCS = errors.New("explorer cannot use a nil consensus set")
)

type (
	// blockFacts contains a set of facts about the consensus set related to a
	// certain block. The explorer needs some additional information in the
	// history so that it can calculate certain values, which is one of the
	// reasons that the explorer uses a separate struct instead of
	// modules.BlockFacts.
	blockFacts struct {
		modules.BlockFacts

		Timestamp types.Timestamp
	}

	// An Explorer contains a more comprehensive view of the blockchain,
	// including various statistics and metrics.
	Explorer struct {
		cs             modules.ConsensusSet
		db             *persist.BoltDatabase
		persistDir     string
		bcInfo         types.BlockchainInfo
		chainCts       types.ChainConstants
		rootTarget     types.Target
		genesisBlock   types.Block
		genesisBlockID types.BlockID
	}
)

// New creates the internal data structures, and subscribes to
// consensus for changes to the blockchain
func New(cs modules.ConsensusSet, persistDir string, bcInfo types.BlockchainInfo, chainCts types.ChainConstants) (*Explorer, error) {
	// Check that input modules are non-nil
	if cs == nil {
		return nil, errNilCS
	}

	// Initialize the explorer.
	genesisBlock := chainCts.GenesisBlock()
	e := &Explorer{
		cs:             cs,
		persistDir:     persistDir,
		bcInfo:         bcInfo,
		chainCts:       chainCts,
		rootTarget:     chainCts.RootTarget(),
		genesisBlock:   genesisBlock,
		genesisBlockID: genesisBlock.ID(),
	}

	// Initialize the persistent structures, including the database.
	err := e.initPersist()
	if err != nil {
		return nil, err
	}

	// retrieve the current ConsensusChangeID
	var recentChange modules.ConsensusChangeID
	err = e.db.View(dbGetInternal(internalRecentChange, &recentChange))
	if err != nil {
		return nil, err
	}

	err = cs.ConsensusSetSubscribe(e, recentChange)
	if err != nil {
		// TODO: restart from 0
		return nil, errors.New("explorer subscription failed: " + err.Error())
	}

	return e, nil
}

// Close closes the explorer.
func (e *Explorer) Close() error {
	return e.db.Close()
}
<commit_msg>Fix #391: Unsubscribe explorer from cs<commit_after>// Package explorer provides a glimpse into what the network currently
// looks like.
package explorer

import (
	"errors"

	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/persist"
	"github.com/rivine/rivine/types"
)

const (
	// ActiveBSEstimationBlocks is the number of blocks that are used to
	// estimate the active block stake used to generate blocks.
	ActiveBSEstimationBlocks = 200
)

var (
	errNilCS = errors.New("explorer cannot use a nil consensus set")
)

type (
	// blockFacts contains a set of facts about the consensus set related to a
	// certain block. The explorer needs some additional information in the
	// history so that it can calculate certain values, which is one of the
	// reasons that the explorer uses a separate struct instead of
	// modules.BlockFacts.
	blockFacts struct {
		modules.BlockFacts

		Timestamp types.Timestamp
	}

	// An Explorer contains a more comprehensive view of the blockchain,
	// including various statistics and metrics.
	Explorer struct {
		cs             modules.ConsensusSet
		db             *persist.BoltDatabase
		persistDir     string
		bcInfo         types.BlockchainInfo
		chainCts       types.ChainConstants
		rootTarget     types.Target
		genesisBlock   types.Block
		genesisBlockID types.BlockID
	}
)

// New creates the internal data structures, and subscribes to
// consensus for changes to the blockchain
func New(cs modules.ConsensusSet, persistDir string, bcInfo types.BlockchainInfo, chainCts types.ChainConstants) (*Explorer, error) {
	// Check that input modules are non-nil
	if cs == nil {
		return nil, errNilCS
	}

	// Initialize the explorer.
	genesisBlock := chainCts.GenesisBlock()
	e := &Explorer{
		cs:             cs,
		persistDir:     persistDir,
		bcInfo:         bcInfo,
		chainCts:       chainCts,
		rootTarget:     chainCts.RootTarget(),
		genesisBlock:   genesisBlock,
		genesisBlockID: genesisBlock.ID(),
	}

	// Initialize the persistent structures, including the database.
	err := e.initPersist()
	if err != nil {
		return nil, err
	}

	// retrieve the current ConsensusChangeID
	var recentChange modules.ConsensusChangeID
	err = e.db.View(dbGetInternal(internalRecentChange, &recentChange))
	if err != nil {
		return nil, err
	}

	err = cs.ConsensusSetSubscribe(e, recentChange)
	if err != nil {
		// TODO: restart from 0
		return nil, errors.New("explorer subscription failed: " + err.Error())
	}

	return e, nil
}

// Close closes the explorer.
func (e *Explorer) Close() error {
	e.cs.Unsubscribe(e)
	return e.db.Close()
}
<|endoftext|>


<commit_before>// MurmurHash3 was written by Austin Appleby, and is placed in the public
package murmurhash3

import (
	"encoding/binary"
	"hash"
)

type (
	murmurhash3A uint32
	murmurhash3C uint32
	murmurhash3F uint64
)

func New3A() hash.Hash32 {
	var m murmurhash3A = 0
	return &m
}
func (m *murmurhash3A) Reset() { *m = 0 }
func (m *murmurhash3A) Size() int {
	return 4
}
func (m *murmurhash3A) BlockSize() int {
	return 4
}
func (m *murmurhash3A) Write(p []byte) (n int, err error) {
	*m = murmurhash3A(Murmur3A(p, uint32(*m)))
	return len(p), nil
}
func (m *murmurhash3A) Sum32() uint32 {
	return uint32(*m)
}
func (m *murmurhash3A) Sum(in []byte) []byte {
	v := uint32(*m)
	return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

func New3C() hash.Hash32 {
	var m murmurhash3C = 0
	return &m
}
func (m *murmurhash3C) Reset() { *m = 0 }
func (m *murmurhash3C) Size() int {
	return 4
}
func (m *murmurhash3C) BlockSize() int {
	return 16
}
func (m *murmurhash3C) Write(p []byte) (n int, err error) {
	*m = murmurhash3C(Murmur3C(p, uint32(*m))[0])
	return len(p), nil
}
func (m *murmurhash3C) Sum32() uint32 {
	return uint32(*m)
}
func (m *murmurhash3C) Sum(in []byte) []byte {
	v := uint32(*m)
	return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

func New3F() hash.Hash64 {
	var m murmurhash3F = 0
	return &m
}
func (m *murmurhash3F) Reset() { *m = 0 }
func (m *murmurhash3F) Size() int {
	return 8
}
func (m *murmurhash3F) BlockSize() int {
	return 16
}
func (m *murmurhash3F) Write(p []byte) (n int, err error) {
	*m = murmurhash3F(Murmur3F(p, uint64(*m))[0])
	return len(p), nil
}
func (m *murmurhash3F) Sum64() uint64 {
	return uint64(*m)
}
func (m *murmurhash3F) Sum(in []byte) []byte {
	v := uint64(*m)
	return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

func rotl32(x uint32, r uint8) uint32 {
	return (x << r) | (x >> (32 - r))
}

func rotl64(x uint64, r uint8) uint64 {
	return (x << r) | (x >> (64 - r))
}

func fmix32(h uint32) uint32 {
	h ^= h >> 16
	h *= 0x85ebca6b
	h ^= h >> 13
	h *= 0xc2b2ae35
	h ^= h >> 16

	return h
}

func fmix64(h uint64) uint64 {
	h ^= h >> 33
	h *= 0xff51afd7ed558ccd
	h ^= h >> 33
	h *= 0xc4ceb9fe1a85ec53
	h ^= h >> 33

	return h
}

func Murmur3A(key []byte, seed uint32) uint32 {
	nblocks := len(key) / 4
	var h1 uint32 = seed

	var c1 uint32 = 0xcc9e2d51
	var c2 uint32 = 0x1b873593

	// body
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(key[i*4:]) // TODO Validate

		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2

		h1 ^= k1
		h1 = rotl32(h1, 13)
		h1 = h1*5 + 0xe6546b64
	}

	// tail
	var tail []byte = key[nblocks*4:] // TODO Validate
	var k1 uint32 = 0
	switch len(key) & 3 {
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0])
		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	//finalization
	h1 ^= uint32(len(key))

	h1 = fmix32(h1)

	return h1
}

func Murmur3C(key []byte, seed uint32) [4]uint32 {
	nblocks := len(key) / 16
	var h1 uint32 = seed
	var h2 uint32 = seed
	var h3 uint32 = seed
	var h4 uint32 = seed

	var c1 uint32 = 0x239b961b
	var c2 uint32 = 0xab0e9789
	var c3 uint32 = 0x38b34ae5
	var c4 uint32 = 0xa1e38b93

	// body
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(key[(i*4+0)*4:]) // TODO Validate
		k2 := binary.LittleEndian.Uint32(key[(i*4+1)*4:])
		k3 := binary.LittleEndian.Uint32(key[(i*4+2)*4:])
		k4 := binary.LittleEndian.Uint32(key[(i*4+3)*4:])

		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2
		h1 ^= k1

		h1 = rotl32(h1, 19)
		h1 += h2
		h1 = h1*5 + 0x561ccd1b

		k2 *= c2
		k2 = rotl32(k2, 16)
		k2 *= c3
		h2 ^= k2

		h2 = rotl32(h2, 17)
		h2 += h3
		h2 = h2*5 + 0x0bcaa747

		k3 *= c3
		k3 = rotl32(k3, 17)
		k3 *= c4
		h3 ^= k3

		h3 = rotl32(h3, 15)
		h3 += h4
		h3 = h3*5 + 0x96cd1c35

		k4 *= c4
		k4 = rotl32(k4, 18)
		k4 *= c1
		h4 ^= k4

		h4 = rotl32(h4, 13)
		h4 += h1
		h4 = h4*5 + 0x32ac3b17
	}

	// tail
	var tail []byte = key[nblocks*16:] // TODO Validate
	var k1 uint32 = 0
	var k2 uint32 = 0
	var k3 uint32 = 0
	var k4 uint32 = 0
	switch len(key) & 15 {
	case 15:
		k4 ^= uint32(tail[14]) << 16
		fallthrough
	case 14:
		k4 ^= uint32(tail[13]) << 8
		fallthrough
	case 13:
		k4 ^= uint32(tail[12]) << 0
		k4 *= c4
		k4 = rotl32(k4, 18)
		k4 *= c1
		h4 ^= k4
		fallthrough
	case 12:
		k3 ^= uint32(tail[11]) << 24
		fallthrough
	case 11:
		k3 ^= uint32(tail[10]) << 16
		fallthrough
	case 10:
		k3 ^= uint32(tail[9]) << 8
		fallthrough
	case 9:
		k3 ^= uint32(tail[8]) << 0
		k3 *= c3
		k3 = rotl32(k3, 17)
		k3 *= c4
		h3 ^= k3
		fallthrough
	case 8:
		k2 ^= uint32(tail[7]) << 24
		fallthrough
	case 7:
		k2 ^= uint32(tail[6]) << 16
		fallthrough
	case 6:
		k2 ^= uint32(tail[5]) << 8
		fallthrough
	case 5:
		k2 ^= uint32(tail[4]) << 0
		k2 *= c2
		k2 = rotl32(k2, 16)
		k2 *= c3
		h2 ^= k2
		fallthrough
	case 4:
		k1 ^= uint32(tail[3]) << 24
		fallthrough
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0]) << 0
		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	//finalization
	h1 ^= uint32(len(key))
	h2 ^= uint32(len(key))
	h3 ^= uint32(len(key))
	h4 ^= uint32(len(key))

	h1 += h2
	h1 += h3
	h1 += h4
	h2 += h1
	h3 += h1
	h4 += h1

	h1 = fmix32(h1)
	h2 = fmix32(h2)
	h3 = fmix32(h3)
	h4 = fmix32(h4)

	h1 += h2
	h1 += h3
	h1 += h4
	h2 += h1
	h3 += h1
	h4 += h1

	return [4]uint32{h1, h2, h3, h4}
}

func Murmur3F(key []byte, seed uint64) [2]uint64 {
	nblocks := len(key) / 16
	var h1 uint64 = seed
	var h2 uint64 = seed

	var c1 uint64 = 0x87c37b91114253d5
	var c2 uint64 = 0x4cf5ad432745937f

	// body
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint64(key[(i*2+0)*8:]) // TODO Validate
		k2 := binary.LittleEndian.Uint64(key[(i*2+1)*8:])

		k1 *= c1
		k1 = rotl64(k1, 31)
		k1 *= c2
		h1 ^= k1

		h1 = rotl64(h1, 27)
		h1 += h2
		h1 = h1*5 + 0x52dce729

		k2 *= c2
		k2 = rotl64(k2, 33)
		k2 *= c1
		h2 ^= k2

		h2 = rotl64(h2, 31)
		h2 += h1
		h2 = h2*5 + 0x38495ab5
	}

	// tail
	var tail []byte = key[nblocks*16:] // TODO Validate
	var k1 uint64 = 0
	var k2 uint64 = 0
	switch len(key) & 15 {
	case 15:
		k2 ^= uint64(tail[14]) << 48
		fallthrough
	case 14:
		k2 ^= uint64(tail[13]) << 40
		fallthrough
	case 13:
		k2 ^= uint64(tail[12]) << 32
		fallthrough
	case 12:
		k2 ^= uint64(tail[11]) << 24
		fallthrough
	case 11:
		k2 ^= uint64(tail[10]) << 16
		fallthrough
	case 10:
		k2 ^= uint64(tail[9]) << 8
		fallthrough
	case 9:
		k2 ^= uint64(tail[8]) << 0
		k2 *= c2
		k2 = rotl64(k2, 33)
		k2 *= c1
		h2 ^= k2
		fallthrough
	case 8:
		k1 ^= uint64(tail[7]) << 56
		fallthrough
	case 7:
		k1 ^= uint64(tail[6]) << 48
		fallthrough
	case 6:
		k1 ^= uint64(tail[5]) << 40
		fallthrough
	case 5:
		k1 ^= uint64(tail[4]) << 32
		fallthrough
	case 4:
		k1 ^= uint64(tail[3]) << 24
		fallthrough
	case 3:
		k1 ^= uint64(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint64(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint64(tail[0]) << 0
		k1 *= c1
		k1 = rotl64(k1, 31)
		k1 *= c2
		h1 ^= k1
	}

	//finalization
	h1 ^= uint64(len(key))
	h2 ^= uint64(len(key))

	h1 += h2
	h2 += h1

	h1 = fmix64(h1)
	h2 = fmix64(h2)

	h1 += h2
	h2 += h1

	return [2]uint64{h1, h2}
}
<commit_msg>Docs.<commit_after>// MurmurHash3 was written by Austin Appleby, and is placed in the public
package murmurhash3

import (
	"encoding/binary"
	"hash"
)

type (
	murmurhash3A uint32
	murmurhash3C uint32
	murmurhash3F uint64
)

func New3A() hash.Hash32 {
	var m murmurhash3A = 0
	return &m
}
func (m *murmurhash3A) Reset() { *m = 0 }
func (m *murmurhash3A) Size() int {
	return 4
}
func (m *murmurhash3A) BlockSize() int {
	return 4
}
func (m *murmurhash3A) Write(p []byte) (n int, err error) {
	*m = murmurhash3A(Murmur3A(p, uint32(*m)))
	return len(p), nil
}
func (m *murmurhash3A) Sum32() uint32 {
	return uint32(*m)
}
func (m *murmurhash3A) Sum(in []byte) []byte {
	v := uint32(*m)
	return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

func New3C() hash.Hash32 {
	var m murmurhash3C = 0
	return &m
}
func (m *murmurhash3C) Reset() { *m = 0 }
func (m *murmurhash3C) Size() int {
	return 4
}
func (m *murmurhash3C) BlockSize() int {
	return 16
}
func (m *murmurhash3C) Write(p []byte) (n int, err error) {
	*m = murmurhash3C(Murmur3C(p, uint32(*m))[0])
	return len(p), nil
}
func (m *murmurhash3C) Sum32() uint32 {
	return uint32(*m)
}
func (m *murmurhash3C) Sum(in []byte) []byte {
	v := uint32(*m)
	return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

func New3F() hash.Hash64 {
	var m murmurhash3F = 0
	return &m
}
func (m *murmurhash3F) Reset() { *m = 0 }
func (m *murmurhash3F) Size() int {
	return 8
}
func (m *murmurhash3F) BlockSize() int {
	return 16
}
func (m *murmurhash3F) Write(p []byte) (n int, err error) {
	*m = murmurhash3F(Murmur3F(p, uint64(*m))[0])
	return len(p), nil
}
func (m *murmurhash3F) Sum64() uint64 {
	return uint64(*m)
}
func (m *murmurhash3F) Sum(in []byte) []byte {
	v := uint64(*m)
	return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

func rotl32(x uint32, r uint8) uint32 {
	return (x << r) | (x >> (32 - r))
}

func rotl64(x uint64, r uint8) uint64 {
	return (x << r) | (x >> (64 - r))
}

func fmix32(h uint32) uint32 {
	h ^= h >> 16
	h *= 0x85ebca6b
	h ^= h >> 13
	h *= 0xc2b2ae35
	h ^= h >> 16

	return h
}

func fmix64(h uint64) uint64 {
	h ^= h >> 33
	h *= 0xff51afd7ed558ccd
	h ^= h >> 33
	h *= 0xc4ceb9fe1a85ec53
	h ^= h >> 33

	return h
}

// MurmurHash3 for x86, 32-bit (MurmurHash3_x86_32)
func Murmur3A(key []byte, seed uint32) uint32 {
	nblocks := len(key) / 4
	var h1 uint32 = seed

	var c1 uint32 = 0xcc9e2d51
	var c2 uint32 = 0x1b873593

	// body
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(key[i*4:]) // TODO Validate

		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2

		h1 ^= k1
		h1 = rotl32(h1, 13)
		h1 = h1*5 + 0xe6546b64
	}

	// tail
	var tail []byte = key[nblocks*4:] // TODO Validate
	var k1 uint32 = 0
	switch len(key) & 3 {
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0])
		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	//finalization
	h1 ^= uint32(len(key))

	h1 = fmix32(h1)

	return h1
}

// MurmurHash3 for x86, 128-bit (MurmurHash3_x86_128)
func Murmur3C(key []byte, seed uint32) [4]uint32 {
	nblocks := len(key) / 16
	var h1 uint32 = seed
	var h2 uint32 = seed
	var h3 uint32 = seed
	var h4 uint32 = seed

	var c1 uint32 = 0x239b961b
	var c2 uint32 = 0xab0e9789
	var c3 uint32 = 0x38b34ae5
	var c4 uint32 = 0xa1e38b93

	// body
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(key[(i*4+0)*4:]) // TODO Validate
		k2 := binary.LittleEndian.Uint32(key[(i*4+1)*4:])
		k3 := binary.LittleEndian.Uint32(key[(i*4+2)*4:])
		k4 := binary.LittleEndian.Uint32(key[(i*4+3)*4:])

		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2
		h1 ^= k1

		h1 = rotl32(h1, 19)
		h1 += h2
		h1 = h1*5 + 0x561ccd1b

		k2 *= c2
		k2 = rotl32(k2, 16)
		k2 *= c3
		h2 ^= k2

		h2 = rotl32(h2, 17)
		h2 += h3
		h2 = h2*5 + 0x0bcaa747

		k3 *= c3
		k3 = rotl32(k3, 17)
		k3 *= c4
		h3 ^= k3

		h3 = rotl32(h3, 15)
		h3 += h4
		h3 = h3*5 + 0x96cd1c35

		k4 *= c4
		k4 = rotl32(k4, 18)
		k4 *= c1
		h4 ^= k4

		h4 = rotl32(h4, 13)
		h4 += h1
		h4 = h4*5 + 0x32ac3b17
	}

	// tail
	var tail []byte = key[nblocks*16:] // TODO Validate
	var k1 uint32 = 0
	var k2 uint32 = 0
	var k3 uint32 = 0
	var k4 uint32 = 0
	switch len(key) & 15 {
	case 15:
		k4 ^= uint32(tail[14]) << 16
		fallthrough
	case 14:
		k4 ^= uint32(tail[13]) << 8
		fallthrough
	case 13:
		k4 ^= uint32(tail[12]) << 0
		k4 *= c4
		k4 = rotl32(k4, 18)
		k4 *= c1
		h4 ^= k4
		fallthrough
	case 12:
		k3 ^= uint32(tail[11]) << 24
		fallthrough
	case 11:
		k3 ^= uint32(tail[10]) << 16
		fallthrough
	case 10:
		k3 ^= uint32(tail[9]) << 8
		fallthrough
	case 9:
		k3 ^= uint32(tail[8]) << 0
		k3 *= c3
		k3 = rotl32(k3, 17)
		k3 *= c4
		h3 ^= k3
		fallthrough
	case 8:
		k2 ^= uint32(tail[7]) << 24
		fallthrough
	case 7:
		k2 ^= uint32(tail[6]) << 16
		fallthrough
	case 6:
		k2 ^= uint32(tail[5]) << 8
		fallthrough
	case 5:
		k2 ^= uint32(tail[4]) << 0
		k2 *= c2
		k2 = rotl32(k2, 16)
		k2 *= c3
		h2 ^= k2
		fallthrough
	case 4:
		k1 ^= uint32(tail[3]) << 24
		fallthrough
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0]) << 0
		k1 *= c1
		k1 = rotl32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	//finalization
	h1 ^= uint32(len(key))
	h2 ^= uint32(len(key))
	h3 ^= uint32(len(key))
	h4 ^= uint32(len(key))

	h1 += h2
	h1 += h3
	h1 += h4
	h2 += h1
	h3 += h1
	h4 += h1

	h1 = fmix32(h1)
	h2 = fmix32(h2)
	h3 = fmix32(h3)
	h4 = fmix32(h4)

	h1 += h2
	h1 += h3
	h1 += h4
	h2 += h1
	h3 += h1
	h4 += h1

	return [4]uint32{h1, h2, h3, h4}
}

// MurmurHash3 for x64, 128-bit (MurmurHash3_x64_128)
func Murmur3F(key []byte, seed uint64) [2]uint64 {
	nblocks := len(key) / 16
	var h1 uint64 = seed
	var h2 uint64 = seed

	var c1 uint64 = 0x87c37b91114253d5
	var c2 uint64 = 0x4cf5ad432745937f

	// body
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint64(key[(i*2+0)*8:]) // TODO Validate
		k2 := binary.LittleEndian.Uint64(key[(i*2+1)*8:])

		k1 *= c1
		k1 = rotl64(k1, 31)
		k1 *= c2
		h1 ^= k1

		h1 = rotl64(h1, 27)
		h1 += h2
		h1 = h1*5 + 0x52dce729

		k2 *= c2
		k2 = rotl64(k2, 33)
		k2 *= c1
		h2 ^= k2

		h2 = rotl64(h2, 31)
		h2 += h1
		h2 = h2*5 + 0x38495ab5
	}

	// tail
	var tail []byte = key[nblocks*16:] // TODO Validate
	var k1 uint64 = 0
	var k2 uint64 = 0
	switch len(key) & 15 {
	case 15:
		k2 ^= uint64(tail[14]) << 48
		fallthrough
	case 14:
		k2 ^= uint64(tail[13]) << 40
		fallthrough
	case 13:
		k2 ^= uint64(tail[12]) << 32
		fallthrough
	case 12:
		k2 ^= uint64(tail[11]) << 24
		fallthrough
	case 11:
		k2 ^= uint64(tail[10]) << 16
		fallthrough
	case 10:
		k2 ^= uint64(tail[9]) << 8
		fallthrough
	case 9:
		k2 ^= uint64(tail[8]) << 0
		k2 *= c2
		k2 = rotl64(k2, 33)
		k2 *= c1
		h2 ^= k2
		fallthrough
	case 8:
		k1 ^= uint64(tail[7]) << 56
		fallthrough
	case 7:
		k1 ^= uint64(tail[6]) << 48
		fallthrough
	case 6:
		k1 ^= uint64(tail[5]) << 40
		fallthrough
	case 5:
		k1 ^= uint64(tail[4]) << 32
		fallthrough
	case 4:
		k1 ^= uint64(tail[3]) << 24
		fallthrough
	case 3:
		k1 ^= uint64(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint64(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint64(tail[0]) << 0
		k1 *= c1
		k1 = rotl64(k1, 31)
		k1 *= c2
		h1 ^= k1
	}

	//finalization
	h1 ^= uint64(len(key))
	h2 ^= uint64(len(key))

	h1 += h2
	h2 += h1

	h1 = fmix64(h1)
	h2 = fmix64(h2)

	h1 += h2
	h2 += h1

	return [2]uint64{h1, h2}
}
<|endoftext|>


<commit_before>package connection

import (
	"fmt"
	"log"
	"reflect"
	"strconv"
	"time"
)

//following two structs represent cipherscan output

type CipherscanOutput struct {
	Target         string                  `json:"target"`
	IP             string                  `json:"ip"`
	Timestamp      string                  `json:"utctimestamp"`
	ServerSide     string                  `json:"serverside"`
	CurvesFallback string                  `json:"curves_fallback"`
	CipherSuites   []CipherscanCiphersuite `json:"ciphersuite"`
}

type CipherscanCiphersuite struct {
	Cipher       string   `json:"cipher"`
	Protocols    []string `json:"protocols"`
	PubKey       []string `json:"pubkey"`
	SigAlg       []string `json:"sigalg"`
	Trusted      string   `json:"trusted"`
	TicketHint   string   `json:"ticket_hint"`
	OCSPStapling string   `json:"ocsp_stapling"`
	PFS          string   `json:"pfs"`
	Curves       []string `json:"curves,omitempty"`
}

//the following structs represent the output we want to provide to DB.

type Stored struct {
	ScanIP             string                       `json:"scanIP"`
	FirstSeenTimestamp string                       `json:"firstSeenTimestamp"`
	LastSeenTimestamp  string                       `json:"lastSeenTimestamp"`
	ServerSide         bool                         `json:"serverside"`
	CipherSuites       map[string]StoredCiphersuite `json:"cipherscanCiphersuite"`
	CurvesFallback     bool                         `json:"curvesFallback"`
	ObsoletedBy        string                       `json:"obsoletedBy,omitempty"`
}

type StoredCiphersuite struct {
	Cipher       string   `json:"cipher"`
	Protocols    []string `json:"protocols"`
	PubKey       float64  `json:"pubkey"`
	SigAlg       string   `json:"sigalg"`
	TicketHint   string   `json:"ticket_hint"`
	OCSPStapling bool     `json:"ocsp_stapling"`
	PFS          string   `json:"pfs"`
	Curves       []string `json:"curves,omitempty"`
}

func stringtoBool(s string) bool {
	if s == "True" {
		return true
	} else {
		return false
	}

}

func (c Stored) Equal(ci Stored) bool {

	if c.CurvesFallback != ci.CurvesFallback {
		return false
	}

	if c.ServerSide != ci.ServerSide {
		return false
	}

	for pos, suite := range c.CipherSuites {

		if !suite.equal(ci.CipherSuites[pos]) {
			return false
		}
	}

	return true
}

func (s StoredCiphersuite) equal(cs StoredCiphersuite) bool {

	if s.Cipher != cs.Cipher {
		return false
	}

	if s.OCSPStapling != cs.OCSPStapling {
		return false
	}

	if s.PFS != cs.PFS {
		return false
	}

	if s.PubKey != cs.PubKey {
		return false
	}

	if s.SigAlg != cs.SigAlg {
		return false
	}

	if !reflect.DeepEqual(s.Curves, cs.Curves) {
		return false
	}

	if !reflect.DeepEqual(s.Protocols, cs.Protocols) {
		return false
	}

	return true

}

func (s CipherscanOutput) convertTimestamp(t string) (time.Time, error) {

	layout := "2006-01-02T15:04:05.0Z"
	return time.Parse(layout, t)
}

func (s CipherscanOutput) Stored() (Stored, error) {

	c := Stored{}

	var err error

	t, err := s.convertTimestamp(s.Timestamp)

	if err != nil {
		return c, err
	}

	timestamp := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d", t.UTC().Year(), t.UTC().Month(), t.UTC().Day(), t.UTC().Hour(), t.UTC().Minute(), t.UTC().Second())

	c.FirstSeenTimestamp = timestamp
	c.LastSeenTimestamp = timestamp
	c.ServerSide = stringtoBool(s.ServerSide)
	c.CurvesFallback = stringtoBool(s.CurvesFallback)
	c.ScanIP = s.IP

	c.CipherSuites = make(map[string]StoredCiphersuite)

	pos := 1

	for _, cipher := range s.CipherSuites {

		newcipher := StoredCiphersuite{}

		newcipher.Cipher = cipher.Cipher
		newcipher.OCSPStapling = stringtoBool(cipher.OCSPStapling)
		newcipher.PFS = cipher.PFS

		newcipher.Protocols = cipher.Protocols

		if len(cipher.PubKey) > 1 {
			log.Println("Multiple PubKeys for ", s.Target, " at cipher :", cipher.Cipher)
		}

		if len(cipher.PubKey) > 0 {
			newcipher.PubKey, err = strconv.ParseFloat(cipher.PubKey[0], 64)
		} else {
			return c, fmt.Errorf("No Public Keys found")
		}

		if len(cipher.SigAlg) > 1 {
			log.Println("Multiple SigAlgs for ", s.Target, " at cipher :", cipher.Cipher)
		}

		if len(cipher.SigAlg) > 0 {
			newcipher.SigAlg = cipher.SigAlg[0]
		} else {
			return c, fmt.Errorf("No Signature Algorithms found")
		}

		newcipher.TicketHint = cipher.TicketHint

		if err != nil {
			return c, err
		}

		newcipher.Curves = append(newcipher.Curves, cipher.Curves...)

		c.CipherSuites[strconv.Itoa(pos)] = newcipher
		pos++
	}

	return c, nil
}
<commit_msg>retriever does not log anything, just returns the errors<commit_after>package connection

import (
	"fmt"
	"reflect"
	"strconv"
	"time"
)

//following two structs represent cipherscan output

type CipherscanOutput struct {
	Target         string                  `json:"target"`
	IP             string                  `json:"ip"`
	Timestamp      string                  `json:"utctimestamp"`
	ServerSide     string                  `json:"serverside"`
	CurvesFallback string                  `json:"curves_fallback"`
	CipherSuites   []CipherscanCiphersuite `json:"ciphersuite"`
}

type CipherscanCiphersuite struct {
	Cipher       string   `json:"cipher"`
	Protocols    []string `json:"protocols"`
	PubKey       []string `json:"pubkey"`
	SigAlg       []string `json:"sigalg"`
	Trusted      string   `json:"trusted"`
	TicketHint   string   `json:"ticket_hint"`
	OCSPStapling string   `json:"ocsp_stapling"`
	PFS          string   `json:"pfs"`
	Curves       []string `json:"curves,omitempty"`
}

//the following structs represent the output we want to provide to DB.

type Stored struct {
	ScanIP             string                       `json:"scanIP"`
	FirstSeenTimestamp string                       `json:"firstSeenTimestamp"`
	LastSeenTimestamp  string                       `json:"lastSeenTimestamp"`
	ServerSide         bool                         `json:"serverside"`
	CipherSuites       map[string]StoredCiphersuite `json:"cipherscanCiphersuite"`
	CurvesFallback     bool                         `json:"curvesFallback"`
	ObsoletedBy        string                       `json:"obsoletedBy,omitempty"`
}

type StoredCiphersuite struct {
	Cipher       string   `json:"cipher"`
	Protocols    []string `json:"protocols"`
	PubKey       float64  `json:"pubkey"`
	SigAlg       string   `json:"sigalg"`
	TicketHint   string   `json:"ticket_hint"`
	OCSPStapling bool     `json:"ocsp_stapling"`
	PFS          string   `json:"pfs"`
	Curves       []string `json:"curves,omitempty"`
}

func stringtoBool(s string) bool {
	if s == "True" {
		return true
	} else {
		return false
	}

}

func (c Stored) Equal(ci Stored) bool {

	if c.CurvesFallback != ci.CurvesFallback {
		return false
	}

	if c.ServerSide != ci.ServerSide {
		return false
	}

	for pos, suite := range c.CipherSuites {

		if !suite.equal(ci.CipherSuites[pos]) {
			return false
		}
	}

	return true
}

func (s StoredCiphersuite) equal(cs StoredCiphersuite) bool {

	if s.Cipher != cs.Cipher {
		return false
	}

	if s.OCSPStapling != cs.OCSPStapling {
		return false
	}

	if s.PFS != cs.PFS {
		return false
	}

	if s.PubKey != cs.PubKey {
		return false
	}

	if s.SigAlg != cs.SigAlg {
		return false
	}

	if !reflect.DeepEqual(s.Curves, cs.Curves) {
		return false
	}

	if !reflect.DeepEqual(s.Protocols, cs.Protocols) {
		return false
	}

	return true

}

func (s CipherscanOutput) convertTimestamp(t string) (time.Time, error) {

	layout := "2006-01-02T15:04:05.0Z"
	return time.Parse(layout, t)
}

func (s CipherscanOutput) Stored() (Stored, error) {

	c := Stored{}

	var err error

	t, err := s.convertTimestamp(s.Timestamp)

	if err != nil {
		return c, err
	}

	timestamp := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d", t.UTC().Year(), t.UTC().Month(), t.UTC().Day(), t.UTC().Hour(), t.UTC().Minute(), t.UTC().Second())

	c.FirstSeenTimestamp = timestamp
	c.LastSeenTimestamp = timestamp
	c.ServerSide = stringtoBool(s.ServerSide)
	c.CurvesFallback = stringtoBool(s.CurvesFallback)
	c.ScanIP = s.IP

	c.CipherSuites = make(map[string]StoredCiphersuite)

	pos := 1

	for _, cipher := range s.CipherSuites {

		newcipher := StoredCiphersuite{}

		newcipher.Cipher = cipher.Cipher
		newcipher.OCSPStapling = stringtoBool(cipher.OCSPStapling)
		newcipher.PFS = cipher.PFS

		newcipher.Protocols = cipher.Protocols

		if len(cipher.PubKey) > 1 {

			return c, fmt.Errorf("Multiple PubKeys for ", s.Target, " at cipher :", cipher.Cipher)

		}

		if len(cipher.PubKey) > 0 {
			newcipher.PubKey, err = strconv.ParseFloat(cipher.PubKey[0], 64)
		} else {
			return c, fmt.Errorf("No Public Keys found")
		}

		if len(cipher.SigAlg) > 1 {

			return c, fmt.Errorf("Multiple SigAlgs for ", s.Target, " at cipher :", cipher.Cipher)
		}

		if len(cipher.SigAlg) > 0 {
			newcipher.SigAlg = cipher.SigAlg[0]
		} else {
			return c, fmt.Errorf("No Signature Algorithms found")
		}

		newcipher.TicketHint = cipher.TicketHint

		if err != nil {
			return c, err
		}

		newcipher.Curves = append(newcipher.Curves, cipher.Curves...)

		c.CipherSuites[strconv.Itoa(pos)] = newcipher
		pos++
	}

	return c, nil
}
<|endoftext|>


<commit_before>package extra

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/elpinal/coco3/extra/ast"
	"github.com/elpinal/coco3/extra/parser"
	"github.com/elpinal/coco3/extra/typed"
	"github.com/elpinal/coco3/extra/types"
)

func TestEval(t *testing.T) {
	var buf bytes.Buffer
	prefix := "print: the argument is"
	printCommand := func(args []ast.Expr) error {
		_, err := fmt.Fprintln(&buf, prefix, args[0].(*ast.String).Lit)
		return err
	}
	e := Env{cmds: map[string]typed.Command{"print": {Params: []types.Type{types.String}, Fn: printCommand}}}
	src := "print 'aaa'"
	c, err := parser.Parse([]byte(src))
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}
	err = e.Eval(c)
	if err != nil {
		t.Fatalf("Eval: %v", err)
	}
	got := buf.String()
	want := prefix + " aaa\n"
	if got != want {
		t.Errorf("Eval(%q) != %q; got %q", src, want, got)
	}
}
<commit_msg>Test only Eval in eval_test.go<commit_after>package extra

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/elpinal/coco3/extra/ast"
	"github.com/elpinal/coco3/extra/typed"
	"github.com/elpinal/coco3/extra/types"
)

func TestEval(t *testing.T) {
	var buf bytes.Buffer
	prefix := "print: the argument is"
	printCommand := func(args []ast.Expr) error {
		_, err := fmt.Fprintln(&buf, prefix, args[0].(*ast.String).Lit)
		return err
	}
	e := Env{cmds: map[string]typed.Command{"print": {Params: []types.Type{types.String}, Fn: printCommand}}}
	err := e.Eval(&ast.Command{Name: "print", Args: []ast.Expr{&ast.String{Lit: "aaa"}}})
	if err != nil {
		t.Fatalf("Eval: %v", err)
	}
	got := buf.String()
	want := prefix + " aaa\n"
	if got != want {
		t.Errorf("Eval: want %q, but got %q", want, got)
	}
}
<|endoftext|>