<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
)

const (
	DefaultRefreshCfgThrottle = time.Minute
	keepAlivePeriod           = time.Minute
)

var (
	// errNotCached is returned when the instance was not found in the Client's
	// cache. It is an internal detail and is not actually ever returned to the
	// user.
	errNotCached      = errors.New("instance was not found in cache")
	refreshCertBuffer = 30 * time.Second
)

// Conn represents a connection from a client to a specific instance.
type Conn struct {
	Instance string
	Conn     net.Conn
}

// CertSource is how a Client obtains various certificates required for operation.
type CertSource interface {
	// Local returns a certificate that can be used to authenticate with the
	// provided instance.
	Local(instance string) (tls.Certificate, error)
	// Remote returns the instance's CA certificate, address, and name.
	Remote(instance string) (cert *x509.Certificate, addr, name string, err error)
}

// Client is a type to handle connecting to a Server. All fields are required
// unless otherwise specified.
type Client struct {
	// Port designates which remote port should be used when connecting to
	// instances. This value is defined by the server-side code, but for now it
	// should always be 3307.
	Port int
	// Required; specifies how certificates are obtained.
	Certs CertSource
	// Optionally tracks connections through this client. If nil, connections
	// are not tracked and will not be closed before method Run exits.
	Conns *ConnSet
	// Dialer should return a new connection to the provided address. It is
	// called on each new connection to an instance. net.Dial will be used if
	// left nil.
	Dialer func(net, addr string) (net.Conn, error)

	// RefreshCfgThrottle is the amount of time to wait between configuration
	// refreshes. If not set, it defaults to 1 minute.
	//
	// This is to prevent quota exhaustion in the case of client-side
	// malfunction.
	RefreshCfgThrottle time.Duration

	// The cfgCache holds the most recent connection configuration keyed by
	// instance. Relevant functions are refreshCfg and cachedCfg. It is
	// protected by cacheL.
	cfgCache map[string]cacheEntry
	cacheL   sync.RWMutex

	// refreshCfgL prevents multiple goroutines from contacting the Cloud SQL API at once.
	refreshCfgL sync.Mutex

	// MaxConnections is the maximum number of connections to establish
	// before refusing new connections. 0 means no limit.
	MaxConnections uint64

	// ConnectionsCounter is used to enforce the optional maxConnections limit
	ConnectionsCounter uint64
}

type cacheEntry struct {
	lastRefreshed time.Time
	// If err is not nil, the addr and cfg are not valid.
	err  error
	addr string
	cfg  *tls.Config
}

// Run causes the client to start waiting for new connections to connSrc and
// proxy them to the destination instance. It blocks until connSrc is closed.
func (c *Client) Run(connSrc <-chan Conn) {
	for conn := range connSrc {
		go c.handleConn(conn)
	}

	if err := c.Conns.Close(); err != nil {
		logging.Errorf("closing client had error: %v", err)
	}
}

func (c *Client) handleConn(conn Conn) {
	active := atomic.AddUint64(&c.ConnectionsCounter, 1)

	// Deferred decrement of ConnectionsCounter upon connection closing
	defer atomic.AddUint64(&c.ConnectionsCounter, ^uint64(0))

	if c.MaxConnections > 0 && active > c.MaxConnections {
		logging.Errorf("too many open connections (max %d)", c.MaxConnections)
		conn.Conn.Close()
		return
	}

	server, err := c.Dial(conn.Instance)
	if err != nil {
		logging.Errorf("couldn't connect to %q: %v", conn.Instance, err)
		conn.Conn.Close()
		return
	}

	if false {
		// Log the connection's traffic via the debug connection if we're in a
		// verbose mode. Note that this is the unencrypted traffic stream.
		conn.Conn = dbgConn{conn.Conn}
	}

	c.Conns.Add(conn.Instance, conn.Conn)
	copyThenClose(server, conn.Conn, conn.Instance, "local connection on "+conn.Conn.LocalAddr().String())

	if err := c.Conns.Remove(conn.Instance, conn.Conn); err != nil {
		logging.Errorf("%s", err)
	}
}

// refreshCfg uses the CertSource inside the Client to find the instance's
// address as well as construct a new tls.Config to connect to the instance. It
// caches the result.
func (c *Client) refreshCfg(instance string) (addr string, cfg *tls.Config, err error) {
	c.refreshCfgL.Lock()
	defer c.refreshCfgL.Unlock()

	throttle := c.RefreshCfgThrottle
	if throttle == 0 {
		throttle = DefaultRefreshCfgThrottle
	}

	c.cacheL.Lock()
	if c.cfgCache == nil {
		c.cfgCache = make(map[string]cacheEntry)
	}
	old, oldok := c.cfgCache[instance]
	c.cacheL.Unlock()
	if oldok && time.Since(old.lastRefreshed) < throttle {
		logging.Errorf("Throttling refreshCfg(%s): it was only called %v ago", instance, time.Since(old.lastRefreshed))
		// Refresh was called too recently, just reuse the result.
		return old.addr, old.cfg, old.err
	}

	defer func() {
		c.cacheL.Lock()
		c.cfgCache[instance] = cacheEntry{
			lastRefreshed: time.Now(),
			err:           err,
			addr:          addr,
			cfg:           cfg,
		}
		c.cacheL.Unlock()
	}()

	mycert, err := c.Certs.Local(instance)
	if err != nil {
		return "", nil, err
	}

	scert, addr, name, err := c.Certs.Remote(instance)
	if err != nil {
		return "", nil, err
	}
	certs := x509.NewCertPool()
	certs.AddCert(scert)

	cfg = &tls.Config{
		ServerName:   name,
		Certificates: []tls.Certificate{mycert},
		RootCAs:      certs,
		// We need to set InsecureSkipVerify to true due to
		// https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/194
		// https://tip.golang.org/doc/go1.11#crypto/x509
		//
		// Since we have a secure channel to the Cloud SQL API which we use to retrieve the
		// certificates, we instead need to implement our own VerifyPeerCertificate function
		// that will verify that the certificate is OK.
		InsecureSkipVerify:    true,
		VerifyPeerCertificate: genVerifyPeerCertificateFunc(name, certs),
	}

	expire := mycert.Leaf.NotAfter
	now := time.Now()
	timeToRefresh := expire.Sub(now) - refreshCertBuffer
	if timeToRefresh <= 0 {
		err = fmt.Errorf("new ephemeral certificate expires too soon: current time: %v, certificate expires: %v", expire, now)
		logging.Errorf("ephemeral certificate (%+v) error: %v", mycert, err)
		return "", nil, err
	}
	go c.refreshCertAfter(instance, timeToRefresh)

	return fmt.Sprintf("%s:%d", addr, c.Port), cfg, nil
}

// refreshCertAfter refreshes the epehemeral certificate of the instance after timeToRefresh.
func (c *Client) refreshCertAfter(instance string, timeToRefresh time.Duration) {
	<-time.After(timeToRefresh)
	logging.Verbosef("ephemeral certificate for instance %s will expire soon, refreshing now.", instance)
	if _, _, err := c.refreshCfg(instance); err != nil {
		logging.Errorf("failed to refresh the ephemeral certificate for %s before expiring: %v", instance, err)
	}
}

// genVerifyPeerCertificateFunc creates a VerifyPeerCertificate func that verifies that the peer
// certificate is in the cert pool. We need to define our own because of our sketchy non-standard
// CNs.
func genVerifyPeerCertificateFunc(instanceName string, pool *x509.CertPool) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
	return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
		if len(rawCerts) == 0 {
			return fmt.Errorf("no certificate to verify")
		}

		cert, err := x509.ParseCertificate(rawCerts[0])
		if err != nil {
			return fmt.Errorf("x509.ParseCertificate(rawCerts[0]) returned error: %v", err)
		}

		opts := x509.VerifyOptions{Roots: pool}
		if _, err = cert.Verify(opts); err != nil {
			return err
		}

		if cert.Subject.CommonName != instanceName {
			return fmt.Errorf("certificate had CN %q, expected %q", cert.Subject.CommonName, instanceName)
		}
		return nil
	}
}

func isExpired(cfg *tls.Config) bool {
	return time.Now().After(cfg.Certificates[0].Leaf.NotAfter)
}

func (c *Client) cachedCfg(instance string) (string, *tls.Config) {
	c.cacheL.RLock()
	ret, ok := c.cfgCache[instance]
	c.cacheL.RUnlock()

	// Don't waste time returning an expired/invalid cert.
	if !ok || ret.err != nil || isExpired(ret.cfg) {
		return "", nil
	}
	return ret.addr, ret.cfg
}

// Dial uses the configuration stored in the client to connect to an instance.
// If this func returns a nil error the connection is correctly authenticated
// to connect to the instance.
func (c *Client) Dial(instance string) (net.Conn, error) {
	if addr, cfg := c.cachedCfg(instance); cfg != nil {
		ret, err := c.tryConnect(addr, cfg)
		if err == nil {
			return ret, err
		}
	}

	addr, cfg, err := c.refreshCfg(instance)
	if err != nil {
		return nil, err
	}
	return c.tryConnect(addr, cfg)
}

func (c *Client) tryConnect(addr string, cfg *tls.Config) (net.Conn, error) {
	d := c.Dialer
	if d == nil {
		d = net.Dial
	}
	conn, err := d("tcp", addr)
	if err != nil {
		return nil, err
	}
	type setKeepAliver interface {
		SetKeepAlive(keepalive bool) error
		SetKeepAlivePeriod(d time.Duration) error
	}

	if s, ok := conn.(setKeepAliver); ok {
		if err := s.SetKeepAlive(true); err != nil {
			logging.Verbosef("Couldn't set KeepAlive to true: %v", err)
		} else if err := s.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
			logging.Verbosef("Couldn't set KeepAlivePeriod to %v", keepAlivePeriod)
		}
	} else {
		logging.Verbosef("KeepAlive not supported: long-running tcp connections may be killed by the OS.")
	}

	ret := tls.Client(conn, cfg)
	if err := ret.Handshake(); err != nil {
		ret.Close()
		return nil, err
	}
	return ret, nil
}

// NewConnSrc returns a chan which can be used to receive connections
// on the passed Listener. All requests sent to the returned chan will have the
// instance name provided here. The chan will be closed if the Listener returns
// an error.
func NewConnSrc(instance string, l net.Listener) <-chan Conn {
	ch := make(chan Conn)
	go func() {
		for {
			start := time.Now()
			c, err := l.Accept()
			if err != nil {
				logging.Errorf("listener (%#v) had error: %v", l, err)
				if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
					d := 10*time.Millisecond - time.Since(start)
					if d > 0 {
						time.Sleep(d)
					}
					continue
				}
				l.Close()
				close(ch)
				return
			}
			ch <- Conn{instance, c}
		}
	}()
	return ch
}

// Shutdown waits up to a given amount of time for all active connections to
// close. Returns an error if there are still active connections after waiting
// for the whole length of the timeout.
func (c *Client) Shutdown(termTimeout time.Duration) error {
	termTime := time.Now().Add(termTimeout)
	for termTime.After(time.Now()) && atomic.LoadUint64(&c.ConnectionsCounter) > 0 {
		time.Sleep(1)
	}

	active := atomic.LoadUint64(&c.ConnectionsCounter)
	if active == 0 {
		return nil
	}
	return fmt.Errorf("%d active connections still exist after waiting for %v", active, termTimeout)
}
<commit_msg>Enable SOCKS Proxy (#361)<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
	"golang.org/x/net/proxy"
)

const (
	DefaultRefreshCfgThrottle = time.Minute
	keepAlivePeriod           = time.Minute
)

var (
	// errNotCached is returned when the instance was not found in the Client's
	// cache. It is an internal detail and is not actually ever returned to the
	// user.
	errNotCached      = errors.New("instance was not found in cache")
	refreshCertBuffer = 30 * time.Second
)

// Conn represents a connection from a client to a specific instance.
type Conn struct {
	Instance string
	Conn     net.Conn
}

// CertSource is how a Client obtains various certificates required for operation.
type CertSource interface {
	// Local returns a certificate that can be used to authenticate with the
	// provided instance.
	Local(instance string) (tls.Certificate, error)
	// Remote returns the instance's CA certificate, address, and name.
	Remote(instance string) (cert *x509.Certificate, addr, name string, err error)
}

// Client is a type to handle connecting to a Server. All fields are required
// unless otherwise specified.
type Client struct {
	// Port designates which remote port should be used when connecting to
	// instances. This value is defined by the server-side code, but for now it
	// should always be 3307.
	Port int
	// Required; specifies how certificates are obtained.
	Certs CertSource
	// Optionally tracks connections through this client. If nil, connections
	// are not tracked and will not be closed before method Run exits.
	Conns *ConnSet
	// Dialer should return a new connection to the provided address. It is
	// called on each new connection to an instance. net.Dial will be used if
	// left nil.
	Dialer func(net, addr string) (net.Conn, error)

	// RefreshCfgThrottle is the amount of time to wait between configuration
	// refreshes. If not set, it defaults to 1 minute.
	//
	// This is to prevent quota exhaustion in the case of client-side
	// malfunction.
	RefreshCfgThrottle time.Duration

	// The cfgCache holds the most recent connection configuration keyed by
	// instance. Relevant functions are refreshCfg and cachedCfg. It is
	// protected by cacheL.
	cfgCache map[string]cacheEntry
	cacheL   sync.RWMutex

	// refreshCfgL prevents multiple goroutines from contacting the Cloud SQL API at once.
	refreshCfgL sync.Mutex

	// MaxConnections is the maximum number of connections to establish
	// before refusing new connections. 0 means no limit.
	MaxConnections uint64

	// ConnectionsCounter is used to enforce the optional maxConnections limit
	ConnectionsCounter uint64
}

type cacheEntry struct {
	lastRefreshed time.Time
	// If err is not nil, the addr and cfg are not valid.
	err  error
	addr string
	cfg  *tls.Config
}

// Run causes the client to start waiting for new connections to connSrc and
// proxy them to the destination instance. It blocks until connSrc is closed.
func (c *Client) Run(connSrc <-chan Conn) {
	for conn := range connSrc {
		go c.handleConn(conn)
	}

	if err := c.Conns.Close(); err != nil {
		logging.Errorf("closing client had error: %v", err)
	}
}

func (c *Client) handleConn(conn Conn) {
	active := atomic.AddUint64(&c.ConnectionsCounter, 1)

	// Deferred decrement of ConnectionsCounter upon connection closing
	defer atomic.AddUint64(&c.ConnectionsCounter, ^uint64(0))

	if c.MaxConnections > 0 && active > c.MaxConnections {
		logging.Errorf("too many open connections (max %d)", c.MaxConnections)
		conn.Conn.Close()
		return
	}

	server, err := c.Dial(conn.Instance)
	if err != nil {
		logging.Errorf("couldn't connect to %q: %v", conn.Instance, err)
		conn.Conn.Close()
		return
	}

	if false {
		// Log the connection's traffic via the debug connection if we're in a
		// verbose mode. Note that this is the unencrypted traffic stream.
		conn.Conn = dbgConn{conn.Conn}
	}

	c.Conns.Add(conn.Instance, conn.Conn)
	copyThenClose(server, conn.Conn, conn.Instance, "local connection on "+conn.Conn.LocalAddr().String())

	if err := c.Conns.Remove(conn.Instance, conn.Conn); err != nil {
		logging.Errorf("%s", err)
	}
}

// refreshCfg uses the CertSource inside the Client to find the instance's
// address as well as construct a new tls.Config to connect to the instance. It
// caches the result.
func (c *Client) refreshCfg(instance string) (addr string, cfg *tls.Config, err error) {
	c.refreshCfgL.Lock()
	defer c.refreshCfgL.Unlock()

	throttle := c.RefreshCfgThrottle
	if throttle == 0 {
		throttle = DefaultRefreshCfgThrottle
	}

	c.cacheL.Lock()
	if c.cfgCache == nil {
		c.cfgCache = make(map[string]cacheEntry)
	}
	old, oldok := c.cfgCache[instance]
	c.cacheL.Unlock()
	if oldok && time.Since(old.lastRefreshed) < throttle {
		logging.Errorf("Throttling refreshCfg(%s): it was only called %v ago", instance, time.Since(old.lastRefreshed))
		// Refresh was called too recently, just reuse the result.
		return old.addr, old.cfg, old.err
	}

	defer func() {
		c.cacheL.Lock()
		c.cfgCache[instance] = cacheEntry{
			lastRefreshed: time.Now(),
			err:           err,
			addr:          addr,
			cfg:           cfg,
		}
		c.cacheL.Unlock()
	}()

	mycert, err := c.Certs.Local(instance)
	if err != nil {
		return "", nil, err
	}

	scert, addr, name, err := c.Certs.Remote(instance)
	if err != nil {
		return "", nil, err
	}
	certs := x509.NewCertPool()
	certs.AddCert(scert)

	cfg = &tls.Config{
		ServerName:   name,
		Certificates: []tls.Certificate{mycert},
		RootCAs:      certs,
		// We need to set InsecureSkipVerify to true due to
		// https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/194
		// https://tip.golang.org/doc/go1.11#crypto/x509
		//
		// Since we have a secure channel to the Cloud SQL API which we use to retrieve the
		// certificates, we instead need to implement our own VerifyPeerCertificate function
		// that will verify that the certificate is OK.
		InsecureSkipVerify:    true,
		VerifyPeerCertificate: genVerifyPeerCertificateFunc(name, certs),
	}

	expire := mycert.Leaf.NotAfter
	now := time.Now()
	timeToRefresh := expire.Sub(now) - refreshCertBuffer
	if timeToRefresh <= 0 {
		err = fmt.Errorf("new ephemeral certificate expires too soon: current time: %v, certificate expires: %v", expire, now)
		logging.Errorf("ephemeral certificate (%+v) error: %v", mycert, err)
		return "", nil, err
	}
	go c.refreshCertAfter(instance, timeToRefresh)

	return fmt.Sprintf("%s:%d", addr, c.Port), cfg, nil
}

// refreshCertAfter refreshes the epehemeral certificate of the instance after timeToRefresh.
func (c *Client) refreshCertAfter(instance string, timeToRefresh time.Duration) {
	<-time.After(timeToRefresh)
	logging.Verbosef("ephemeral certificate for instance %s will expire soon, refreshing now.", instance)
	if _, _, err := c.refreshCfg(instance); err != nil {
		logging.Errorf("failed to refresh the ephemeral certificate for %s before expiring: %v", instance, err)
	}
}

// genVerifyPeerCertificateFunc creates a VerifyPeerCertificate func that verifies that the peer
// certificate is in the cert pool. We need to define our own because of our sketchy non-standard
// CNs.
func genVerifyPeerCertificateFunc(instanceName string, pool *x509.CertPool) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
	return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
		if len(rawCerts) == 0 {
			return fmt.Errorf("no certificate to verify")
		}

		cert, err := x509.ParseCertificate(rawCerts[0])
		if err != nil {
			return fmt.Errorf("x509.ParseCertificate(rawCerts[0]) returned error: %v", err)
		}

		opts := x509.VerifyOptions{Roots: pool}
		if _, err = cert.Verify(opts); err != nil {
			return err
		}

		if cert.Subject.CommonName != instanceName {
			return fmt.Errorf("certificate had CN %q, expected %q", cert.Subject.CommonName, instanceName)
		}
		return nil
	}
}

func isExpired(cfg *tls.Config) bool {
	return time.Now().After(cfg.Certificates[0].Leaf.NotAfter)
}

func (c *Client) cachedCfg(instance string) (string, *tls.Config) {
	c.cacheL.RLock()
	ret, ok := c.cfgCache[instance]
	c.cacheL.RUnlock()

	// Don't waste time returning an expired/invalid cert.
	if !ok || ret.err != nil || isExpired(ret.cfg) {
		return "", nil
	}
	return ret.addr, ret.cfg
}

// Dial uses the configuration stored in the client to connect to an instance.
// If this func returns a nil error the connection is correctly authenticated
// to connect to the instance.
func (c *Client) Dial(instance string) (net.Conn, error) {
	if addr, cfg := c.cachedCfg(instance); cfg != nil {
		ret, err := c.tryConnect(addr, cfg)
		if err == nil {
			return ret, err
		}
	}

	addr, cfg, err := c.refreshCfg(instance)
	if err != nil {
		return nil, err
	}
	return c.tryConnect(addr, cfg)
}

func (c *Client) tryConnect(addr string, cfg *tls.Config) (net.Conn, error) {
	d := c.Dialer
	if d == nil {
		d = proxy.FromEnvironment().Dial
	}
	conn, err := d("tcp", addr)
	if err != nil {
		return nil, err
	}
	type setKeepAliver interface {
		SetKeepAlive(keepalive bool) error
		SetKeepAlivePeriod(d time.Duration) error
	}

	if s, ok := conn.(setKeepAliver); ok {
		if err := s.SetKeepAlive(true); err != nil {
			logging.Verbosef("Couldn't set KeepAlive to true: %v", err)
		} else if err := s.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
			logging.Verbosef("Couldn't set KeepAlivePeriod to %v", keepAlivePeriod)
		}
	} else {
		logging.Verbosef("KeepAlive not supported: long-running tcp connections may be killed by the OS.")
	}

	ret := tls.Client(conn, cfg)
	if err := ret.Handshake(); err != nil {
		ret.Close()
		return nil, err
	}
	return ret, nil
}

// NewConnSrc returns a chan which can be used to receive connections
// on the passed Listener. All requests sent to the returned chan will have the
// instance name provided here. The chan will be closed if the Listener returns
// an error.
func NewConnSrc(instance string, l net.Listener) <-chan Conn {
	ch := make(chan Conn)
	go func() {
		for {
			start := time.Now()
			c, err := l.Accept()
			if err != nil {
				logging.Errorf("listener (%#v) had error: %v", l, err)
				if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
					d := 10*time.Millisecond - time.Since(start)
					if d > 0 {
						time.Sleep(d)
					}
					continue
				}
				l.Close()
				close(ch)
				return
			}
			ch <- Conn{instance, c}
		}
	}()
	return ch
}

// Shutdown waits up to a given amount of time for all active connections to
// close. Returns an error if there are still active connections after waiting
// for the whole length of the timeout.
func (c *Client) Shutdown(termTimeout time.Duration) error {
	termTime := time.Now().Add(termTimeout)
	for termTime.After(time.Now()) && atomic.LoadUint64(&c.ConnectionsCounter) > 0 {
		time.Sleep(1)
	}

	active := atomic.LoadUint64(&c.ConnectionsCounter)
	if active == 0 {
		return nil
	}
	return fmt.Errorf("%d active connections still exist after waiting for %v", active, termTimeout)
}
<|endoftext|>


<commit_before>package udnssdk

import "testing"

func Test_ListAccountsOfUser(t *testing.T) {
	if !enableIntegrationTests {
		t.SkipNow()
	}
	if !enableAccountTests {
		t.SkipNow()
	}

	testClient, err := NewClient(testUsername, testPassword, testBaseURL)
	if err != nil {
		t.Fatal(err)
	}

	accounts, resp, err := testClient.Accounts.GetAccountsOfUser()

	if err != nil {
		if resp.Response.StatusCode == 404 {
			t.SkipNow()
		}
		t.Fatal(err)
	}
	t.Logf("Accounts: %+v \n", accounts)
	t.Logf("Response: %+v\n", resp.Response)
}
<commit_msg>account_test.go: update to new interface<commit_after>package udnssdk

import "testing"

func Test_ListAccountsOfUser(t *testing.T) {
	if !enableIntegrationTests {
		t.SkipNow()
	}
	if !enableAccountTests {
		t.SkipNow()
	}

	testClient, err := NewClient(testUsername, testPassword, testBaseURL)
	if err != nil {
		t.Fatal(err)
	}

	accounts, resp, err := testClient.Accounts.Select()

	if err != nil {
		if resp.Response.StatusCode == 404 {
			t.SkipNow()
		}
		t.Fatal(err)
	}
	t.Logf("Accounts: %+v \n", accounts)
	t.Logf("Response: %+v\n", resp.Response)
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/common/response"
	"socialapi/workers/payment"

	"github.com/stripe/stripe-go"
)

// DeleteSubscription deletes the subscription of group
func DeleteSubscription(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	if err := checkContext(context); err != nil {
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		payment.DeleteSubscriptionForGroup(context.GroupName),
	)
}

// GetSubscription gets the subscription of group
func GetSubscription(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	if err := checkContext(context); err != nil {
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		payment.GetSubscriptionForGroup(context.GroupName),
	)
}

// CreateSubscription creates the subscription of group
func CreateSubscription(u *url.URL, h http.Header, params *stripe.SubParams, context *models.Context) (int, http.Header, interface{}, error) {
	if err := checkContext(context); err != nil {
		return response.NewBadRequest(err)
	}

	// TODO
	// Add idempotency here
	//

	return response.HandleResultAndError(
		payment.CreateSubscriptionForGroup(context.GroupName, params),
	)
}
<commit_msg>go/payment: allow non-admins to fetch sub info<commit_after>package api

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/common/response"
	"socialapi/workers/payment"

	"github.com/stripe/stripe-go"
)

// DeleteSubscription deletes the subscription of group
func DeleteSubscription(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	if err := checkContext(context); err != nil {
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		payment.DeleteSubscriptionForGroup(context.GroupName),
	)
}

// GetSubscription gets the subscription of group
func GetSubscription(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	return response.HandleResultAndError(
		payment.GetSubscriptionForGroup(context.GroupName),
	)
}

// CreateSubscription creates the subscription of group
func CreateSubscription(u *url.URL, h http.Header, params *stripe.SubParams, context *models.Context) (int, http.Header, interface{}, error) {
	if err := checkContext(context); err != nil {
		return response.NewBadRequest(err)
	}

	// TODO
	// Add idempotency here
	//

	return response.HandleResultAndError(
		payment.CreateSubscriptionForGroup(context.GroupName, params),
	)
}
<|endoftext|>


<commit_before>package agg

import (
	"github.com/Supernomad/quantum/common"
	"testing"
	"time"
)

func TestAgg(t *testing.T) {
	stats := make([]*common.Stats, 3)
	for i := 0; i < 3; i++ {
		stats[i] = common.NewStats()
	}
	agg := New(&common.Config{
		StatsWindow: 1,
		NumWorkers:  3,
	}, stats, stats)
	agg.Start()
	time.Sleep(2 * time.Second)
	agg.Stop()
}
<commit_msg>Added some more testing for agg<commit_after>package agg

import (
	"github.com/Supernomad/quantum/common"
	"testing"
	"time"
)

func TestAgg(t *testing.T) {
	stats := make([]*common.Stats, 3)
	for i := 0; i < 3; i++ {
		stats[i] = common.NewStats()
		stats[i].Links["10.0.0.0"] = common.NewStats()
	}
	stats[0].Links["10.0.0.1"] = common.NewStats()

	agg := New(&common.Config{
		StatsWindow: 1,
		NumWorkers:  3,
	}, stats, stats)
	agg.Start()
	time.Sleep(2 * time.Second)
	agg.Stop()
}
<|endoftext|>


<commit_before>package gohistogram

// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
// Please see the LICENSE file for applicable license terms.

// Histogram is the interface that wraps the Add and Quantile methods.
type Histogram interface {
	// Add adds a new value, n, to the histogram. Trimming is done
	// automatically.
	Add(n float64)

	// Quantile returns an approximation.
	Quantile(n float64) (q float64)
}

type bin struct {
	value float64
	count float64
}
<commit_msg>Added String() function to the histogram interface.<commit_after>package gohistogram

// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
// Please see the LICENSE file for applicable license terms.

// Histogram is the interface that wraps the Add and Quantile methods.
type Histogram interface {
	// Add adds a new value, n, to the histogram. Trimming is done
	// automatically.
	Add(n float64)

	// Quantile returns an approximation.
	Quantile(n float64) (q float64)

	// String returns a string reprentation of the histogram,
	// which is useful for printing to a terminal.
	String() (str string)
}

type bin struct {
	value float64
	count float64
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"sort"

	"github.com/blevesearch/bleve/index/upside_down"
)

type TermCount struct {
	Term  string
	Count uint64
}

type sortedTermCounts []TermCount

func (s sortedTermCounts) Len() int {
	return len(s)
}

func (s sortedTermCounts) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortedTermCounts) Less(i, j int) bool {
	//return s[i].Term < s[j].Term
	return s[i].Count < s[j].Count
}

func decodeTermFrequencyRow(tfr *upside_down.TermFrequencyRow) ([]byte, uint64, error) {
	key := tfr.Key()
	// Strip field
	if len(key) < 3 {
		return nil, 0, fmt.Errorf("key is unexpectedly short")
	}
	key = key[3:]
	pos := bytes.IndexByte(key, upside_down.ByteSeparator)
	if pos < 0 {
		return nil, 0, fmt.Errorf("cannot extract term")
	}
	term := key[:pos]

	value := tfr.Value()
	n, read := binary.Uvarint(value)
	if read <= 0 {
		return nil, 0, fmt.Errorf("could not decode term frequency")
	}
	return term, n, nil
}

var (
	histogramCmd = app.Command("histogram", "generate indexed terms histogram")
)

func histogramFn(cfg *Config) error {
	index, err := OpenOfferIndex(cfg.Index())
	if err != nil {
		return err
	}
	terms := map[string]uint64{}
	rows := index.DumpAll()
	var failed error
	for item := range rows {
		if failed != nil {
			continue
		}
		switch item.(type) {
		case error:
			failed = item.(error)
		case *upside_down.TermFrequencyRow:
			row := item.(*upside_down.TermFrequencyRow)
			term, freq, err := decodeTermFrequencyRow(row)
			if err != nil {
				failed = err
				continue
			}
			terms[string(term)] += freq
		}
	}
	if failed != nil {
		return failed
	}
	counts := make([]TermCount, 0, len(terms))
	for k, v := range terms {
		counts = append(counts, TermCount{
			Term:  k,
			Count: v,
		})
	}
	sort.Sort(sortedTermCounts(counts))
	for _, t := range counts {
		fmt.Println(t.Term, t.Count)
	}
	return nil
}

var (
	indexStatsCmd = app.Command("indexstats",
		"collect and display full text index statistics")
)

func indexStatsFn(cfg *Config) error {
	index, err := OpenOfferIndex(cfg.Index())
	if err != nil {
		return err
	}
	kinds := map[byte]struct {
		Count int
		Size  int
	}{}
	unknown := 0
	rows := index.DumpAll()
	var failed error
	for item := range rows {
		if failed != nil {
			continue
		}
		switch item.(type) {
		case error:
			failed = item.(error)
		case upside_down.UpsideDownCouchRow:
			row := item.(upside_down.UpsideDownCouchRow)
			key := row.Key()
			st := kinds[key[0]]
			st.Count++
			st.Size += row.KeySize() + row.ValueSize()
			kinds[key[0]] = st
		default:
			unknown++
		}
	}
	totalCount := 0
	totalSize := 0
	for i := 0; i < 256; i++ {
		st, ok := kinds[byte(i)]
		if !ok {
			continue
		}
		fmt.Printf("%s: count: %d, size: %.1fkB\n", string([]byte{byte(i)}),
			st.Count, float64(st.Size)/1024.)
		totalCount += st.Count
		totalSize += st.Size
	}
	fmt.Printf("total: count: %d, size: %.1fkB\n", totalCount, float64(totalSize)/1024.)
	if unknown > 0 {
		fmt.Printf("unknown rows: %d\n", unknown)
	}
	return nil
}
<commit_msg>histogram: add optional index path argument<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"sort"

	"github.com/blevesearch/bleve/index/upside_down"
)

type TermCount struct {
	Term  string
	Count uint64
}

type sortedTermCounts []TermCount

func (s sortedTermCounts) Len() int {
	return len(s)
}

func (s sortedTermCounts) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortedTermCounts) Less(i, j int) bool {
	//return s[i].Term < s[j].Term
	return s[i].Count < s[j].Count
}

func decodeTermFrequencyRow(tfr *upside_down.TermFrequencyRow) ([]byte, uint64, error) {
	key := tfr.Key()
	// Strip field
	if len(key) < 3 {
		return nil, 0, fmt.Errorf("key is unexpectedly short")
	}
	key = key[3:]
	pos := bytes.IndexByte(key, upside_down.ByteSeparator)
	if pos < 0 {
		return nil, 0, fmt.Errorf("cannot extract term")
	}
	term := key[:pos]

	value := tfr.Value()
	n, read := binary.Uvarint(value)
	if read <= 0 {
		return nil, 0, fmt.Errorf("could not decode term frequency")
	}
	return term, n, nil
}

var (
	histogramCmd = app.Command("histogram", "generate indexed terms histogram")
)

func histogramFn(cfg *Config) error {
	index, err := OpenOfferIndex(cfg.Index())
	if err != nil {
		return err
	}
	terms := map[string]uint64{}
	rows := index.DumpAll()
	var failed error
	for item := range rows {
		if failed != nil {
			continue
		}
		switch item.(type) {
		case error:
			failed = item.(error)
		case *upside_down.TermFrequencyRow:
			row := item.(*upside_down.TermFrequencyRow)
			term, freq, err := decodeTermFrequencyRow(row)
			if err != nil {
				failed = err
				continue
			}
			terms[string(term)] += freq
		}
	}
	if failed != nil {
		return failed
	}
	counts := make([]TermCount, 0, len(terms))
	for k, v := range terms {
		counts = append(counts, TermCount{
			Term:  k,
			Count: v,
		})
	}
	sort.Sort(sortedTermCounts(counts))
	for _, t := range counts {
		fmt.Println(t.Term, t.Count)
	}
	return nil
}

var (
	indexStatsCmd = app.Command("indexstats",
		"collect and display full text index statistics")
	indexPathArg = indexStatsCmd.Arg("path", "index path").String()
)

func indexStatsFn(cfg *Config) error {
	path := *indexPathArg
	if path == "" {
		path = cfg.Index()
	}
	index, err := OpenOfferIndex(path)
	if err != nil {
		return err
	}
	kinds := map[byte]struct {
		Count int
		Size  int
	}{}
	unknown := 0
	rows := index.DumpAll()
	var failed error
	for item := range rows {
		if failed != nil {
			continue
		}
		switch item.(type) {
		case error:
			failed = item.(error)
		case upside_down.UpsideDownCouchRow:
			row := item.(upside_down.UpsideDownCouchRow)
			key := row.Key()
			st := kinds[key[0]]
			st.Count++
			st.Size += row.KeySize() + row.ValueSize()
			kinds[key[0]] = st
		default:
			unknown++
		}
	}
	totalCount := 0
	totalSize := 0
	for i := 0; i < 256; i++ {
		st, ok := kinds[byte(i)]
		if !ok {
			continue
		}
		fmt.Printf("%s: count: %d, size: %.1fkB\n", string([]byte{byte(i)}),
			st.Count, float64(st.Size)/1024.)
		totalCount += st.Count
		totalSize += st.Size
	}
	fmt.Printf("total: count: %d, size: %.1fkB\n", totalCount, float64(totalSize)/1024.)
	if unknown > 0 {
		fmt.Printf("unknown rows: %d\n", unknown)
	}
	return nil
}
<|endoftext|>


<commit_before>package gohl7

import (
	"errors"
	//"log"
)

var (
	ErrMssgHeader     = errors.New("Invalid Message Header")
	ErrMssgEncoding   = errors.New("Invalid message encoding field")
	ErrUnexpectedCase = errors.New("Unexpected case, implementation error")
	errNoMoreData     = errors.New("no more data")
)

const (
	HEADER_LABEL      = "MSH"
	CR           byte = '\r'
	NL           byte = '\n'
)

type Hl7Parser struct {
	enc  *Encoding
	mssg *Message
}

func NewHl7Parser(source []byte) (*Hl7Parser, error) {

	l := len(source)

	if l < 3 || string(source[:3]) != HEADER_LABEL {
		return nil, ErrMssgHeader
	}

	if l < 8 {
		return nil, ErrMssgEncoding
	}

	enc, err := ParseEncoding(source[3:8])
	if err != nil {
		return nil, err
	}

	segments := NewComplexField(message, MessageValidator)

	return &Hl7Parser{
		enc: enc,
		mssg: &Message{
			raw:          source,
			ComplexField: segments,
		},
	}, nil

}

func (p *Hl7Parser) Parse() (*Message, error) {

	mssg := p.mssg
	currentSegment := NewComplexField(segment, SegmentValidator)

	//manually adding header
	err := currentSegment.Push(NewSimpleField(mssg.raw[:3]))
	if err != nil {
		return nil, err
	}

	//manually adding encoding
	err = currentSegment.Push(NewSimpleField(mssg.raw[4:8]))
	if err != nil {
		return nil, err
	}

	last := Simple
	i, l := 9, len(mssg.raw)

	for i < l {

		nextF, consumed, err := next(mssg.raw[i:], p.enc)

		if err != nil {

			if err != errNoMoreData {
				return nil, err
			}
			//treat end of data as segment
			nextF = segment

		}

		value := mssg.raw[i : i+consumed]

		i = i + consumed + 1

		switch {
		case last == segment && nextF == Simple:
			//segment header case
			fallthrough

		case last == Simple && nextF == Simple:
			err = currentSegment.Push(NewSimpleField(value))

		case last == Simple && nextF == segment:
			//append last part of segment
			err = currentSegment.Push(NewSimpleField(value))
			//add current segment to the message
			mssg.Push(currentSegment)
			//create new segment
			currentSegment = NewComplexField(segment, SegmentValidator)

		case last == Simple && nextF == Component:
			//create complex field component
			complexF := NewComplexField(Component, ComponentValidator)
			//append simple field
			err = complexF.Push(NewSimpleField(value))
			//push component into segment
			currentSegment.Push(complexF)

		case last == Component && nextF == Simple:
			fallthrough
		case last == Component && nextF == Component:
			err = pushChildToLastChild(currentSegment, NewSimpleField(value))
		case last == Component && nextF == segment:
			err = pushChildToLastChild(currentSegment, NewSimpleField(value))
			//add current segment to the message
			mssg.Push(currentSegment)
			//create new segment
			currentSegment = NewComplexField(segment, SegmentValidator)

		case last == segment && nextF == segment:
			//special case for CR+EndOfData
			//noop
		default:
			err = ErrUnexpectedCase
		}

		if err != nil {
			return nil, err
		}

		last = nextF

	}

	return mssg, nil
}

func next(source []byte, enc *Encoding) (FieldType, int, error) {

	l, k := len(source), 0

	for ; k < l; k++ {

		v := source[k]
		switch v {
		case enc.Field:
			return Simple, k, nil
		case enc.Component:
			return Component, k, nil
		case enc.Repeated:
			return Component, k, nil
		case enc.Component:
			return SubComponent, k, nil
		case CR:
			return segment, k, nil
		case enc.Escaping:
			//continue
			k++
		}
	}

	return 0, k + 1, errNoMoreData
}

//Given a complex field append a new child to its last child
//in other word: add a grandchild
//in other words: push child to last child
func pushChildToLastChild(parent *ComplexField, newChild Field) error {

	lastField, err := parent.Pop()
	if err != nil {
		return err
	}

	//converting last field back to *ComplexField
	complexF, ok := lastField.(*ComplexField)
	if !ok {
		return ErrUnexpectedCase
	}

	//push new child
	err = complexF.Push(newChild)
	if err != nil {
		return err
	}

	//push back the last complex field
	return parent.Push(complexF)
}
<commit_msg>Parsing case: SimpleField to SubComponent<commit_after>package gohl7

import (
	"errors"
	//"log"
)

var (
	ErrMssgHeader     = errors.New("Invalid Message Header")
	ErrMssgEncoding   = errors.New("Invalid message encoding field")
	ErrUnexpectedCase = errors.New("Unexpected case, implementation error")
	errNoMoreData     = errors.New("no more data")
)

const (
	HEADER_LABEL      = "MSH"
	CR           byte = '\r'
	NL           byte = '\n'
)

type Hl7Parser struct {
	enc  *Encoding
	mssg *Message
}

func NewHl7Parser(source []byte) (*Hl7Parser, error) {

	l := len(source)

	if l < 3 || string(source[:3]) != HEADER_LABEL {
		return nil, ErrMssgHeader
	}

	if l < 8 {
		return nil, ErrMssgEncoding
	}

	enc, err := ParseEncoding(source[3:8])
	if err != nil {
		return nil, err
	}

	segments := NewComplexField(message, MessageValidator)

	return &Hl7Parser{
		enc: enc,
		mssg: &Message{
			raw:          source,
			ComplexField: segments,
		},
	}, nil

}

func (p *Hl7Parser) Parse() (*Message, error) {

	mssg := p.mssg
	currentSegment := NewComplexField(segment, SegmentValidator)

	//manually adding header
	err := currentSegment.Push(NewSimpleField(mssg.raw[:3]))
	if err != nil {
		return nil, err
	}

	//manually adding encoding
	err = currentSegment.Push(NewSimpleField(mssg.raw[4:8]))
	if err != nil {
		return nil, err
	}

	last := Simple
	i, l := 9, len(mssg.raw)

	for i < l {

		nextF, consumed, err := next(mssg.raw[i:], p.enc)

		if err != nil {

			if err != errNoMoreData {
				return nil, err
			}
			//treat end of data as segment
			nextF = segment

		}

		value := mssg.raw[i : i+consumed]

		i = i + consumed + 1

		switch {
		case last == segment && nextF == Simple:
			//segment header case
			fallthrough

		case last == Simple && nextF == Simple:
			err = currentSegment.Push(NewSimpleField(value))

		case last == Simple && nextF == segment:
			//append last part of segment
			err = currentSegment.Push(NewSimpleField(value))
			//add current segment to the message
			mssg.Push(currentSegment)
			//create new segment
			currentSegment = NewComplexField(segment, SegmentValidator)

		case last == Simple && nextF == Component:
			//create complex field component
			complexF := NewComplexField(Component, ComponentValidator)
			//push component into segment
			currentSegment.Push(complexF)
			//append simple field
			complexF.Push(NewSimpleField(value))

		case last == Simple && nextF == SubComponent:

			//create complex field component
			componentF := NewComplexField(Component, ComponentValidator)
			//push component into segment
			currentSegment.Push(componentF)
			//create comples field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			componentF.Push(subcomponentF)
			//push simple field into subcomponent
			subcomponentF.Push(NewSimpleField(value))

		case last == Component && nextF == Simple:
			fallthrough
		case last == Component && nextF == Component:
			err = pushChildToLastChild(currentSegment, NewSimpleField(value))
		case last == Component && nextF == segment:
			err = pushChildToLastChild(currentSegment, NewSimpleField(value))
			//add current segment to the message
			mssg.Push(currentSegment)
			//create new segment
			currentSegment = NewComplexField(segment, SegmentValidator)

		case last == segment && nextF == segment:
			//special case for CR+EndOfData
			//noop
		default:
			err = ErrUnexpectedCase
		}

		if err != nil {
			return nil, err
		}

		last = nextF

	}

	return mssg, nil
}

func next(source []byte, enc *Encoding) (FieldType, int, error) {

	l, k := len(source), 0

	for ; k < l; k++ {

		v := source[k]
		switch v {
		case enc.Field:
			return Simple, k, nil
		case enc.Component:
			return Component, k, nil
		case enc.Repeated:
			return Component, k, nil
		case enc.Component:
			return SubComponent, k, nil
		case CR:
			return segment, k, nil
		case enc.Escaping:
			//continue
			k++
		}
	}

	return 0, k + 1, errNoMoreData
}

//Given a complex field append a new child to its last child
//in other word: add a grandchild
//in other words: push child to last child
func pushChildToLastChild(parent *ComplexField, newChild Field) error {

	lastField, err := parent.Pop()
	if err != nil {
		return err
	}

	//converting last field back to *ComplexField
	complexF, ok := lastField.(*ComplexField)
	if !ok {
		return ErrUnexpectedCase
	}

	//push new child
	err = complexF.Push(newChild)
	if err != nil {
		return err
	}

	//push back the last complex field
	return parent.Push(complexF)
}
<|endoftext|>


<commit_before>// go-holidayjp is a package for detecting holiday in Japan.
package holidayjp

import (
	"time"

	"github.com/tcnksm/go-holidayjp/asset"
	"gopkg.in/yaml.v2"
)

//go:generate go-bindata -pkg=asset -o asset/asset.go ./asset/holidays.yml

// Holiday returns Holiday name of the given time.
// If it's not holiday, returns error.
func holiday(t time.Time) (string, error) {
	// TODO
	return "", nil
}

// IsHoliday reports whether the time is holiday.
func IsHoliday(target time.Time) bool {
	for dateStr, _ := range holidays() {
		date, _ := time.Parse("2006-01-02", dateStr)
		if equal(target, date) {
			return true
		}
	}
	return false
}

func equal(t0, t1 time.Time) bool {
	return t0.Year() == t1.Year() && t0.Month() == t1.Month() && t0.Day() == t1.Day()
}

func holidays() map[string]string {
	var s map[string]string
	buf, _ := asset.Asset("holidays.yml")
	yaml.Unmarshal(buf, &s)
	return s
}
<commit_msg>Fix read asset path<commit_after>// go-holidayjp is a package for detecting holiday in Japan.
package holidayjp

import (
	"time"

	"github.com/tcnksm/go-holidayjp/asset"
	"gopkg.in/yaml.v2"
)

//go:generate go-bindata -pkg=asset -o asset/asset.go ./asset/holidays.yml

// Holiday returns Holiday name of the given time.
// If it's not holiday, returns error.
func holiday(t time.Time) (string, error) {
	// TODO
	return "", nil
}

// IsHoliday reports whether the time is holiday.
func IsHoliday(target time.Time) bool {
	for dateStr, _ := range holidays() {
		date, _ := time.Parse("2006-01-02", dateStr)
		if equal(target, date) {
			return true
		}
	}
	return false
}

func equal(t0, t1 time.Time) bool {
	return t0.Year() == t1.Year() && t0.Month() == t1.Month() && t0.Day() == t1.Day()
}

func holidays() map[string]string {
	var s map[string]string
	buf, _ := asset.Asset("asset/holidays.yml")
	yaml.Unmarshal(buf, &s)
	return s
}
<|endoftext|>


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

// Package home implements a detecting the user home directory.
package home

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/user"
	"runtime"
	"strings"
)

var usrHome = os.Getenv("HOME")
var usr = &user.User{}

// TODO(zchee): user.Current() use of cgo compile in the Go stdlib internal.
// Support cross-platform compiling without the "os/user" package if possible. Or make to optional with the build tag.
func init() {
	cUser, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	usr = cUser
}

// Dir detects and returns the user home directory.
func Dir() string {
	// At first, Check the $HOME environment variable
	if usrHome != "" {
		return usrHome
	}

	// TODO(zchee): In Windows OS, which of $HOME and these checks has priority?
	if runtime.GOOS == "windows" {
		// Respect the USERPROFILE environment variable because Go stdlib uses it for default GOPATH in the "go/build" package.
		if usrHome = os.Getenv("USERPROFILE"); usrHome == "" {
			if usrHome = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH"); usrHome == "" {
				userProfile, err := echoUserProfile()
				if err != nil {
					log.Fatalf("home: %v", err)
				}
				usrHome = userProfile
			}
		}

		if usrHome != "" {
			return usrHome
		}
	}

	usrHome = usr.HomeDir

	return usrHome
}

func echoUserProfile() (string, error) {
	var stdout bytes.Buffer
	cmd := exec.Command("echo", "%USERPROFILE%")
	cmd.Stdout = &stdout
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Couldn't get the %%USERPROFILE%% environment variable: %v", err)
	}

	return strings.TrimSpace(stdout.String()), nil
}
<commit_msg>home: remove unnecesarry echoUserProfile<commit_after>// Copyright 2017 The go-xdgbasedir Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package home implements a detecting the user home directory.
package home

import (
	"log"
	"os"
	"os/user"
	"runtime"
)

var usrHome = os.Getenv("HOME")
var usr = &user.User{}

// TODO(zchee): user.Current() use of cgo compile in the Go stdlib internal.
// Support cross-platform compiling without the "os/user" package if possible. Or make to optional with the build tag.
func init() {
	cUser, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	usr = cUser
}

// Dir detects and returns the user home directory.
func Dir() string {
	// At first, Check the $HOME environment variable
	if usrHome != "" {
		return usrHome
	}

	// TODO(zchee): In Windows OS, which of $HOME and these checks has priority?
	if runtime.GOOS == "windows" {
		// Respect the USERPROFILE environment variable because Go stdlib uses it for default GOPATH in the "go/build" package.
		if usrHome = os.Getenv("USERPROFILE"); usrHome == "" {
			usrHome = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		}

		if usrHome != "" {
			return usrHome
		}
	}

	usrHome = usr.HomeDir

	return usrHome
}
<|endoftext|>


<commit_before>package hook

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Constants used to specify the parameter source
const (
	SourceHeader        string = "header"
	SourceQuery         string = "url"
	SourcePayload       string = "payload"
	SourceString        string = "string"
	SourceEntirePayload string = "entire-payload"
)

// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, bool) {
	if strings.HasPrefix(signature, "sha1=") {
		signature = signature[5:]
	}

	mac := hmac.New(sha1.New, []byte(secret))
	mac.Write(payload)
	expectedMAC := hex.EncodeToString(mac.Sum(nil))

	return expectedMAC, hmac.Equal([]byte(signature), []byte(expectedMAC))
}

// ReplaceParameter replaces parameter value with the passed value in the passed map
// (please note you should pass pointer to the map, because we're modifying it)
// based on the passed string
func ReplaceParameter(s string, params interface{}, value interface{}) bool {
	if params == nil {
		return false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return false
				}

				return ReplaceParameter(p[1], params.([]interface{})[index], value)
			}
		}

		return false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return ReplaceParameter(p[1], pValue, value)
		}
	} else {
		if _, ok := (*params.(*map[string]interface{}))[p[0]]; ok {
			(*params.(*map[string]interface{}))[p[0]] = value
			return true
		}
	}

	return false
}

// GetParameter extracts interface{} value based on the passed string
func GetParameter(s string, params interface{}) (interface{}, bool) {
	if params == nil {
		return nil, false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return nil, false
				}

				return GetParameter(p[1], params.([]interface{})[index])
			}

			index, err := strconv.ParseUint(s, 10, 64)

			if err != nil || paramsValueSliceLength <= int(index) {
				return nil, false
			}

			return params.([]interface{})[index], true
		}

		return nil, false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Map {
			if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
				return GetParameter(p[1], pValue)
			}
		} else {
			return nil, false
		}
	} else {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return pValue, true
		}
	}

	return nil, false
}

// ExtractParameterAsString extracts value from interface{} as string based on the passed string
func ExtractParameterAsString(s string, params interface{}) (string, bool) {
	if pValue, ok := GetParameter(s, params); ok {
		return fmt.Sprintf("%v", pValue), true
	}
	return "", false
}

// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
	Source string `json:"source"`
	Name   string `json:"name"`
}

// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
	var source *map[string]interface{}

	switch ha.Source {
	case SourceHeader:
		source = headers
	case SourceQuery:
		source = query
	case SourcePayload:
		source = payload
	case SourceString:
		return ha.Name, true
	case SourceEntirePayload:
		r, err := json.Marshal(payload)

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

		return string(r), true
	}

	if source != nil {
		return ExtractParameterAsString(ha.Name, *source)
	}

	return "", false
}

// Hook type is a structure containing details for a single hook
type Hook struct {
	ID                      string     `json:"id"`
	ExecuteCommand          string     `json:"execute-command"`
	CommandWorkingDirectory string     `json:"command-working-directory"`
	ResponseMessage         string     `json:"response-message"`
	CaptureCommandOutput    bool       `json:"include-command-output-in-response"`
	PassArgumentsToCommand  []Argument `json:"pass-arguments-to-command"`
	JSONStringParameters    []Argument `json:"parse-parameters-as-json"`
	TriggerRule             *Rules     `json:"trigger-rule"`
}

// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
// string with the newly created object
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) {
	for i := range h.JSONStringParameters {
		if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
			var newArg map[string]interface{}

			decoder := json.NewDecoder(strings.NewReader(string(arg)))
			decoder.UseNumber()

			err := decoder.Decode(&newArg)

			if err != nil {
				log.Printf("error parsing argument as JSON payload %+v\n", err)
			} else {
				var source *map[string]interface{}

				switch h.JSONStringParameters[i].Source {
				case SourceHeader:
					source = headers
				case SourcePayload:
					source = payload
				case SourceQuery:
					source = query
				}

				if source != nil {
					ReplaceParameter(h.JSONStringParameters[i].Name, source, newArg)
				} else {
					log.Printf("invalid source for argument %+v\n", h.JSONStringParameters[i])
				}
			}
		} else {
			log.Printf("couldn't retrieve argument for %+v\n", h.JSONStringParameters[i])
		}
	}
}

// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) []string {
	var args = make([]string, 0)

	args = append(args, h.ExecuteCommand)

	for i := range h.PassArgumentsToCommand {
		if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
			args = append(args, arg)
		} else {
			args = append(args, "")
			log.Printf("couldn't retrieve argument for %+v\n", h.PassArgumentsToCommand[i])
		}
	}

	return args
}

// Hooks is an array of Hook objects
type Hooks []Hook

// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
	if path == "" {
		return nil
	}

	// parse hook file for hooks
	file, e := ioutil.ReadFile(path)

	if e != nil {
		return e
	}

	e = json.Unmarshal(file, h)
	return e
}

// Match iterates through Hooks and returns first one that matches the given ID,
// if no hook matches the given ID, nil is returned
func (h *Hooks) Match(id string) *Hook {
	for i := range *h {
		if (*h)[i].ID == id {
			return &(*h)[i]
		}
	}

	return nil
}

// MatchAll iterates through Hooks and returns all of the hooks that match the
// given ID, if no hook matches the given ID, nil is returned
func (h *Hooks) MatchAll(id string) []*Hook {
	matchedHooks := make([]*Hook, 0)
	for i := range *h {
		if (*h)[i].ID == id {
			matchedHooks = append(matchedHooks, &(*h)[i])
		}
	}

	if len(matchedHooks) > 0 {
		return matchedHooks
	}

	return nil
}

// Rules is a structure that contains one of the valid rule types
type Rules struct {
	And   *AndRule   `json:"and"`
	Or    *OrRule    `json:"or"`
	Not   *NotRule   `json:"not"`
	Match *MatchRule `json:"match"`
}

// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	switch {
	case r.And != nil:
		return r.And.Evaluate(headers, query, payload, body)
	case r.Or != nil:
		return r.Or.Evaluate(headers, query, payload, body)
	case r.Not != nil:
		return r.Not.Evaluate(headers, query, payload, body)
	case r.Match != nil:
		return r.Match.Evaluate(headers, query, payload, body)
	}

	return false
}

// AndRule will evaluate to true if and only if all of the ChildRules evaluate to true
type AndRule []Rules

// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := true

	for _, v := range r {
		res = res && v.Evaluate(headers, query, payload, body)
		if res == false {
			return res
		}
	}

	return res
}

// OrRule will evaluate to true if any of the ChildRules evaluate to true
type OrRule []Rules

// Evaluate OrRule will return true if any of ChildRules evaluate to true
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := false

	for _, v := range r {
		res = res || v.Evaluate(headers, query, payload, body)
		if res == true {
			return res
		}
	}

	return res
}

// NotRule will evaluate to true if any and only if the ChildRule evaluates to false
type NotRule Rules

// Evaluate NotRule will return true if and only if ChildRule evaluates to false
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	return !Rules(r).Evaluate(headers, query, payload, body)
}

// MatchRule will evaluate to true based on the type
type MatchRule struct {
	Type      string   `json:"type"`
	Regex     string   `json:"regex"`
	Secret    string   `json:"secret"`
	Value     string   `json:"value"`
	Parameter Argument `json:"parameter"`
}

// Constants for the MatchRule type
const (
	MatchValue    string = "value"
	MatchRegex    string = "regex"
	MatchHashSHA1 string = "payload-hash-sha1"
)

// Evaluate MatchRule will return based on the type
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	if arg, ok := r.Parameter.Get(headers, query, payload); ok {
		switch r.Type {
		case MatchValue:
			return arg == r.Value
		case MatchRegex:
			ok, err := regexp.MatchString(r.Regex, arg)
			if err != nil {
				log.Printf("error while trying to evaluate regex: %+v", err)
			}
			return ok
		case MatchHashSHA1:
			expected, ok := CheckPayloadSignature(*body, r.Secret, arg)
			if !ok {
				log.Printf("payload signature mismatch, expected %s got %s", expected, arg)
			}

			return ok
		}
	} else {
		log.Printf("couldn't retrieve argument for %+v\n", r.Parameter)
	}
	return false
}

// CommandStatusResponse type encapsulates the executed command exit code, message, stdout and stderr
type CommandStatusResponse struct {
	ResponseMessage string `json:"message"`
	Output          string `json:"output"`
	Error           string `json:"error"`
}
<commit_msg>add entire query and headers as well<commit_after>package hook

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Constants used to specify the parameter source
const (
	SourceHeader        string = "header"
	SourceQuery         string = "url"
	SourcePayload       string = "payload"
	SourceString        string = "string"
	SourceEntirePayload string = "entire-payload"
	SourceEntireQuery   string = "entire-query"
	SourceEntireHeaders string = "entire-headers"
)

// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, bool) {
	if strings.HasPrefix(signature, "sha1=") {
		signature = signature[5:]
	}

	mac := hmac.New(sha1.New, []byte(secret))
	mac.Write(payload)
	expectedMAC := hex.EncodeToString(mac.Sum(nil))

	return expectedMAC, hmac.Equal([]byte(signature), []byte(expectedMAC))
}

// ReplaceParameter replaces parameter value with the passed value in the passed map
// (please note you should pass pointer to the map, because we're modifying it)
// based on the passed string
func ReplaceParameter(s string, params interface{}, value interface{}) bool {
	if params == nil {
		return false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return false
				}

				return ReplaceParameter(p[1], params.([]interface{})[index], value)
			}
		}

		return false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return ReplaceParameter(p[1], pValue, value)
		}
	} else {
		if _, ok := (*params.(*map[string]interface{}))[p[0]]; ok {
			(*params.(*map[string]interface{}))[p[0]] = value
			return true
		}
	}

	return false
}

// GetParameter extracts interface{} value based on the passed string
func GetParameter(s string, params interface{}) (interface{}, bool) {
	if params == nil {
		return nil, false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return nil, false
				}

				return GetParameter(p[1], params.([]interface{})[index])
			}

			index, err := strconv.ParseUint(s, 10, 64)

			if err != nil || paramsValueSliceLength <= int(index) {
				return nil, false
			}

			return params.([]interface{})[index], true
		}

		return nil, false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Map {
			if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
				return GetParameter(p[1], pValue)
			}
		} else {
			return nil, false
		}
	} else {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return pValue, true
		}
	}

	return nil, false
}

// ExtractParameterAsString extracts value from interface{} as string based on the passed string
func ExtractParameterAsString(s string, params interface{}) (string, bool) {
	if pValue, ok := GetParameter(s, params); ok {
		return fmt.Sprintf("%v", pValue), true
	}
	return "", false
}

// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
	Source string `json:"source"`
	Name   string `json:"name"`
}

// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
	var source *map[string]interface{}

	switch ha.Source {
	case SourceHeader:
		source = headers
	case SourceQuery:
		source = query
	case SourcePayload:
		source = payload
	case SourceString:
		return ha.Name, true
	case SourceEntirePayload:
		r, err := json.Marshal(payload)

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

		return string(r), true
	case SourceEntireHeaders:
		r, err := json.Marshal(headers)

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

		return string(r), true
	case SourceEntireQuery:
		r, err := json.Marshal(query)

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

		return string(r), true
	}

	if source != nil {
		return ExtractParameterAsString(ha.Name, *source)
	}

	return "", false
}

// Hook type is a structure containing details for a single hook
type Hook struct {
	ID                      string     `json:"id"`
	ExecuteCommand          string     `json:"execute-command"`
	CommandWorkingDirectory string     `json:"command-working-directory"`
	ResponseMessage         string     `json:"response-message"`
	CaptureCommandOutput    bool       `json:"include-command-output-in-response"`
	PassArgumentsToCommand  []Argument `json:"pass-arguments-to-command"`
	JSONStringParameters    []Argument `json:"parse-parameters-as-json"`
	TriggerRule             *Rules     `json:"trigger-rule"`
}

// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
// string with the newly created object
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) {
	for i := range h.JSONStringParameters {
		if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
			var newArg map[string]interface{}

			decoder := json.NewDecoder(strings.NewReader(string(arg)))
			decoder.UseNumber()

			err := decoder.Decode(&newArg)

			if err != nil {
				log.Printf("error parsing argument as JSON payload %+v\n", err)
			} else {
				var source *map[string]interface{}

				switch h.JSONStringParameters[i].Source {
				case SourceHeader:
					source = headers
				case SourcePayload:
					source = payload
				case SourceQuery:
					source = query
				}

				if source != nil {
					ReplaceParameter(h.JSONStringParameters[i].Name, source, newArg)
				} else {
					log.Printf("invalid source for argument %+v\n", h.JSONStringParameters[i])
				}
			}
		} else {
			log.Printf("couldn't retrieve argument for %+v\n", h.JSONStringParameters[i])
		}
	}
}

// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) []string {
	var args = make([]string, 0)

	args = append(args, h.ExecuteCommand)

	for i := range h.PassArgumentsToCommand {
		if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
			args = append(args, arg)
		} else {
			args = append(args, "")
			log.Printf("couldn't retrieve argument for %+v\n", h.PassArgumentsToCommand[i])
		}
	}

	return args
}

// Hooks is an array of Hook objects
type Hooks []Hook

// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
	if path == "" {
		return nil
	}

	// parse hook file for hooks
	file, e := ioutil.ReadFile(path)

	if e != nil {
		return e
	}

	e = json.Unmarshal(file, h)
	return e
}

// Match iterates through Hooks and returns first one that matches the given ID,
// if no hook matches the given ID, nil is returned
func (h *Hooks) Match(id string) *Hook {
	for i := range *h {
		if (*h)[i].ID == id {
			return &(*h)[i]
		}
	}

	return nil
}

// MatchAll iterates through Hooks and returns all of the hooks that match the
// given ID, if no hook matches the given ID, nil is returned
func (h *Hooks) MatchAll(id string) []*Hook {
	matchedHooks := make([]*Hook, 0)
	for i := range *h {
		if (*h)[i].ID == id {
			matchedHooks = append(matchedHooks, &(*h)[i])
		}
	}

	if len(matchedHooks) > 0 {
		return matchedHooks
	}

	return nil
}

// Rules is a structure that contains one of the valid rule types
type Rules struct {
	And   *AndRule   `json:"and"`
	Or    *OrRule    `json:"or"`
	Not   *NotRule   `json:"not"`
	Match *MatchRule `json:"match"`
}

// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	switch {
	case r.And != nil:
		return r.And.Evaluate(headers, query, payload, body)
	case r.Or != nil:
		return r.Or.Evaluate(headers, query, payload, body)
	case r.Not != nil:
		return r.Not.Evaluate(headers, query, payload, body)
	case r.Match != nil:
		return r.Match.Evaluate(headers, query, payload, body)
	}

	return false
}

// AndRule will evaluate to true if and only if all of the ChildRules evaluate to true
type AndRule []Rules

// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := true

	for _, v := range r {
		res = res && v.Evaluate(headers, query, payload, body)
		if res == false {
			return res
		}
	}

	return res
}

// OrRule will evaluate to true if any of the ChildRules evaluate to true
type OrRule []Rules

// Evaluate OrRule will return true if any of ChildRules evaluate to true
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := false

	for _, v := range r {
		res = res || v.Evaluate(headers, query, payload, body)
		if res == true {
			return res
		}
	}

	return res
}

// NotRule will evaluate to true if any and only if the ChildRule evaluates to false
type NotRule Rules

// Evaluate NotRule will return true if and only if ChildRule evaluates to false
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	return !Rules(r).Evaluate(headers, query, payload, body)
}

// MatchRule will evaluate to true based on the type
type MatchRule struct {
	Type      string   `json:"type"`
	Regex     string   `json:"regex"`
	Secret    string   `json:"secret"`
	Value     string   `json:"value"`
	Parameter Argument `json:"parameter"`
}

// Constants for the MatchRule type
const (
	MatchValue    string = "value"
	MatchRegex    string = "regex"
	MatchHashSHA1 string = "payload-hash-sha1"
)

// Evaluate MatchRule will return based on the type
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	if arg, ok := r.Parameter.Get(headers, query, payload); ok {
		switch r.Type {
		case MatchValue:
			return arg == r.Value
		case MatchRegex:
			ok, err := regexp.MatchString(r.Regex, arg)
			if err != nil {
				log.Printf("error while trying to evaluate regex: %+v", err)
			}
			return ok
		case MatchHashSHA1:
			expected, ok := CheckPayloadSignature(*body, r.Secret, arg)
			if !ok {
				log.Printf("payload signature mismatch, expected %s got %s", expected, arg)
			}

			return ok
		}
	} else {
		log.Printf("couldn't retrieve argument for %+v\n", r.Parameter)
	}
	return false
}

// CommandStatusResponse type encapsulates the executed command exit code, message, stdout and stderr
type CommandStatusResponse struct {
	ResponseMessage string `json:"message"`
	Output          string `json:"output"`
	Error           string `json:"error"`
}
<|endoftext|>


<commit_before>package hook

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Constants used to specify the parameter source
const (
	SourceHeader  string = "header"
	SourceQuery   string = "url"
	SourcePayload string = "payload"
)

// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, bool) {
	if strings.HasPrefix(signature, "sha1=") {
		signature = signature[5:]
	}

	mac := hmac.New(sha1.New, []byte(secret))
	mac.Write(payload)
	expectedMAC := hex.EncodeToString(mac.Sum(nil))

	return expectedMAC, hmac.Equal([]byte(signature), []byte(expectedMAC))
}

// ExtractParameter extracts value from interface{} based on the passed string
func ExtractParameter(s string, params interface{}) (string, bool) {
	if params == nil {
		return "", false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseInt(p[0], 10, 64)

				if err != nil {
					return "", false
				} else if paramsValueSliceLength <= int(index) {
					return "", false
				}

				return ExtractParameter(p[1], params.([]interface{})[index])
			}
		}

		return "", false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return ExtractParameter(p[1], pValue)
		}
	} else {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return fmt.Sprintf("%v", pValue), true
		}
	}

	return "", false
}

// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
	Source string `json:"source"`
	Name   string `json:"name"`
}

// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
	var source *map[string]interface{}

	switch ha.Source {
	case SourceHeader:
		source = headers
	case SourceQuery:
		source = query
	case SourcePayload:
		source = payload
	}

	if source != nil {
		return ExtractParameter(ha.Name, *source)
	}

	return "", false
}

// Hook type is a structure containing details for a single hook
type Hook struct {
	ID                      string     `json:"id"`
	ExecuteCommand          string     `json:"execute-command"`
	CommandWorkingDirectory string     `json:"command-working-directory"`
	ResponseMessage         string     `json:"response-message"`
	PassArgumentsToCommand  []Argument `json:"pass-arguments-to-command"`
	TriggerRule             *Rules     `json:"trigger-rule"`
}

// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) []string {
	var args = make([]string, 0)

	args = append(args, h.ExecuteCommand)

	for i := range h.PassArgumentsToCommand {
		if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
			args = append(args, arg)
		} else {
			args = append(args, "")
			log.Printf("couldn't retrieve argument for %+v\n", h.PassArgumentsToCommand[i])
		}
	}

	return args
}

// Hooks is an array of Hook objects
type Hooks []Hook

// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
	if path == "" {
		return nil
	}

	// parse hook file for hooks
	file, e := ioutil.ReadFile(path)

	if e != nil {
		return e
	}

	e = json.Unmarshal(file, h)
	return e
}

// Match iterates through Hooks and returns first one that matches the given ID,
// if no hook matches the given ID, nil is returned
func (h *Hooks) Match(id string) *Hook {
	for i := range *h {
		if (*h)[i].ID == id {
			return &(*h)[i]
		}
	}

	return nil
}

// Rules is a structure that contains one of the valid rule types
type Rules struct {
	And   *AndRule   `json:"and"`
	Or    *OrRule    `json:"or"`
	Not   *NotRule   `json:"not"`
	Match *MatchRule `json:"match"`
}

// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	switch {
	case r.And != nil:
		return r.And.Evaluate(headers, query, payload, body)
	case r.Or != nil:
		return r.Or.Evaluate(headers, query, payload, body)
	case r.Not != nil:
		return r.Not.Evaluate(headers, query, payload, body)
	case r.Match != nil:
		return r.Match.Evaluate(headers, query, payload, body)
	}

	return false
}

// AndRule will evaluate to true if and only if all of the ChildRules evaluate to true
type AndRule []Rules

// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := true

	for _, v := range r {
		res = res && v.Evaluate(headers, query, payload, body)
		if res == false {
			return res
		}
	}

	return res
}

// OrRule will evaluate to true if any of the ChildRules evaluate to true
type OrRule []Rules

// Evaluate OrRule will return true if any of ChildRules evaluate to true
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := false

	for _, v := range r {
		res = res || v.Evaluate(headers, query, payload, body)
		if res == true {
			return res
		}
	}

	return res
}

// NotRule will evaluate to true if any and only if the ChildRule evaluates to false
type NotRule Rules

// Evaluate NotRule will return true if and only if ChildRule evaluates to false
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	return !r.Evaluate(headers, query, payload, body)
}

// MatchRule will evaluate to true based on the type
type MatchRule struct {
	Type      string   `json:"type"`
	Regex     string   `json:"regex"`
	Secret    string   `json:"secret"`
	Value     string   `json:"value"`
	Parameter Argument `json:"parameter"`
}

// Constants for the MatchRule type
const (
	MatchValue    string = "value"
	MatchRegex    string = "regex"
	MatchHashSHA1 string = "payload-hash-sha1"
)

// Evaluate MatchRule will return based on the type
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	if arg, ok := r.Parameter.Get(headers, query, payload); ok {
		switch r.Type {
		case MatchValue:
			return arg == r.Value
		case MatchRegex:
			ok, err := regexp.MatchString(r.Regex, arg)
			if err != nil {
				log.Printf("error while trying to evaluate regex: %+v", err)
			}
			return ok
		case MatchHashSHA1:
			expected, ok := CheckPayloadSignature(*body, r.Secret, arg)
			if !ok {
				log.Printf("payload signature mismatch, expected %s got %s", expected, arg)
			}

			return ok
		}
	} else {
		log.Printf("couldn't retrieve argument for %+v\n", r.Parameter)
	}
	return false
}
<commit_msg>Fix infinite loop in NotRule.Evaluate<commit_after>package hook

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Constants used to specify the parameter source
const (
	SourceHeader  string = "header"
	SourceQuery   string = "url"
	SourcePayload string = "payload"
)

// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, bool) {
	if strings.HasPrefix(signature, "sha1=") {
		signature = signature[5:]
	}

	mac := hmac.New(sha1.New, []byte(secret))
	mac.Write(payload)
	expectedMAC := hex.EncodeToString(mac.Sum(nil))

	return expectedMAC, hmac.Equal([]byte(signature), []byte(expectedMAC))
}

// ExtractParameter extracts value from interface{} based on the passed string
func ExtractParameter(s string, params interface{}) (string, bool) {
	if params == nil {
		return "", false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseInt(p[0], 10, 64)

				if err != nil {
					return "", false
				} else if paramsValueSliceLength <= int(index) {
					return "", false
				}

				return ExtractParameter(p[1], params.([]interface{})[index])
			}
		}

		return "", false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return ExtractParameter(p[1], pValue)
		}
	} else {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return fmt.Sprintf("%v", pValue), true
		}
	}

	return "", false
}

// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
	Source string `json:"source"`
	Name   string `json:"name"`
}

// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
	var source *map[string]interface{}

	switch ha.Source {
	case SourceHeader:
		source = headers
	case SourceQuery:
		source = query
	case SourcePayload:
		source = payload
	}

	if source != nil {
		return ExtractParameter(ha.Name, *source)
	}

	return "", false
}

// Hook type is a structure containing details for a single hook
type Hook struct {
	ID                      string     `json:"id"`
	ExecuteCommand          string     `json:"execute-command"`
	CommandWorkingDirectory string     `json:"command-working-directory"`
	ResponseMessage         string     `json:"response-message"`
	PassArgumentsToCommand  []Argument `json:"pass-arguments-to-command"`
	TriggerRule             *Rules     `json:"trigger-rule"`
}

// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) []string {
	var args = make([]string, 0)

	args = append(args, h.ExecuteCommand)

	for i := range h.PassArgumentsToCommand {
		if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
			args = append(args, arg)
		} else {
			args = append(args, "")
			log.Printf("couldn't retrieve argument for %+v\n", h.PassArgumentsToCommand[i])
		}
	}

	return args
}

// Hooks is an array of Hook objects
type Hooks []Hook

// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
	if path == "" {
		return nil
	}

	// parse hook file for hooks
	file, e := ioutil.ReadFile(path)

	if e != nil {
		return e
	}

	e = json.Unmarshal(file, h)
	return e
}

// Match iterates through Hooks and returns first one that matches the given ID,
// if no hook matches the given ID, nil is returned
func (h *Hooks) Match(id string) *Hook {
	for i := range *h {
		if (*h)[i].ID == id {
			return &(*h)[i]
		}
	}

	return nil
}

// Rules is a structure that contains one of the valid rule types
type Rules struct {
	And   *AndRule   `json:"and"`
	Or    *OrRule    `json:"or"`
	Not   *NotRule   `json:"not"`
	Match *MatchRule `json:"match"`
}

// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	switch {
	case r.And != nil:
		return r.And.Evaluate(headers, query, payload, body)
	case r.Or != nil:
		return r.Or.Evaluate(headers, query, payload, body)
	case r.Not != nil:
		return r.Not.Evaluate(headers, query, payload, body)
	case r.Match != nil:
		return r.Match.Evaluate(headers, query, payload, body)
	}

	return false
}

// AndRule will evaluate to true if and only if all of the ChildRules evaluate to true
type AndRule []Rules

// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := true

	for _, v := range r {
		res = res && v.Evaluate(headers, query, payload, body)
		if res == false {
			return res
		}
	}

	return res
}

// OrRule will evaluate to true if any of the ChildRules evaluate to true
type OrRule []Rules

// Evaluate OrRule will return true if any of ChildRules evaluate to true
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	res := false

	for _, v := range r {
		res = res || v.Evaluate(headers, query, payload, body)
		if res == true {
			return res
		}
	}

	return res
}

// NotRule will evaluate to true if any and only if the ChildRule evaluates to false
type NotRule Rules

// Evaluate NotRule will return true if and only if ChildRule evaluates to false
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	return !Rules(r).Evaluate(headers, query, payload, body)
}

// MatchRule will evaluate to true based on the type
type MatchRule struct {
	Type      string   `json:"type"`
	Regex     string   `json:"regex"`
	Secret    string   `json:"secret"`
	Value     string   `json:"value"`
	Parameter Argument `json:"parameter"`
}

// Constants for the MatchRule type
const (
	MatchValue    string = "value"
	MatchRegex    string = "regex"
	MatchHashSHA1 string = "payload-hash-sha1"
)

// Evaluate MatchRule will return based on the type
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) bool {
	if arg, ok := r.Parameter.Get(headers, query, payload); ok {
		switch r.Type {
		case MatchValue:
			return arg == r.Value
		case MatchRegex:
			ok, err := regexp.MatchString(r.Regex, arg)
			if err != nil {
				log.Printf("error while trying to evaluate regex: %+v", err)
			}
			return ok
		case MatchHashSHA1:
			expected, ok := CheckPayloadSignature(*body, r.Secret, arg)
			if !ok {
				log.Printf("payload signature mismatch, expected %s got %s", expected, arg)
			}

			return ok
		}
	} else {
		log.Printf("couldn't retrieve argument for %+v\n", r.Parameter)
	}
	return false
}
<|endoftext|>