<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"errors"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	uuid "github.com/satori/go.uuid"
)

type client struct {
	tcpServerAddr   string
	serverHost      string
	httpClient      *http.Client
	cfg             config
	keepAlivePeriod time.Duration
	dialTimeout     time.Duration
	wg              sync.WaitGroup
	errChan         chan error
	signal          chan os.Signal
	done            chan struct{}
}

func newClient(cfg config, sigChan chan os.Signal) *client {
	tr := &http.Transport{
		MaxIdleConns:    10,
		IdleConnTimeout: 30 * time.Second,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: cfg.InsecureSkipVerify,
		},
	}
	hc := &http.Client{Transport: tr}
	return &client{
		cfg:             cfg,
		httpClient:      hc,
		keepAlivePeriod: time.Duration(cfg.KeepAlivePeriod) * time.Second,
		dialTimeout:     time.Duration(cfg.DialTimeout) * time.Second,
		errChan:         make(chan error, 1),
		signal:          sigChan,
		done:            make(chan struct{}),
	}
}

func (c *client) proxyClientConn(conn, rConn net.Conn, ch chan struct{}) {
	defer c.wg.Done()
	defer close(ch)
	var wg sync.WaitGroup
	connCopy := func(dst, src net.Conn) {
		defer wg.Done()
		_, err := io.Copy(dst, src)
		if err != nil {
			if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "readfrom") {
				log.Println("[ERR] gsocks5: Failed to copy connection from",
					src.RemoteAddr(), "to", conn.RemoteAddr(), ":", err)
			}
			return
		}
	}

	wg.Add(2)
	go connCopy(rConn, conn)
	go connCopy(conn, rConn)
	wg.Wait()
}

func (c *client) getConnID() (string, error) {
	endpoint := url.URL{
		Scheme: "https",
		Host:   c.serverHost,
		Path:   newSocksProxyEndpoint,
	}

	resp, err := c.httpClient.Get(endpoint.String())
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	cl := resp.Header.Get("Content-Length")
	l, err := strconv.Atoi(cl)
	if err != nil {
		return "", err
	}
	body := make([]byte, l)
	_, err = io.ReadFull(resp.Body, body)
	if err != nil {
		return "", err
	}
	connID, err := uuid.FromBytes(body)
	if err != nil {
		return "", err
	}

	return connID.String(), nil
}

func (c *client) write(connID string, b []byte) ([]byte, error) {
	endpoint := url.URL{
		Scheme:   "https",
		Host:     c.serverHost,
		Path:     writeSocksProxyEndpoint,
		RawQuery: "connID=" + connID,
	}
	buf := bytes.NewBuffer(b)
	resp, err := c.httpClient.Post(endpoint.String(), "application/octet-stream", buf)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		// TODO: return a convenient error message
		return nil, errors.New("something went wrong")
	}
	cl := resp.Header.Get("Content-Length")
	l, err := strconv.Atoi(cl)
	if err != nil {
		return nil, err
	}
	body := make([]byte, l)
	_, err = io.ReadFull(resp.Body, body)
	if err != nil {
		return nil, err
	}
	return body, nil
}

// copyBuffer is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func (c *client) socksOverHTTP(src net.Conn, connID string) error {
	buf := make([]byte, 32*1024)
	type result struct {
		nr  int
		err error
	}

	res := make(chan result, 1)
	rChan := func() chan result {
		nr, er := src.Read(buf)
		rr := result{nr: nr, err: er}
		res <- rr
		return res
	}

	for {
		select {
		case <-c.done:
			return errors.New("[ERR] gsocks5: Request cancelled")
		case res := <-rChan():
			if res.nr > 0 {
				data, ew := c.write(connID, buf[:res.nr])
				if ew != nil {
					return ew
				}
				_, ew = src.Write(data)
				if ew != nil {
					return ew
				}
				nr := len(data)
				if nr >= 6 && nr <= 22 && data[0] == socks5Version && data[1] == socksSuccess {
					return nil
				}
			}
			if res.err != nil {
				if res.err == io.EOF {
					return nil
				}
				return res.err
			}
		}
	}
	return nil
}

func (c *client) clientConn(conn net.Conn) {
	defer c.wg.Done()
	defer closeConn(conn)
	connID, err := c.getConnID()
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to create a new SOCKS5 proxy:", err)
		return
	}
	ch := make(chan struct{})
	if err := c.socksOverHTTP(conn, connID); err != nil {
		log.Println("[ERR] gsocks5: Failed to proxy SOCKS5 over HTTP", err)
		return
	}

	rConn, err := net.DialTimeout(c.cfg.Method, c.tcpServerAddr, c.dialTimeout)
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to dial", c.tcpServerAddr, err)
		return
	}
	defer closeConn(rConn)

	cID, err := uuid.FromString(connID)
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to process ConnID:", connID, err)
		return
	}

	_, err = rConn.Write(cID.Bytes())
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to send ConnID", c.tcpServerAddr, err)
		return
	}

	b := make([]byte, 1)
	_, err = rConn.Read(b)
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to read from raw socket", c.tcpServerAddr, err)
		return
	}

	c.wg.Add(1)
	go c.proxyClientConn(conn, rConn, ch)
	select {
	case <-c.done:
	case <-ch:
	}
}

func (c *client) serve(l net.Listener) {
	defer c.wg.Done()
	for {
		conn, err := l.Accept()
		if err != nil {
			// Shutdown the client immediately.
			c.shutdown()
			if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
				c.errChan <- err
				return
			}
			c.errChan <- nil
			return
		}

		// ASSOCIATE command has not been implemented by go-socks5. We currently support TCP but when someone
		// implements ASSOCIATE command, we will implement an UDP relay in gsocks5.
		if c.cfg.Method == "tcp" {
			conn.(*net.TCPConn).SetKeepAlive(true)
			conn.(*net.TCPConn).SetKeepAlivePeriod(c.keepAlivePeriod)
		}

		c.wg.Add(1)
		go c.clientConn(conn)
	}
}

func (c *client) shutdown() {
	select {
	case <-c.done:
		return
	default:
	}
	close(c.done)
}

func (c *client) run() error {
	var err error
	host, port := c.cfg.ClientHost, c.cfg.ClientPort

	addr := net.JoinHostPort(host, port)
	c.serverHost = net.JoinHostPort(c.cfg.ServerHost, c.cfg.ServerTLSPort)
	c.tcpServerAddr = net.JoinHostPort(c.cfg.ServerHost, c.cfg.ServerPort)

	rawListener, err := net.Listen(c.cfg.Method, addr)
	if err != nil {
		return err
	}

	log.Println("[INF] gsocks5: Proxy client runs on", addr)
	c.wg.Add(1)
	go c.serve(rawListener)

	select {
	// Wait for SIGINT or SIGTERM
	case <-c.signal:
	// Wait for a listener error
	case <-c.done:
	}

	// Signal all running goroutines to stop.
	c.shutdown()

	log.Println("[INF] gsocks5: Stopping proxy", addr)
	if err = rawListener.Close(); err != nil {
		log.Println("[ERR] gsocks5: Failed to close listener", err)
	}

	ch := make(chan struct{})
	go func() {
		defer close(ch)
		c.wg.Wait()
	}()

	select {
	case <-ch:
	case <-time.After(time.Duration(c.cfg.GracefulPeriod) * time.Second):
		log.Println("[WARN] Some goroutines will be stopped immediately")
	}

	err = <-c.errChan
	return err
}
<commit_msg>close remote connections properly<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"errors"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	uuid "github.com/satori/go.uuid"
)

type client struct {
	tcpServerAddr   string
	serverHost      string
	httpClient      *http.Client
	cfg             config
	keepAlivePeriod time.Duration
	dialTimeout     time.Duration
	wg              sync.WaitGroup
	errChan         chan error
	signal          chan os.Signal
	done            chan struct{}
}

func newClient(cfg config, sigChan chan os.Signal) *client {
	tr := &http.Transport{
		MaxIdleConns:    10,
		IdleConnTimeout: 30 * time.Second,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: cfg.InsecureSkipVerify,
		},
	}
	hc := &http.Client{Transport: tr}
	return &client{
		cfg:             cfg,
		httpClient:      hc,
		keepAlivePeriod: time.Duration(cfg.KeepAlivePeriod) * time.Second,
		dialTimeout:     time.Duration(cfg.DialTimeout) * time.Second,
		errChan:         make(chan error, 1),
		signal:          sigChan,
		done:            make(chan struct{}),
	}
}

// copyBuffer is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func (c *client) copyBuffer(dst io.Writer, src io.Reader, proxyDone chan struct{}) (written int64, err error) {
	buf := make([]byte, 32*1024)
	type result struct {
		nr  int
		err error
	}
	readRes := make(chan result, 1)
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()
		for {
			nr, er := src.Read(buf)
			rr := result{nr: nr, err: er}
			readRes <- rr
			if err != nil {
				return
			}
		}
	}()

	for {
		select {
		case <-proxyDone:
			// TODO: Return an error
			return 0, nil
		case res := <-readRes:
			if res.nr > 0 {
				nw, ew := dst.Write(buf[0:res.nr])
				if nw > 0 {
					written += int64(nw)
				}
				if ew != nil {
					err = ew
					return written, err
				}
				if res.nr != nw {
					err = io.ErrShortWrite
					return written, err
				}
			}
			if res.err != nil {
				if res.err != io.EOF {
					err = res.err
				}
				return written, err
			}
		}
	}
}

func (c *client) connCopy(dst, src net.Conn, copyDone chan struct{}, proxyDone chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	_, err := c.copyBuffer(dst, src, proxyDone)
	if err != nil {
		if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "readfrom") {
			log.Println("[ERR] gsocks5: Failed to copy connection from",
				src.RemoteAddr(), "to", dst.RemoteAddr(), ":", err)
		}
	}
	copyDone <- struct{}{}
}

func (c *client) proxyClientConn(conn, rConn net.Conn, ch chan struct{}) {
	defer c.wg.Done()
	defer close(ch)
	var wg sync.WaitGroup
	proxyDone := make(chan struct{})
	copyDone := make(chan struct{}, 2)

	wg.Add(2)
	go c.connCopy(rConn, conn, copyDone, proxyDone, &wg)
	go c.connCopy(conn, rConn, copyDone, proxyDone, &wg)

	<-copyDone
	close(proxyDone)
	wg.Wait()
}

func (c *client) getConnID() (string, error) {
	endpoint := url.URL{
		Scheme: "https",
		Host:   c.serverHost,
		Path:   newSocksProxyEndpoint,
	}

	resp, err := c.httpClient.Get(endpoint.String())
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	cl := resp.Header.Get("Content-Length")
	l, err := strconv.Atoi(cl)
	if err != nil {
		return "", err
	}
	body := make([]byte, l)
	_, err = io.ReadFull(resp.Body, body)
	if err != nil {
		return "", err
	}
	connID, err := uuid.FromBytes(body)
	if err != nil {
		return "", err
	}

	return connID.String(), nil
}

func (c *client) httpPost(connID string, b []byte) ([]byte, error) {
	endpoint := url.URL{
		Scheme:   "https",
		Host:     c.serverHost,
		Path:     writeSocksProxyEndpoint,
		RawQuery: "connID=" + connID,
	}
	buf := bytes.NewBuffer(b)
	resp, err := c.httpClient.Post(endpoint.String(), "application/octet-stream", buf)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		// TODO: return a convenient error message
		return nil, errors.New("something went wrong")
	}
	cl := resp.Header.Get("Content-Length")
	l, err := strconv.Atoi(cl)
	if err != nil {
		return nil, err
	}
	body := make([]byte, l)
	_, err = io.ReadFull(resp.Body, body)
	if err != nil {
		return nil, err
	}
	return body, nil
}

// copyBuffer is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func (c *client) socksOverHTTP(src net.Conn, connID string) error {
	buf := make([]byte, 32*1024)
	type result struct {
		nr  int
		err error
	}
	res := make(chan result, 1)
	rChan := func() chan result {
		nr, er := src.Read(buf)
		rr := result{nr: nr, err: er}
		res <- rr
		return res
	}

	for {
		select {
		case <-c.done:
			return errors.New("[ERR] gsocks5: Request cancelled")
		case res := <-rChan():
			if res.nr > 0 {
				data, ew := c.httpPost(connID, buf[:res.nr])
				if ew != nil {
					return ew
				}
				_, ew = src.Write(data)
				if ew != nil {
					return ew
				}
				nr := len(data)
				if nr >= 6 && nr <= 22 && data[0] == socks5Version && data[1] == socksSuccess {
					return nil
				}
			}
			if res.err != nil {
				if res.err == io.EOF {
					return nil
				}
				return res.err
			}
		}
	}
	return nil
}

func (c *client) clientConn(conn net.Conn) {
	defer c.wg.Done()
	defer closeConn(conn)
	connID, err := c.getConnID()
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to create a new SOCKS5 proxy:", err)
		return
	}

	if err := c.socksOverHTTP(conn, connID); err != nil {
		log.Println("[ERR] gsocks5: Failed to proxy SOCKS5 over HTTP", err)
		return
	}

	rConn, err := net.DialTimeout(c.cfg.Method, c.tcpServerAddr, c.dialTimeout)
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to dial", c.tcpServerAddr, err)
		return
	}
	defer closeConn(rConn)

	cID, err := uuid.FromString(connID)
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to process ConnID:", connID, err)
		return
	}

	_, err = rConn.Write(cID.Bytes())
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to send ConnID", c.tcpServerAddr, err)
		return
	}

	b := make([]byte, 1)
	_, err = rConn.Read(b)
	if err != nil {
		log.Println("[ERR] gsocks5: Failed to read from raw socket", c.tcpServerAddr, err)
		return
	}

	ch := make(chan struct{})
	c.wg.Add(1)
	go c.proxyClientConn(conn, rConn, ch)
	select {
	case <-c.done:
	case <-ch:
		log.Println("[DEBUG] gsocks5: Connection closed", connID)
	}
}

func (c *client) serve(l net.Listener) {
	defer c.wg.Done()
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Println("[ERR] gsocks5: Listener error:", err)
			// Shutdown the client immediately.
			c.shutdown()
			if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
				c.errChan <- err
				return
			}
			c.errChan <- nil
			return
		}

		// ASSOCIATE command has not been implemented by go-socks5. We currently support TCP but when someone
		// implements ASSOCIATE command, we will implement an UDP relay in gsocks5.
		if c.cfg.Method == "tcp" {
			conn.(*net.TCPConn).SetKeepAlive(true)
			conn.(*net.TCPConn).SetKeepAlivePeriod(c.keepAlivePeriod)
		}

		c.wg.Add(1)
		go c.clientConn(conn)
	}
}

func (c *client) shutdown() {
	select {
	case <-c.done:
		return
	default:
	}
	close(c.done)
}

func (c *client) run() error {
	var err error
	host, port := c.cfg.ClientHost, c.cfg.ClientPort

	addr := net.JoinHostPort(host, port)
	c.serverHost = net.JoinHostPort(c.cfg.ServerHost, c.cfg.ServerTLSPort)
	c.tcpServerAddr = net.JoinHostPort(c.cfg.ServerHost, c.cfg.ServerPort)

	rawListener, err := net.Listen(c.cfg.Method, addr)
	if err != nil {
		return err
	}

	log.Println("[INF] gsocks5: Proxy client runs on", addr)
	c.wg.Add(1)
	go c.serve(rawListener)

	select {
	// Wait for SIGINT or SIGTERM
	case <-c.signal:
	// Wait for a listener error
	case <-c.done:
	}

	// Signal all running goroutines to stop.
	c.shutdown()

	log.Println("[INF] gsocks5: Stopping proxy", addr)
	if err = rawListener.Close(); err != nil {
		log.Println("[ERR] gsocks5: Failed to close listener", err)
	}

	ch := make(chan struct{})
	go func() {
		defer close(ch)
		c.wg.Wait()
	}()

	select {
	case <-ch:
	case <-time.After(time.Duration(c.cfg.GracefulPeriod) * time.Second):
		log.Println("[WARN] Some goroutines will be stopped immediately")
	}

	err = <-c.errChan
	return err
}
<|endoftext|>


<commit_before><commit_msg>Create config.go<commit_after><|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"github.com/casimir/xdg-go"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/ini.v1"
	"os"
)

var (
	xdgapp = xdg.App{Name: "brightbox"}
)

type Config struct {
	App               *kingpin.Application
	defaultClientName string
	currentClient     *Client
	clients           map[string]Client
}

func NewConfig() (*Config, error) {
	c := new(Config)
	err := c.Setup()
	if err != nil {
		return nil, err
	}
	return c, nil
}

func (c *Config) SaveClientConfig(client *Client) error {
	if client == nil {
		panic("Can't save client config for nil client")
	}

	filename := xdgapp.ConfigPath("config")
	cfg, err := ini.Load(filename)
	if os.IsNotExist(err) {
		cfg = ini.Empty()
	} else if err != nil {
		return err
	}

	section := cfg.Section(client.ClientName)
	section.Key("client_id").SetValue(client.ClientID)
	section.Key("secret").SetValue(client.Secret)
	section.Key("api_url").SetValue(client.ApiUrl)
	section.Key("auth_url").SetValue(client.AuthUrl)
	section.Key("default_account").SetValue(client.DefaultAccount)
	section.Key("username").SetValue(client.Username)
	err = cfg.SaveTo(filename)
	if err != nil {
		return err
	}
	return nil
}

func (c *Config) Client(cname string) (*Client, error) {
	client, exists := c.clients[cname]
	if exists == false {
		return nil, fmt.Errorf("client '%s' not found in config.", cname)
	}
	return &client, nil
}

func (c *Config) CurrentClient() *Client {
	return c.currentClient
}

func (c *Config) DefaultClient() *Client {
	client, err := c.Client(c.defaultClientName)
	if err != nil && client == nil {
		return nil
	}
	return client
}

func (c *Config) Setup() error {
	err := os.MkdirAll(xdgapp.ConfigPath(""), 0750)
	if err != nil {
		return err
	}
	err = os.MkdirAll(xdgapp.CachePath(""), 0750)
	if err != nil {
		return err
	}
	if c.clients == nil {
		c.clients = make(map[string]Client)
	}
	err = c.Read()
	if err != nil {
		return err
	}
	return nil
}

func (c *Config) Read() error {
	filename := xdgapp.ConfigPath("config")
	cfg, err := ini.Load(filename)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return err
	}
	core := cfg.Section("core")
	c.defaultClientName = core.Key("default_client").String()
	for _, sec := range cfg.Sections() {
		if sec.Name() != "DEFAULT" && sec.Name() != "core" {
			cs := new(Client)
			cs.ClientName = sec.Name()
			if cs.ClientName == "" {
				continue
			}
			err = sec.MapTo(cs)
			if err != nil {
				continue
			}
			c.clients[cs.ClientName] = *cs
			if c.defaultClientName == "" {
				c.defaultClientName = cs.ClientName
			}

		}
	}
	return nil

}

func (c *Config) Write() error {
	filename := xdgapp.ConfigPath("config")
	cfg, err := ini.Load(filename)
	if os.IsNotExist(err) {
		cfg = ini.Empty()
	} else if err != nil {
		return err
	}

	section := cfg.Section("core")
	key := section.Key("default_client")
	key.SetValue(c.defaultClientName)
	err = cfg.SaveTo(filename)
	if err != nil {
		return err
	}
	return nil
}

func (c *Config) SetClient(clientName string) error {
	if clientName == "" {
		c.currentClient = c.DefaultClient()
		return nil
	}
	client, err := c.Client(clientName)
	if err == nil && client != nil {
		c.currentClient = client
		return nil
	} else {
		return fmt.Errorf("client '%s' not found in config.", clientName)
	}
}

type ConfigCommand struct {
	Id string
}

func (l *ConfigCommand) list(pc *kingpin.ParseContext) error {
	cfg, err := NewConfig()
	if err != nil {
		return err
	}
	w := tabWriter()
	defer w.Flush()
	listRec(w, "NAME", "CLIENTID", "SECRET", "API_URL", "AUTH_URL")
	dc := cfg.DefaultClient()
	for _, c := range cfg.clients {
		name := c.ClientName
		if dc != nil && dc.ClientName == name {
			name = "*" + name
		}
		listRec(w, name, c.ClientID, c.Secret,
			c.ApiUrl, c.findAuthUrl())
	}
	return nil
}

func (l *ConfigCommand) show(pc *kingpin.ParseContext) error {
	cfg, err := NewConfig()
	if err != nil {
		return err
	}
	w := tabWriterRight()
	defer w.Flush()
	c, err := cfg.Client(l.Id)
	if err != nil {
		return err
	}
	dc := cfg.DefaultClient()
	drawShow(w, []interface{}{
		"name", c.ClientName,
		"default", dc != nil && dc.ClientName == c.ClientName,
		"client_id", c.ClientID,
		"api_url", c.ApiUrl,
		"auth_url", c.AuthUrl,
		"username", c.Username,
		"secret", c.Secret,
		"default_account", c.DefaultAccount,
	})
	return nil

}

func ConfigureConfigCommand(app *CliApp) {
	c := &ConfigCommand{}
	configcmd := app.Command("config", "manage cli configuration")
	configcmd.Command("list", "list local client configurations").
		Default().Action(c.list)
	show := configcmd.Command("show", "view details on a client config").Action(c.show)
	show.Arg("name", "name or id of client config").Required().StringVar(&c.Id)
}
<commit_msg>config clients add<commit_after>package cli

import (
	"fmt"
	"github.com/casimir/xdg-go"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/ini.v1"
	"os"
)

var (
	xdgapp = xdg.App{Name: "brightbox"}
)

type Config struct {
	App               *kingpin.Application
	defaultClientName string
	currentClient     *Client
	clients           map[string]Client
}

func NewConfig() (*Config, error) {
	c := new(Config)
	err := c.Setup()
	if err != nil {
		return nil, err
	}
	return c, nil
}

func (c *Config) SaveClientConfig(client *Client) error {
	if client == nil {
		panic("Can't save client config for nil client")
	}

	filename := xdgapp.ConfigPath("config")
	cfg, err := ini.Load(filename)
	if os.IsNotExist(err) {
		cfg = ini.Empty()
	} else if err != nil {
		return err
	}

	section := cfg.Section(client.ClientName)
	section.Key("client_id").SetValue(client.ClientID)
	section.Key("secret").SetValue(client.Secret)
	section.Key("api_url").SetValue(client.ApiUrl)
	section.Key("auth_url").SetValue(client.AuthUrl)
	section.Key("default_account").SetValue(client.DefaultAccount)
	section.Key("username").SetValue(client.Username)
	err = cfg.SaveTo(filename)
	if err != nil {
		return err
	}
	return nil
}

func (c *Config) Client(cname string) (*Client, error) {
	client, exists := c.clients[cname]
	if exists == false {
		return nil, fmt.Errorf("client '%s' not found in config.", cname)
	}
	return &client, nil
}

func (c *Config) CurrentClient() *Client {
	return c.currentClient
}

func (c *Config) DefaultClient() *Client {
	client, err := c.Client(c.defaultClientName)
	if err != nil && client == nil {
		return nil
	}
	return client
}

func (c *Config) Setup() error {
	err := os.MkdirAll(xdgapp.ConfigPath(""), 0750)
	if err != nil {
		return err
	}
	err = os.MkdirAll(xdgapp.CachePath(""), 0750)
	if err != nil {
		return err
	}
	if c.clients == nil {
		c.clients = make(map[string]Client)
	}
	err = c.Read()
	if err != nil {
		return err
	}
	return nil
}

func (c *Config) Read() error {
	filename := xdgapp.ConfigPath("config")
	cfg, err := ini.Load(filename)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return err
	}
	core := cfg.Section("core")
	c.defaultClientName = core.Key("default_client").String()
	for _, sec := range cfg.Sections() {
		if sec.Name() != "DEFAULT" && sec.Name() != "core" {
			cs := new(Client)
			cs.ClientName = sec.Name()
			if cs.ClientName == "" {
				continue
			}
			err = sec.MapTo(cs)
			if err != nil {
				continue
			}
			c.clients[cs.ClientName] = *cs
			if c.defaultClientName == "" {
				c.defaultClientName = cs.ClientName
			}

		}
	}
	return nil

}

func (c *Config) Write() error {
	filename := xdgapp.ConfigPath("config")
	cfg, err := ini.Load(filename)
	if os.IsNotExist(err) {
		cfg = ini.Empty()
	} else if err != nil {
		return err
	}

	section := cfg.Section("core")
	key := section.Key("default_client")
	key.SetValue(c.defaultClientName)
	err = cfg.SaveTo(filename)
	if err != nil {
		return err
	}
	return nil
}

func (c *Config) SetClient(clientName string) error {
	if clientName == "" {
		c.currentClient = c.DefaultClient()
		return nil
	}
	client, err := c.Client(clientName)
	if err == nil && client != nil {
		c.currentClient = client
		return nil
	} else {
		return fmt.Errorf("client '%s' not found in config.", clientName)
	}
}

type ConfigCommand struct {
	*CliApp
	Id      string
	Secret  string
	ApiUrl  string
	AuthUrl string
	Name string
}

func (l *ConfigCommand) list(pc *kingpin.ParseContext) error {
	cfg, err := NewConfig()
	if err != nil {
		return err
	}
	w := tabWriter()
	defer w.Flush()
	listRec(w, "NAME", "CLIENTID", "SECRET", "API_URL", "AUTH_URL")
	dc := cfg.DefaultClient()
	for _, c := range cfg.clients {
		name := c.ClientName
		if dc != nil && dc.ClientName == name {
			name = "*" + name
		}
		listRec(w, name, c.ClientID, c.Secret,
			c.ApiUrl, c.findAuthUrl())
	}
	return nil
}

func (l *ConfigCommand) add(pc *kingpin.ParseContext) error {
	err := l.Configure()
	if err != nil {
		return err
	}

	client := new(Client)
	client.ClientName = l.Id
	if l.Name != "" {
		client.ClientName = l.Name
	}
	client.ClientID = l.Id
	client.Secret = l.Secret
	client.ApiUrl = l.ApiUrl
	client.AuthUrl = l.AuthUrl
	fmt.Printf("%s\n", client.AuthUrl)
	if client.AuthUrl == "" {
		client.AuthUrl = l.ApiUrl
	}

	err = l.Config.SaveClientConfig(client)
	if err != nil {
		l.Fatalf("Couldn't save client config %s: %s", client.ClientName, err)
	}
	if l.Config.DefaultClient() == nil {
		l.Config.defaultClientName = client.ClientName
		l.Config.Write()
	}
	return nil
}

func (l *ConfigCommand) show(pc *kingpin.ParseContext) error {
	cfg, err := NewConfig()
	if err != nil {
		return err
	}
	w := tabWriterRight()
	defer w.Flush()
	c, err := cfg.Client(l.Id)
	if err != nil {
		return err
	}
	dc := cfg.DefaultClient()
	drawShow(w, []interface{}{
		"name", c.ClientName,
		"default", dc != nil && dc.ClientName == c.ClientName,
		"client_id", c.ClientID,
		"api_url", c.ApiUrl,
		"auth_url", c.AuthUrl,
		"username", c.Username,
		"secret", c.Secret,
		"default_account", c.DefaultAccount,
	})
	return nil

}

func ConfigureConfigCommand(app *CliApp) {
	c := &ConfigCommand{CliApp: app}
	configcmd := app.Command("config", "manage cli configuration")
	configcmd.Command("list", "list local client configurations").
		Default().Action(c.list)
	show := configcmd.Command("show", "view details on a client config").Action(c.show)
	show.Arg("name", "name or id of client config").Required().StringVar(&c.Id)
	clients := configcmd.Command("clients", "manage clients in local config")
	cadd := clients.Command("add", "Add new API client details to the local config").
		Action(c.add)
	cadd.Arg("client_id", "id of api client. e.g: cli-xxxxx").Required().StringVar(&c.Id)
	cadd.Arg("client_secet", "secret of the api client").Required().StringVar(&c.Secret)
	cadd.Flag("api-url", "url of Brightbox API").
		Default("https://api.gb1.brightbox.com").StringVar(&c.ApiUrl)
	cadd.Flag("auth-url", "url of Brightbox API authentication endpoint. Defaults to same as api-url.").
		StringVar(&c.AuthUrl)
	cadd.Flag("name", "an alias for the client config").StringVar(&c.Name)
}
<|endoftext|>


<commit_before>package insightapi

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
)

func GetBlock(blockHash string) (block Block, err error) {
	url := "https://insight.bitpay.com/api/block/" + blockHash

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &block)
	return
}

func GetTx(txId string) (tx Tx, err error) {
	url := "https://insight.bitpay.com/api/tx/" + txId

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &tx)
	return
}

func GetAddr(addrStr string) (addr Addr, err error) {
	url := "https://insight.bitpay.com/api/addr/" + addrStr

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &addr)
	return
}
<commit_msg>Insight API URL can be changed<commit_after>package insightapi

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
)

var (
	ApiURL    = "https://insight.bitpay.com/api"
	UserAgent = "be"
)

func GetBlock(blockHash string) (block Block, err error) {
	url := ApiURL + "/block/" + blockHash

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &block)
	return
}

func GetTx(txId string) (tx Tx, err error) {
	url := ApiURL + "/tx/" + txId

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &tx)
	return
}

func GetAddr(addrStr string) (addr Addr, err error) {
	url := ApiURL + "/addr/" + addrStr

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &addr)
	return
}
<|endoftext|>


<commit_before>package pmb

import (
	"github.com/streadway/amqp"

	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"time"
)

type Message struct {
	Contents map[string]interface{}
	Raw      string
}

type Connection struct {
	Out    chan Message
	In     chan Message
	uri    string
	prefix string
	Key    string
}

var topicSuffix = "pmb"

func connect(URI string, id string) (*Connection, error) {

	uriParts, err := amqp.ParseURI(URI)
	if err != nil {
		return nil, err
	}

	// all resources are prefixed with username
	prefix := uriParts.Username

	in := make(chan Message, 10)
	out := make(chan Message, 10)

	done := make(chan error)

	conn := &Connection{In: in, Out: out, uri: URI, prefix: prefix}

	go listenToAMQP(conn, done, id)
	go sendToAMQP(conn, done, id)

	for i := 1; i <= 2; i++ {
		err := <-done
		if err != nil {
			return nil, err
		}
	}

	return conn, nil
}

func sendToAMQP(pmbConn *Connection, done chan error, id string) {

	uri := pmbConn.uri
	prefix := pmbConn.prefix
	sender := pmbConn.Out

	conn, err := connectToAMQP(uri)
	if err != nil {
		done <- err
		return
	}

	ch, err := conn.Channel()
	if err != nil {
		done <- err
		return
	}

	err = ch.ExchangeDeclare(fmt.Sprintf("%s-%s", prefix, topicSuffix), "topic", true, false, false, false, nil)
	if err != nil {
		done <- err
		return
	}

	done <- nil

	for {
		message := <-sender

		// tag message with sender id
		message.Contents["id"] = id

		// add a few other pieces of information
		hostname, ip, err := localNetInfo()

		message.Contents["hostname"] = hostname
		message.Contents["ip"] = ip
		message.Contents["sent"] = time.Now().Format(time.RFC3339)

		logger.Debugf("Sending message: %s", message.Contents)

		json, err := json.Marshal(message.Contents)
		if err != nil {
			// TODO: handle this error better
			return
		}

		var body []byte
		if len(pmbConn.Key) > 0 {
			logger.Debugf("Encrypting message...")
			encrypted, err := encrypt([]byte(pmbConn.Key), string(json))

			if err != nil {
				logger.Warningf("Unable to encrypt message!")
				continue
			}

			body = []byte(encrypted)
		} else {
			body = json
		}

		logger.Debugf("Sending raw message: %s", string(body))
		err = ch.Publish(
			fmt.Sprintf("%s-%s", prefix, topicSuffix), // exchange
			"test", // routing key
			false,  // mandatory
			false,  // immediate
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        body,
			})

		if err != nil {
			// TODO: connection probably needs to be re-initialized
			return
		}
	}
}

func localNetInfo() (string, string, error) {

	hostname, err := os.Hostname()
	if err != nil {
		return "", "", err
	}

	addrs, err := net.LookupHost(hostname)
	if err != nil {
		return hostname, "", err
	}

	return hostname, addrs[0], nil
}

func connectToAMQP(uri string) (*amqp.Connection, error) {

	var conn *amqp.Connection
	var err error

	if strings.Contains(uri, "amqps") {
		cfg := new(tls.Config)

		if len(os.Getenv("PMB_SSL_INSECURE_SKIP_VERIFY")) > 0 {
			cfg.InsecureSkipVerify = true
		}

		conn, err = amqp.DialTLS(uri, cfg)
	} else {
		conn, err = amqp.Dial(uri)
	}

	if err != nil {
		return nil, err
	}

	//logger.Debugf("Conn: ", conn)
	return conn, nil
}

func listenToAMQP(pmbConn *Connection, done chan error, id string) {

	uri := pmbConn.uri
	prefix := pmbConn.prefix
	receiver := pmbConn.In

	conn, err := connectToAMQP(uri)
	if err != nil {
		done <- err
		return
	}

	ch, err := conn.Channel()
	if err != nil {
		done <- err
		return
	}

	err = ch.ExchangeDeclare(fmt.Sprintf("%s-%s", prefix, topicSuffix), "topic", true, false, false, false, nil)
	if err != nil {
		done <- err
		return
	}

	q, err := ch.QueueDeclarePassive(fmt.Sprintf("%s-%s", prefix, id), false, true, false, false, nil)
	if err != nil {
		ch, err = conn.Channel()
		if err != nil {
			done <- err
			return
		}
		q, err = ch.QueueDeclare(fmt.Sprintf("%s-%s", prefix, id), false, true, false, false, nil)
		if err != nil {
			done <- err
			return
		}
	} else {
		err = fmt.Errorf("Another connection with the same id (%s) already exists.", id)
		done <- err
		return
	}

	err = ch.QueueBind(q.Name, "#", fmt.Sprintf("%s-%s", prefix, topicSuffix), false, nil)
	if err != nil {
		done <- err
		return
	}

	msgs, err := ch.Consume(q.Name, "", true, false, false, false, nil)
	done <- nil

	for {
		delivery, ok := <-msgs
		if !ok {
			// TODO: connection or channel closed, re-initialize
		}
		logger.Debugf("Raw message received: %s", string(delivery.Body))

		var message []byte
		if delivery.Body[0] != '{' {
			logger.Debugf("Decrypting message...")
			if len(pmbConn.Key) > 0 {
				decrypted, err := decrypt([]byte(pmbConn.Key), string(delivery.Body))
				if err != nil {
					logger.Warningf("Unable to decrypt message!")
					continue
				}

				message = []byte(decrypted)
			} else {
				logger.Warningf("Encrypted message and no key!")
			}
		} else {
			message = delivery.Body
		}

		var rawData interface{}
		err := json.Unmarshal(message, &rawData)
		if err != nil {
			logger.Warningf("Unable to unmarshal JSON data, skipping.")
			continue
		}

		data := rawData.(map[string]interface{})

		senderId := data["id"].(string)

		// hide messages from ourselves
		if senderId != id {
			logger.Debugf("Message received: %s", data)
			receiver <- Message{Contents: data, Raw: string(message)}
		} else {
			logger.Debugf("Message received but ignored: %s", data)
		}
	}

}

// encrypt string to base64'd AES
func encrypt(key []byte, text string) (string, error) {
	plaintext := []byte(text)

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return "", err
	}

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	return base64.URLEncoding.EncodeToString(ciphertext), nil
}

// decrypt from base64'd AES
func decrypt(key []byte, cryptoText string) (string, error) {
	ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	if len(ciphertext) < aes.BlockSize {
		return "", fmt.Errorf("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(ciphertext, ciphertext)

	return fmt.Sprintf("%s", ciphertext), nil
}
<commit_msg>attempt to reconnect if connection fails<commit_after>package pmb

import (
	"github.com/streadway/amqp"

	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"time"
)

type Message struct {
	Contents map[string]interface{}
	Raw      string
}

type Connection struct {
	Out    chan Message
	In     chan Message
	uri    string
	prefix string
	Key    string
}

var topicSuffix = "pmb"

func connect(URI string, id string) (*Connection, error) {

	uriParts, err := amqp.ParseURI(URI)
	if err != nil {
		return nil, err
	}

	// all resources are prefixed with username
	prefix := uriParts.Username

	in := make(chan Message, 10)
	out := make(chan Message, 10)

	done := make(chan error)

	conn := &Connection{In: in, Out: out, uri: URI, prefix: prefix}

	go listenToAMQP(conn, done, id)
	go sendToAMQP(conn, done, id)

	for i := 1; i <= 2; i++ {
		err := <-done
		if err != nil {
			return nil, err
		}
	}

	return conn, nil
}

func sendToAMQP(pmbConn *Connection, done chan error, id string) {

	ch, err := setupSend(pmbConn.uri, pmbConn.prefix, id)

	if err != nil {
		done <- err
		return
	}

	done <- nil

	sender := pmbConn.Out
	for {
		message := <-sender

		// tag message with sender id
		message.Contents["id"] = id

		// add a few other pieces of information
		hostname, ip, err := localNetInfo()

		message.Contents["hostname"] = hostname
		message.Contents["ip"] = ip
		message.Contents["sent"] = time.Now().Format(time.RFC3339)

		logger.Debugf("Sending message: %s", message.Contents)

		json, err := json.Marshal(message.Contents)
		if err != nil {
			// TODO: handle this error better
			return
		}

		var body []byte
		if len(pmbConn.Key) > 0 {
			logger.Debugf("Encrypting message...")
			encrypted, err := encrypt([]byte(pmbConn.Key), string(json))

			if err != nil {
				logger.Warningf("Unable to encrypt message!")
				continue
			}

			body = []byte(encrypted)
		} else {
			body = json
		}

		logger.Debugf("Sending raw message: %s", string(body))
		err = ch.Publish(
			fmt.Sprintf("%s-%s", pmbConn.prefix, topicSuffix), // exchange
			"test", // routing key
			false,  // mandatory
			false,  // immediate
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        body,
			})

		if err != nil {
			logger.Warningf("Send connection fail reconnecting...", err)

			// attempt to reconnect forever
			ch, err = setupSendForever(pmbConn.uri, pmbConn.prefix, id)

			if err != nil {
				logger.Criticalf("Unable to reconnect, exiting... %s", err)
				return
			} else {
				logger.Infof("Reconnected.")
				err = ch.Publish(
					fmt.Sprintf("%s-%s", pmbConn.prefix, topicSuffix), // exchange
					"test", // routing key
					false,  // mandatory
					false,  // immediate
					amqp.Publishing{
						ContentType: "text/plain",
						Body:        body,
					})
			}
		}
	}
}

func localNetInfo() (string, string, error) {

	hostname, err := os.Hostname()
	if err != nil {
		return "", "", err
	}

	addrs, err := net.LookupHost(hostname)
	if err != nil {
		return hostname, "", err
	}

	return hostname, addrs[0], nil
}

func connectToAMQP(uri string) (*amqp.Connection, error) {

	var conn *amqp.Connection
	var err error

	if strings.Contains(uri, "amqps") {
		cfg := new(tls.Config)

		if len(os.Getenv("PMB_SSL_INSECURE_SKIP_VERIFY")) > 0 {
			cfg.InsecureSkipVerify = true
		}

		conn, err = amqp.DialTLS(uri, cfg)
	} else {
		conn, err = amqp.Dial(uri)
	}

	if err != nil {
		return nil, err
	}

	//logger.Debugf("Conn: ", conn)
	return conn, nil
}

func listenToAMQP(pmbConn *Connection, done chan error, id string) {

	msgs, err := setupListen(pmbConn.uri, pmbConn.prefix, id)

	if err != nil {
		done <- err
		return
	}

	done <- nil

	receiver := pmbConn.In
	for {
		delivery, ok := <-msgs
		if !ok {
			logger.Warningf("Listen connection fail, reconnecting...")

			// attempt to reconnect forever
			msgs, err = setupListenForever(pmbConn.uri, pmbConn.prefix, id)

			if err != nil {
				logger.Criticalf("Unable to reconnect, exiting... %s", err)
				return
			} else {
				logger.Infof("Reconnected.")
				continue
			}

		}
		logger.Debugf("Raw message received: %s", string(delivery.Body))

		var message []byte
		if delivery.Body[0] != '{' {
			logger.Debugf("Decrypting message...")
			if len(pmbConn.Key) > 0 {
				decrypted, err := decrypt([]byte(pmbConn.Key), string(delivery.Body))
				if err != nil {
					logger.Warningf("Unable to decrypt message!")
					continue
				}

				message = []byte(decrypted)
			} else {
				logger.Warningf("Encrypted message and no key!")
			}
		} else {
			message = delivery.Body
		}

		var rawData interface{}
		err := json.Unmarshal(message, &rawData)
		if err != nil {
			logger.Warningf("Unable to unmarshal JSON data, skipping.")
			continue
		}

		data := rawData.(map[string]interface{})

		senderId := data["id"].(string)

		// hide messages from ourselves
		if senderId != id {
			logger.Debugf("Message received: %s", data)
			receiver <- Message{Contents: data, Raw: string(message)}
		} else {
			logger.Debugf("Message received but ignored: %s", data)
		}
	}

}

func setupSendForever(uri string, prefix string, id string) (*amqp.Channel, error) {

	for {
		ch, err := setupSend(uri, prefix, id)

		if err == nil {
			return ch, nil
		}

		logger.Warningf("Send setup failed, sleeping and then re-trying")
		time.Sleep(1 * time.Second)
	}
}

func setupSend(uri string, prefix string, id string) (*amqp.Channel, error) {
	conn, err := connectToAMQP(uri)
	if err != nil {
		return nil, err
	}

	ch, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	err = ch.ExchangeDeclare(fmt.Sprintf("%s-%s", prefix, topicSuffix), "topic", true, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	return ch, nil
}

func setupListenForever(uri string, prefix string, id string) (<-chan amqp.Delivery, error) {

	for {
		msgs, err := setupListen(uri, prefix, id)

		if err == nil {
			return msgs, nil
		}

		logger.Warningf("Listen setup failed, sleeping and then re-trying")
		time.Sleep(1 * time.Second)
	}
}

func setupListen(uri string, prefix string, id string) (<-chan amqp.Delivery, error) {

	conn, err := connectToAMQP(uri)
	if err != nil {
		return nil, err
	}

	ch, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	err = ch.ExchangeDeclare(fmt.Sprintf("%s-%s", prefix, topicSuffix), "topic", true, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	q, err := ch.QueueDeclarePassive(fmt.Sprintf("%s-%s", prefix, id), false, true, false, false, nil)
	if err != nil {
		ch, err = conn.Channel()
		if err != nil {
			return nil, err
		}
		q, err = ch.QueueDeclare(fmt.Sprintf("%s-%s", prefix, id), false, true, false, false, nil)
		if err != nil {
			return nil, err
		}
	} else {
		err = fmt.Errorf("Another connection with the same id (%s) already exists.", id)
		return nil, err
	}

	err = ch.QueueBind(q.Name, "#", fmt.Sprintf("%s-%s", prefix, topicSuffix), false, nil)
	if err != nil {
		return nil, err
	}

	msgs, err := ch.Consume(q.Name, "", true, false, false, false, nil)

	return msgs, nil
}

// encrypt string to base64'd AES
func encrypt(key []byte, text string) (string, error) {
	plaintext := []byte(text)

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return "", err
	}

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	return base64.URLEncoding.EncodeToString(ciphertext), nil
}

// decrypt from base64'd AES
func decrypt(key []byte, cryptoText string) (string, error) {
	ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	if len(ciphertext) < aes.BlockSize {
		return "", fmt.Errorf("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(ciphertext, ciphertext)

	return fmt.Sprintf("%s", ciphertext), nil
}
<|endoftext|>


<commit_before>package gorange

import (
	"bufio"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/karrick/gogetter"
)

// Client attempts to resolve range queries to list of strings or an error.
type Client struct {
	Getter gogetter.Getter
}

// Query sends the specified query string to the Client's Getter, and converts a non-error result
// into a list of strings.
//
// If the response includes a RangeException header, it returns ErrRangeException. If the status
// code is not okay, it returns ErrStatusNotOK. Finally, if it cannot parse the lines in the
// response body, it returns ErrParseException.
func (rc *Client) Query(query string) ([]string, error) {
	resp, err := rc.Getter.Get(url.QueryEscape(query))
	if err != nil {
		return nil, err
	}

	// got a response from this server, so commit to reading entire body (needed when re-using
	// connections)
	defer func(iorc io.ReadCloser) {
		io.Copy(ioutil.Discard, iorc) // so we can reuse connections via Keep-Alive
		iorc.Close()
	}(resp.Body)

	// NOTE: wrap known range exceptions
	rangeException := resp.Header.Get("RangeException")
	if rangeException != "" {
		// if strings.HasPrefix(rangeException, "NOCLUSTERDEF") {
		// 	return nil, ErrNoClusterDef{rangeException}
		// } else if strings.HasPrefix(rangeException, "NOCLUSTER") {
		// 	return nil, ErrNoCluster{rangeException}
		// } else if strings.HasPrefix(rangeException, "NO_COLO") {
		// 	return nil, ErrNoColo{rangeException}
		// } else if strings.HasPrefix(rangeException, "NOTINYDNS") {
		// 	return nil, ErrNoTinyDNS{rangeException}
		// } else if strings.HasPrefix(rangeException, "HOST_NO_NETBLOCK") {
		// 	return nil, ErrHostNoNetblock{rangeException}
		// } else if strings.HasPrefix(rangeException, "NETBLOCK_NOT_FOUND") {
		// 	return nil, ErrNetblockNotFound{rangeException}
		// } else if strings.HasPrefix(rangeException, "DC_NOT_FOUND") {
		// 	return nil, ErrDCNotFound{rangeException}
		// } else if strings.HasPrefix(rangeException, "NOVIPS") {
		// 	return nil, ErrNoVIPs{rangeException}
		// }
		return nil, ErrRangeException{rangeException}
	}
	if resp.StatusCode != http.StatusOK {
		return nil, ErrStatusNotOK{resp.Status, resp.StatusCode}
	}

	var lines []string

	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		lines = append(lines, strings.TrimSpace(scanner.Text()))
	}

	if err = scanner.Err(); err != nil {
		return nil, ErrParseException{err}
	}

	return lines, nil
}

// ErrRangeException is returned when the response headers includes 'RangeException'.
type ErrRangeException struct {
	Message string
}

func (err ErrRangeException) Error() string {
	return "RangeException: " + err.Message
}

// ErrStatusNotOK is returned when the response status code is not Ok.
type ErrStatusNotOK struct {
	Status     string
	StatusCode int
}

func (err ErrStatusNotOK) Error() string {
	return "response status code: " + strconv.Itoa(err.StatusCode)
}

// ErrParseException is returned by Client.Query method when an error occurs while parsing the Get
// response.
type ErrParseException struct {
	Err error
}

func (err ErrParseException) Error() string {
	return "cannot parse response: " + err.Err.Error()
}

// type ErrNoClusterDef struct {
// 	Message string
// }

// func (err ErrNoClusterDef) Error() string {
// 	return err.Message
// }

// type ErrNoTinyDNS struct {
// 	Message string
// }

// func (err ErrNoTinyDNS) Error() string {
// 	return err.Message
// }

// type ErrHostNoNetblock struct {
// 	Message string
// }

// func (err ErrHostNoNetblock) Error() string {
// 	return err.Message
// }

// type ErrNoColo struct {
// 	Message string
// }

// func (err ErrNoColo) Error() string {
// 	return err.Message
// }

// type ErrNetblockNotFound struct {
// 	Message string
// }

// func (err ErrNetblockNotFound) Error() string {
// 	return err.Message
// }

// type ErrDCNotFound struct {
// 	Message string
// }

// func (err ErrDCNotFound) Error() string {
// 	return err.Message
// }

// type ErrNoVIPs struct {
// 	Message string
// }

// func (err ErrNoVIPs) Error() string {
// 	return err.Message
// }

// type ErrNoCluster struct {
// 	Message string
// }

// func (err ErrNoCluster) Error() string {
// 	return err.Message
// }
<commit_msg>remove commented out code<commit_after>package gorange

import (
	"bufio"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/karrick/gogetter"
)

// Client attempts to resolve range queries to list of strings or an error.
type Client struct {
	Getter gogetter.Getter
}

// Query sends the specified query string to the Client's Getter, and converts a non-error result
// into a list of strings.
//
// If the response includes a RangeException header, it returns ErrRangeException. If the status
// code is not okay, it returns ErrStatusNotOK. Finally, if it cannot parse the lines in the
// response body, it returns ErrParseException.
func (rc *Client) Query(query string) ([]string, error) {
	resp, err := rc.Getter.Get(url.QueryEscape(query))
	if err != nil {
		return nil, err
	}

	// got a response from this server, so commit to reading entire body (needed when re-using
	// connections)
	defer func(iorc io.ReadCloser) {
		io.Copy(ioutil.Discard, iorc) // so we can reuse connections via Keep-Alive
		iorc.Close()
	}(resp.Body)

	// NOTE: wrap known range exceptions
	rangeException := resp.Header.Get("RangeException")
	if rangeException != "" {
		return nil, ErrRangeException{rangeException}
	}
	if resp.StatusCode != http.StatusOK {
		return nil, ErrStatusNotOK{resp.Status, resp.StatusCode}
	}

	var lines []string

	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		lines = append(lines, strings.TrimSpace(scanner.Text()))
	}

	if err = scanner.Err(); err != nil {
		return nil, ErrParseException{err}
	}

	return lines, nil
}

// ErrRangeException is returned when the response headers includes 'RangeException'.
type ErrRangeException struct {
	Message string
}

func (err ErrRangeException) Error() string {
	return "RangeException: " + err.Message
}

// ErrStatusNotOK is returned when the response status code is not Ok.
type ErrStatusNotOK struct {
	Status     string
	StatusCode int
}

func (err ErrStatusNotOK) Error() string {
	return "response status code: " + strconv.Itoa(err.StatusCode)
}

// ErrParseException is returned by Client.Query method when an error occurs while parsing the Get
// response.
type ErrParseException struct {
	Err error
}

func (err ErrParseException) Error() string {
	return "cannot parse response: " + err.Err.Error()
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, 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 api

import (
	"fmt"
	"net/http"

	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	"github.com/vmware/harbor/utils/log"

	"strconv"
	"time"
)

// ProjectAPI handles request to /api/projects/{} /api/projects/{}/logs
type ProjectAPI struct {
	BaseAPI
	userID    int
	projectID int64
}

type projectReq struct {
	ProjectName string `json:"project_name"`
	Public      bool   `json:"public"`
}

const projectNameMaxLen int = 30

// Prepare validates the URL and the user
func (p *ProjectAPI) Prepare() {
	p.userID = p.ValidateUser()
	idStr := p.Ctx.Input.Param(":id")
	if len(idStr) > 0 {
		var err error
		p.projectID, err = strconv.ParseInt(idStr, 10, 64)
		if err != nil {
			log.Errorf("Error parsing project id: %s, error: %v", idStr, err)
			p.CustomAbort(http.StatusBadRequest, "invalid project id")
		}
		exist, err := dao.ProjectExists(p.projectID)
		if err != nil {
			log.Errorf("Error occurred in ProjectExists, error: %v", err)
			p.CustomAbort(http.StatusInternalServerError, "Internal error.")
		}
		if !exist {
			p.CustomAbort(http.StatusNotFound, fmt.Sprintf("project does not exist, id: %v", p.projectID))
		}
	}
}

// Post ...
func (p *ProjectAPI) Post() {
	var req projectReq
	var public int
	p.DecodeJSONReq(&req)
	if req.Public {
		public = 1
	}
	err := validateProjectReq(req)
	if err != nil {
		log.Errorf("Invalid project request, error: %v", err)
		p.RenderError(http.StatusBadRequest, "Invalid request for creating project")
		return
	}
	projectName := req.ProjectName
	exist, err := dao.ProjectExists(projectName)
	if err != nil {
		log.Errorf("Error happened checking project existence in db, error: %v, project name: %s", err, projectName)
	}
	if exist {
		p.RenderError(http.StatusConflict, "")
		return
	}
	project := models.Project{OwnerID: p.userID, Name: projectName, CreationTime: time.Now(), Public: public}
	projectID, err := dao.AddProject(project)
	if err != nil {
		log.Errorf("Failed to add project, error: %v", err)
		p.RenderError(http.StatusInternalServerError, "Failed to add project")
	}

	p.Redirect(http.StatusCreated, strconv.FormatInt(projectID, 10))
}

// Head ...
func (p *ProjectAPI) Head() {
	projectName := p.GetString("project_name")
	result, err := dao.ProjectExists(projectName)
	if err != nil {
		log.Errorf("Error while communicating with DB, error: %v", err)
		p.RenderError(http.StatusInternalServerError, "Error while communicating with DB")
		return
	}
	if !result {
		p.RenderError(http.StatusNotFound, "")
		return
	}
}

// Get ...
func (p *ProjectAPI) Get() {
	var projectList []models.Project
	projectName := p.GetString("project_name")
	if len(projectName) > 0 {
		projectName = "%" + projectName + "%"
	}
	var public int
	var err error
	isPublic := p.GetString("is_public")
	if len(isPublic) > 0 {
		public, err = strconv.Atoi(isPublic)
		if err != nil {
			log.Errorf("Error parsing public property: %d, error: %v", isPublic, err)
			p.CustomAbort(http.StatusBadRequest, "invalid project Id")
		}
	}
	isAdmin := false
	if public == 1 {
		projectList, err = dao.GetPublicProjects(projectName)
	} else {
		isAdmin, err = dao.IsAdminRole(p.userID)
		if err != nil {
			log.Errorf("Error occured in check admin, error: %v", err)
			p.CustomAbort(http.StatusInternalServerError, "Internal error.")
		}
		if isAdmin {
			projectList, err = dao.GetAllProjects(projectName)
		} else {
			projectList, err = dao.GetUserRelevantProjects(p.userID, projectName)
		}
	}
	if err != nil {
		log.Errorf("Error occured in GetUserRelevantProjects, error: %v", err)
		p.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	for i := 0; i < len(projectList); i++ {
		if public != 1 {
			if isAdmin {
				projectList[i].Role = models.PROJECTADMIN
			}
			if projectList[i].Role == models.PROJECTADMIN {
				projectList[i].Togglable = true
			}
		}
		projectList[i].RepoCount = getRepoCountByProject(projectList[i].Name)
	}
	p.Data["json"] = projectList
	p.ServeJSON()
}

// Put ...
func (p *ProjectAPI) Put() {
	var req projectReq
	var public int

	projectID, err := strconv.ParseInt(p.Ctx.Input.Param(":id"), 10, 64)
	if err != nil {
		log.Errorf("Error parsing project id: %d, error: %v", projectID, err)
		p.RenderError(http.StatusBadRequest, "invalid project id")
		return
	}

	p.DecodeJSONReq(&req)
	if req.Public {
		public = 1
	}
	if !isProjectAdmin(p.userID, projectID) {
		log.Warningf("Current user, id: %d does not have project admin role for project, id: %d", p.userID, projectID)
		p.RenderError(http.StatusForbidden, "")
		return
	}
	err = dao.ToggleProjectPublicity(p.projectID, public)
	if err != nil {
		log.Errorf("Error while updating project, project id: %d, error: %v", projectID, err)
		p.RenderError(http.StatusInternalServerError, "Failed to update project")
	}
}

// FilterAccessLog handles GET to /api/projects/{}/logs
func (p *ProjectAPI) FilterAccessLog() {

	var filter models.AccessLog
	p.DecodeJSONReq(&filter)

	username := filter.Username
	keywords := filter.Keywords

	beginTime := time.Unix(filter.BeginTimestamp, 0)
	endTime := time.Unix(filter.EndTimestamp, 0)

	query := models.AccessLog{ProjectID: p.projectID, Username: "%" + username + "%", Keywords: keywords, BeginTime: beginTime, BeginTimestamp: filter.BeginTimestamp, EndTime: endTime, EndTimestamp: filter.EndTimestamp}

	log.Infof("Query AccessLog: begin: %v, end: %v, keywords: %s", query.BeginTime, query.EndTime, query.Keywords)

	accessLogList, err := dao.GetAccessLogs(query)
	if err != nil {
		log.Errorf("Error occurred in GetAccessLogs, error: %v", err)
		p.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	p.Data["json"] = accessLogList

	p.ServeJSON()
}

func isProjectAdmin(userID int, pid int64) bool {
	isSysAdmin, err := dao.IsAdminRole(userID)
	if err != nil {
		log.Errorf("Error occurred in IsAdminRole, returning false, error: %v", err)
		return false
	}

	if isSysAdmin {
		return true
	}

	rolelist, err := dao.GetUserProjectRoles(userID, pid)
	if err != nil {
		log.Errorf("Error occurred in GetUserProjectRoles, returning false, error: %v", err)
		return false
	}

	hasProjectAdminRole := false
	for _, role := range rolelist {
		if role.RoleID == models.PROJECTADMIN {
			hasProjectAdminRole = true
			break
		}
	}

	return hasProjectAdminRole
}

func validateProjectReq(req projectReq) error {
	pn := req.ProjectName
	if len(pn) == 0 {
		return fmt.Errorf("Project name can not be empty")
	}
	if len(pn) > projectNameMaxLen {
		return fmt.Errorf("Project name is too long")
	}
	return nil
}
<commit_msg>modify error message<commit_after>/*
   Copyright (c) 2016 VMware, 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 api

import (
	"fmt"
	"net/http"

	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	"github.com/vmware/harbor/utils/log"

	"strconv"
	"time"
)

// ProjectAPI handles request to /api/projects/{} /api/projects/{}/logs
type ProjectAPI struct {
	BaseAPI
	userID    int
	projectID int64
}

type projectReq struct {
	ProjectName string `json:"project_name"`
	Public      bool   `json:"public"`
}

const projectNameMaxLen int = 30

// Prepare validates the URL and the user
func (p *ProjectAPI) Prepare() {
	p.userID = p.ValidateUser()
	idStr := p.Ctx.Input.Param(":id")
	if len(idStr) > 0 {
		var err error
		p.projectID, err = strconv.ParseInt(idStr, 10, 64)
		if err != nil {
			log.Errorf("Error parsing project id: %s, error: %v", idStr, err)
			p.CustomAbort(http.StatusBadRequest, "invalid project id")
		}
		exist, err := dao.ProjectExists(p.projectID)
		if err != nil {
			log.Errorf("Error occurred in ProjectExists, error: %v", err)
			p.CustomAbort(http.StatusInternalServerError, "Internal error.")
		}
		if !exist {
			p.CustomAbort(http.StatusNotFound, fmt.Sprintf("project does not exist, id: %v", p.projectID))
		}
	}
}

// Post ...
func (p *ProjectAPI) Post() {
	var req projectReq
	var public int
	p.DecodeJSONReq(&req)
	if req.Public {
		public = 1
	}
	err := validateProjectReq(req)
	if err != nil {
		log.Errorf("Invalid project request, error: %v", err)
		p.RenderError(http.StatusBadRequest, "Invalid request for creating project")
		return
	}
	projectName := req.ProjectName
	exist, err := dao.ProjectExists(projectName)
	if err != nil {
		log.Errorf("Error happened checking project existence in db, error: %v, project name: %s", err, projectName)
	}
	if exist {
		p.RenderError(http.StatusConflict, "")
		return
	}
	project := models.Project{OwnerID: p.userID, Name: projectName, CreationTime: time.Now(), Public: public}
	projectID, err := dao.AddProject(project)
	if err != nil {
		log.Errorf("Failed to add project, error: %v", err)
		p.RenderError(http.StatusInternalServerError, "Failed to add project")
	}

	p.Redirect(http.StatusCreated, strconv.FormatInt(projectID, 10))
}

// Head ...
func (p *ProjectAPI) Head() {
	projectName := p.GetString("project_name")
	result, err := dao.ProjectExists(projectName)
	if err != nil {
		log.Errorf("Error while communicating with DB, error: %v", err)
		p.RenderError(http.StatusInternalServerError, "Error while communicating with DB")
		return
	}
	if !result {
		p.RenderError(http.StatusNotFound, "")
		return
	}
}

// Get ...
func (p *ProjectAPI) Get() {
	var projectList []models.Project
	projectName := p.GetString("project_name")
	if len(projectName) > 0 {
		projectName = "%" + projectName + "%"
	}
	var public int
	var err error
	isPublic := p.GetString("is_public")
	if len(isPublic) > 0 {
		public, err = strconv.Atoi(isPublic)
		if err != nil {
			log.Errorf("Error parsing public property: %d, error: %v", isPublic, err)
			p.CustomAbort(http.StatusBadRequest, "invalid project Id")
		}
	}
	isAdmin := false
	if public == 1 {
		projectList, err = dao.GetPublicProjects(projectName)
	} else {
		isAdmin, err = dao.IsAdminRole(p.userID)
		if err != nil {
			log.Errorf("Error occured in check admin, error: %v", err)
			p.CustomAbort(http.StatusInternalServerError, "Internal error.")
		}
		if isAdmin {
			projectList, err = dao.GetAllProjects(projectName)
		} else {
			projectList, err = dao.GetUserRelevantProjects(p.userID, projectName)
		}
	}
	if err != nil {
		log.Errorf("Error occured in get projects info, error: %v", err)
		p.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	for i := 0; i < len(projectList); i++ {
		if public != 1 {
			if isAdmin {
				projectList[i].Role = models.PROJECTADMIN
			}
			if projectList[i].Role == models.PROJECTADMIN {
				projectList[i].Togglable = true
			}
		}
		projectList[i].RepoCount = getRepoCountByProject(projectList[i].Name)
	}
	p.Data["json"] = projectList
	p.ServeJSON()
}

// Put ...
func (p *ProjectAPI) Put() {
	var req projectReq
	var public int

	projectID, err := strconv.ParseInt(p.Ctx.Input.Param(":id"), 10, 64)
	if err != nil {
		log.Errorf("Error parsing project id: %d, error: %v", projectID, err)
		p.RenderError(http.StatusBadRequest, "invalid project id")
		return
	}

	p.DecodeJSONReq(&req)
	if req.Public {
		public = 1
	}
	if !isProjectAdmin(p.userID, projectID) {
		log.Warningf("Current user, id: %d does not have project admin role for project, id: %d", p.userID, projectID)
		p.RenderError(http.StatusForbidden, "")
		return
	}
	err = dao.ToggleProjectPublicity(p.projectID, public)
	if err != nil {
		log.Errorf("Error while updating project, project id: %d, error: %v", projectID, err)
		p.RenderError(http.StatusInternalServerError, "Failed to update project")
	}
}

// FilterAccessLog handles GET to /api/projects/{}/logs
func (p *ProjectAPI) FilterAccessLog() {

	var filter models.AccessLog
	p.DecodeJSONReq(&filter)

	username := filter.Username
	keywords := filter.Keywords

	beginTime := time.Unix(filter.BeginTimestamp, 0)
	endTime := time.Unix(filter.EndTimestamp, 0)

	query := models.AccessLog{ProjectID: p.projectID, Username: "%" + username + "%", Keywords: keywords, BeginTime: beginTime, BeginTimestamp: filter.BeginTimestamp, EndTime: endTime, EndTimestamp: filter.EndTimestamp}

	log.Infof("Query AccessLog: begin: %v, end: %v, keywords: %s", query.BeginTime, query.EndTime, query.Keywords)

	accessLogList, err := dao.GetAccessLogs(query)
	if err != nil {
		log.Errorf("Error occurred in GetAccessLogs, error: %v", err)
		p.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	p.Data["json"] = accessLogList

	p.ServeJSON()
}

func isProjectAdmin(userID int, pid int64) bool {
	isSysAdmin, err := dao.IsAdminRole(userID)
	if err != nil {
		log.Errorf("Error occurred in IsAdminRole, returning false, error: %v", err)
		return false
	}

	if isSysAdmin {
		return true
	}

	rolelist, err := dao.GetUserProjectRoles(userID, pid)
	if err != nil {
		log.Errorf("Error occurred in GetUserProjectRoles, returning false, error: %v", err)
		return false
	}

	hasProjectAdminRole := false
	for _, role := range rolelist {
		if role.RoleID == models.PROJECTADMIN {
			hasProjectAdminRole = true
			break
		}
	}

	return hasProjectAdminRole
}

func validateProjectReq(req projectReq) error {
	pn := req.ProjectName
	if len(pn) == 0 {
		return fmt.Errorf("Project name can not be empty")
	}
	if len(pn) > projectNameMaxLen {
		return fmt.Errorf("Project name is too long")
	}
	return nil
}
<|endoftext|>


<commit_before>package ssh

import "os"

type C struct {
	User string
	Host string
	HostKeyFun  func([]byte)os.Error
	PasswordFun func()string
}

func New(C C) (*Client,os.Error) {
	c,e := connect(C.Host)
	if e != nil { return nil,e }
	writeKexInit(c)
	b,e := readPacket(c)
	c.skex = make([]byte, len(b))
	copy(c.skex, b)
	if e!=nil { return nil,e }
	k,e := parseKexInit(c,b[1:])
	Log(6,"%v",k)
	if e!=nil { return nil,e }
	e = dh(c,k,&C)
	if e!=nil { return nil,e }

	client := &Client{ssh: c}
	for C.PasswordFun!=nil && !password(c,C.User, C.PasswordFun()) {}
	startClientLoop(client)
	return client, nil
}

<commit_msg>Allow PasswordFun to return an error<commit_after>package ssh

import "os"

type C struct {
	User string
	Host string
	HostKeyFun  func([]byte)os.Error
	PasswordFun func()(string,os.Error)
}

func New(C C) (*Client,os.Error) {
	c,e := connect(C.Host)
	if e != nil { return nil,e }
	writeKexInit(c)
	b,e := readPacket(c)
	c.skex = make([]byte, len(b))
	copy(c.skex, b)
	if e!=nil { return nil,e }
	k,e := parseKexInit(c,b[1:])
	Log(6,"%v",k)
	if e!=nil { return nil,e }
	e = dh(c,k,&C)
	if e!=nil { return nil,e }

	client := &Client{ssh: c}
	for C.PasswordFun!=nil {
		pass,err := C.PasswordFun()
		if err!=nil {
			return nil,err
		}
		if password(c,C.User, pass) {
			break
		}
	}
	startClientLoop(client)
	return client, nil
}

<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cluster

import (
	"github.com/golang/protobuf/proto"
)

// A Value provides access to a versioned value in the configuration store
type Value interface {
	// Get retrieves the stored value
	Get(v proto.Message) error

	// Version returns the current version of the value
	Version() int
}

// KVStore provides access to the configuration store
type KVStore interface {
	// Get retrieves the value for the given key
	Get(key string) (Value, error)

	// Set stores the value for the given key
	Set(key string, v proto.Message) error

	// SetIfEmpty sets the value for the given key only if no value already exists
	SetIfEmpty(key string, v proto.Message) error

	// CheckAndSet stores the value for the given key if the current version matches
	// the provided version
	CheckAndSet(key string, version int, v proto.Message) error
}

// A ServiceInstance is a single instance of a service
type ServiceInstance interface {
	Service() string                      // the service implemented by the instance
	SetService(s string) ServiceInstance  // sets the service implemented by the instance
	ID() string                           // ID of the instance
	SetID(id string) ServiceInstance      // sets the ID of the instance
	Zone() string                         // Zone in which the instance resides
	SetZone(z string) ServiceInstance     // sets the zone in which the instance resides
	Endpoint() string                     // Endpoint address for contacting the instance
	SetEndpoint(e string) ServiceInstance // sets the endpoint address for the instance
}

// NewServiceInstance creates a new ServiceInstance
func NewServiceInstance() ServiceInstance { return new(serviceInstance) }

// Advertisement advertises the availability of a given instance of a service
type Advertisement interface {
	ID() string                                  // the ID of the instance being advertised
	SetID(id string) Advertisement               // sets the ID being advertised
	Service() string                             // the service being advertised
	SetService(service string) Advertisement     // sets the service being advertised
	Health() func() error                        // optional health function.  return an error to indicate unhealthy
	SetHealth(health func() error) Advertisement // sets the health function for the advertised instance
	Endpoint() string                            // endpoint exposed by the service
	SetEndpoint(e string) Advertisement          // sets the endpoint exposed by the service
}

// NewAdvertisement creates a new Advertisement
func NewAdvertisement() Advertisement { return new(advertisement) }

// QueryOptions are options to service discovery queries
type QueryOptions interface {
	Zones() []string                         // list of zones to consult. if empty only the local zone will be queried
	SetZones(zones []string) QueryOptions    // sets the list of zones to consult
	IncludeUnhealthy() bool                  // if true, will return unhealthy instances
	SetIncludeUnhealthy(h bool) QueryOptions // sets whether to include unhealthy instances
}

// NewQueryOptions creates new QueryOptions
func NewQueryOptions() QueryOptions { return new(queryOptions) }

// Services provides access to the service topology
type Services interface {
	// Advertise advertises the availability of an instance of a service
	Advertise(ad Advertisement) error

	// Unadvertise indicates a given instance is no longer available
	Unadvertise(service, id string) error

	// QueryInstances returns the list of available instances for a given service
	QueryInstances(service string, opts QueryOptions) ([]ServiceInstance, error)
}

// Client is the base interface into the cluster management system, providing
// access to cluster services
type Client interface {
	// Services returns access to the set of services
	Services() Services

	// KV returns access to the distributed configuration store
	KV() KVStore
}

type serviceInstance struct {
	id       string
	service  string
	zone     string
	endpoint string
}

func (i *serviceInstance) Service() string                      { return i.service }
func (i *serviceInstance) ID() string                           { return i.id }
func (i *serviceInstance) Zone() string                         { return i.zone }
func (i *serviceInstance) Endpoint() string                     { return i.endpoint }
func (i *serviceInstance) SetService(s string) ServiceInstance  { i.service = s; return i }
func (i *serviceInstance) SetID(id string) ServiceInstance      { i.id = id; return i }
func (i *serviceInstance) SetZone(z string) ServiceInstance     { i.zone = z; return i }
func (i *serviceInstance) SetEndpoint(e string) ServiceInstance { i.endpoint = e; return i }

type advertisement struct {
	id       string
	service  string
	endpoint string
	health   func() error
}

func (a *advertisement) ID() string                             { return a.id }
func (a *advertisement) Service() string                        { return a.service }
func (a *advertisement) Endpoint() string                       { return a.endpoint }
func (a *advertisement) Health() func() error                   { return a.health }
func (a *advertisement) SetID(id string) Advertisement          { a.id = id; return a }
func (a *advertisement) SetService(s string) Advertisement      { a.service = s; return a }
func (a *advertisement) SetEndpoint(e string) Advertisement     { a.endpoint = e; return a }
func (a *advertisement) SetHealth(h func() error) Advertisement { a.health = h; return a }

type queryOptions struct {
	zones            []string
	includeUnhealthy bool
}

func (qo *queryOptions) Zones() []string                         { return qo.zones }
func (qo *queryOptions) IncludeUnhealthy() bool                  { return qo.includeUnhealthy }
func (qo *queryOptions) SetZones(z []string) QueryOptions        { qo.zones = z; return qo }
func (qo *queryOptions) SetIncludeUnhealthy(h bool) QueryOptions { qo.includeUnhealthy = h; return qo }
<commit_msg>Add standard errors<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cluster

import (
	"errors"

	"github.com/golang/protobuf/proto"
)

var (
	// ErrVersionMismatch is returned when attempting a CheckAndSet and the
	// key is not at the provided version
	ErrVersionMismatch = errors.New("key is not at the specified version")

	// ErrNotEmpty is returned when attempting a SetIfEmpty and the key
	// already has a value
	ErrNotEmpty = errors.New("key already has a value")

	// ErrNotFound is returned when attempting a Get but no value is found for
	// the given key
	ErrNotFound = errors.New("key not found")
)

// A Value provides access to a versioned value in the configuration store
type Value interface {
	// Get retrieves the stored value
	Get(v proto.Message) error

	// Version returns the current version of the value
	Version() int
}

// KVStore provides access to the configuration store
type KVStore interface {
	// Get retrieves the value for the given key
	Get(key string) (Value, error)

	// Set stores the value for the given key
	Set(key string, v proto.Message) error

	// SetIfEmpty sets the value for the given key only if no value already exists
	SetIfEmpty(key string, v proto.Message) error

	// CheckAndSet stores the value for the given key if the current version matches
	// the provided version
	CheckAndSet(key string, version int, v proto.Message) error
}

// A ServiceInstance is a single instance of a service
type ServiceInstance interface {
	Service() string                      // the service implemented by the instance
	SetService(s string) ServiceInstance  // sets the service implemented by the instance
	ID() string                           // ID of the instance
	SetID(id string) ServiceInstance      // sets the ID of the instance
	Zone() string                         // Zone in which the instance resides
	SetZone(z string) ServiceInstance     // sets the zone in which the instance resides
	Endpoint() string                     // Endpoint address for contacting the instance
	SetEndpoint(e string) ServiceInstance // sets the endpoint address for the instance
}

// NewServiceInstance creates a new ServiceInstance
func NewServiceInstance() ServiceInstance { return new(serviceInstance) }

// Advertisement advertises the availability of a given instance of a service
type Advertisement interface {
	ID() string                                  // the ID of the instance being advertised
	SetID(id string) Advertisement               // sets the ID being advertised
	Service() string                             // the service being advertised
	SetService(service string) Advertisement     // sets the service being advertised
	Health() func() error                        // optional health function.  return an error to indicate unhealthy
	SetHealth(health func() error) Advertisement // sets the health function for the advertised instance
	Endpoint() string                            // endpoint exposed by the service
	SetEndpoint(e string) Advertisement          // sets the endpoint exposed by the service
}

// NewAdvertisement creates a new Advertisement
func NewAdvertisement() Advertisement { return new(advertisement) }

// QueryOptions are options to service discovery queries
type QueryOptions interface {
	Zones() []string                         // list of zones to consult. if empty only the local zone will be queried
	SetZones(zones []string) QueryOptions    // sets the list of zones to consult
	IncludeUnhealthy() bool                  // if true, will return unhealthy instances
	SetIncludeUnhealthy(h bool) QueryOptions // sets whether to include unhealthy instances
}

// NewQueryOptions creates new QueryOptions
func NewQueryOptions() QueryOptions { return new(queryOptions) }

// Services provides access to the service topology
type Services interface {
	// Advertise advertises the availability of an instance of a service
	Advertise(ad Advertisement) error

	// Unadvertise indicates a given instance is no longer available
	Unadvertise(service, id string) error

	// QueryInstances returns the list of available instances for a given service
	QueryInstances(service string, opts QueryOptions) ([]ServiceInstance, error)
}

// Client is the base interface into the cluster management system, providing
// access to cluster services
type Client interface {
	// Services returns access to the set of services
	Services() Services

	// KV returns access to the distributed configuration store
	KV() KVStore
}

type serviceInstance struct {
	id       string
	service  string
	zone     string
	endpoint string
}

func (i *serviceInstance) Service() string                      { return i.service }
func (i *serviceInstance) ID() string                           { return i.id }
func (i *serviceInstance) Zone() string                         { return i.zone }
func (i *serviceInstance) Endpoint() string                     { return i.endpoint }
func (i *serviceInstance) SetService(s string) ServiceInstance  { i.service = s; return i }
func (i *serviceInstance) SetID(id string) ServiceInstance      { i.id = id; return i }
func (i *serviceInstance) SetZone(z string) ServiceInstance     { i.zone = z; return i }
func (i *serviceInstance) SetEndpoint(e string) ServiceInstance { i.endpoint = e; return i }

type advertisement struct {
	id       string
	service  string
	endpoint string
	health   func() error
}

func (a *advertisement) ID() string                             { return a.id }
func (a *advertisement) Service() string                        { return a.service }
func (a *advertisement) Endpoint() string                       { return a.endpoint }
func (a *advertisement) Health() func() error                   { return a.health }
func (a *advertisement) SetID(id string) Advertisement          { a.id = id; return a }
func (a *advertisement) SetService(s string) Advertisement      { a.service = s; return a }
func (a *advertisement) SetEndpoint(e string) Advertisement     { a.endpoint = e; return a }
func (a *advertisement) SetHealth(h func() error) Advertisement { a.health = h; return a }

type queryOptions struct {
	zones            []string
	includeUnhealthy bool
}

func (qo *queryOptions) Zones() []string                         { return qo.zones }
func (qo *queryOptions) IncludeUnhealthy() bool                  { return qo.includeUnhealthy }
func (qo *queryOptions) SetZones(z []string) QueryOptions        { qo.zones = z; return qo }
func (qo *queryOptions) SetIncludeUnhealthy(h bool) QueryOptions { qo.includeUnhealthy = h; return qo }
<|endoftext|>


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

package redo

import (
	"fmt"
	"strings"
)

const (
	// TASK_PREFIX is a marker for scripts that don't produce content.
	TASK_PREFIX = '@'

	// REDO_DIR names the hidden directory used for data and configuration.
	REDO_DIR = ".redo"

	// REDO_DIR_ENV_NAME names the environment variable for the REDO_DIR hidden directory.
	REDO_DIR_ENV_NAME = "REDO_DIR"

	REDO_PARENT_ENV_NAME = "REDO_PARENT"

	// KEY_SEPARATOR is used to join the parts of the database key.
	KEY_SEPARATOR = "/"

	// AUTO marks system generated event records.
	AUTO = "auto"
)

// Dependency Relations
type Relation string

const (
	SATISFIES Relation = "satisfies"
	REQUIRES  Relation = "requires"
)

// Directory creation permission mode
const DIR_PERM = 0755

// makeKey returns a database key consisting of provided arguments, prefixed
// with the path hash.
func (f *File) makeKey(subkeys ...interface{}) (val string) {

	keys := make([]string, len(subkeys)+1)

	keys[0] = string(f.PathHash)

	for i, value := range subkeys {
		keys[i+1] = fmt.Sprintf("%s", value)
	}

	return strings.Join(keys, KEY_SEPARATOR)
}

func (f *File) metadataKey() string {
	return f.makeKey("METADATA")
}

func (f *File) mustRebuildKey() string {
	return f.makeKey("REBUILD")
}

func RecordRelation(dependent *File, target *File, event Event, m *Metadata) error {
	if err := dependent.PutPrerequisite(event, target.PathHash, target.AsPrerequisite(dependent.RootDir, m)); err != nil {
		return err
	}

	if err := target.PutDependency(event, dependent.PathHash, dependent.AsDependent(target.RootDir)); err != nil {
		return err
	}

	return nil
}
<commit_msg>gofmt<commit_after>// Copyright 2014 Gyepi Sam. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package redo

import (
	"fmt"
	"strings"
)

const (
	// TASK_PREFIX is a marker for scripts that don't produce content.
	TASK_PREFIX = '@'

	// REDO_DIR names the hidden directory used for data and configuration.
	REDO_DIR = ".redo"

	// REDO_DIR_ENV_NAME names the environment variable for the REDO_DIR hidden directory.
	REDO_DIR_ENV_NAME = "REDO_DIR"

	REDO_PARENT_ENV_NAME = "REDO_PARENT"

	// KEY_SEPARATOR is used to join the parts of the database key.
	KEY_SEPARATOR = "/"

	// AUTO marks system generated event records.
	AUTO = "auto"
)

// Dependency Relations
type Relation string

const (
	SATISFIES Relation = "satisfies"
	REQUIRES  Relation = "requires"
)

// Directory creation permission mode
const DIR_PERM = 0755

// makeKey returns a database key consisting of provided arguments, joined with KEY_SEPARATOR
// and prefixed with the PathHash.
func (f *File) makeKey(subkeys ...interface{}) (val string) {

	keys := make([]string, len(subkeys)+1)

	keys[0] = string(f.PathHash)

	for i, value := range subkeys {
		keys[i+1] = fmt.Sprintf("%s", value)
	}

	return strings.Join(keys, KEY_SEPARATOR)
}

func (f *File) metadataKey() string {
	return f.makeKey("METADATA")
}

func (f *File) mustRebuildKey() string {
	return f.makeKey("REBUILD")
}

func RecordRelation(dependent *File, target *File, event Event, m *Metadata) error {
	if err := dependent.PutPrerequisite(event, target.PathHash, target.AsPrerequisite(dependent.RootDir, m)); err != nil {
		return err
	}

	if err := target.PutDependency(event, dependent.PathHash, dependent.AsDependent(target.RootDir)); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"regexp"
)

func validBarcode8(s string) bool {
  re := regexp.MustCompile("^[A-Z0-9]{8,8}$")
  return re.MatchString(s))
}

func validBarcode7(s string) bool {
  re := regexp.MustCompile("^[A-Z0-9]{7,7}$")
  return re.MatchString(s))
}

func validCarton20(s string) bool {
  re := regexp.MustCompile("^[0-9]{20,20}$")
  return re.MatchString(s))
}

<commit_msg>barcode validation<commit_after>package common

import (
	"regexp"
)

func validBarcode8(s string) bool {
	re := regexp.MustCompile("^[A-Z0-9]{8,8}$")
	return re.MatchString(s)
}

func validBarcode7(s string) bool {
	re := regexp.MustCompile("^[A-Z0-9]{7,7}$")
	return re.MatchString(s)
}

func validCarton20(s string) bool {
	re := regexp.MustCompile("^[0-9]{20,20}$")
	return re.MatchString(s)
}
<|endoftext|>


<commit_before>// Copyright 2014 Brian J. Downs
//
// 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 formatifier is a library to easily format strings in a user defined
// and predefined manner.
package formatifier

import (
	"crypto/rand"
	"regexp"
	"strings"
)

const (
	lengthError = "ERROR: String not long enough to convert."
	pirateLink  = "http://www.isithackday.com/arrpi.php?text=%s"
)

// Type to hold user input string.
type formatifier struct {
	theString string
}

// New will create a new instance of the String object.
func New(s string) *formatifier {
	return &formatifier{theString: s}
}

// Makes the user entered string lower case.
func (f *formatifier) makeLower() {
	f.theString = strings.ToLower(f.theString)
}

// Remove any non digit characters from the string.
func (f *formatifier) removeNonDigits() {
	rp := regexp.MustCompile(`\D`)
	f.theString = rp.ReplaceAllString(f.theString, "")
}

// Remove all non word characters.
func (f *formatifier) removeNonWordChars() {
	if len(f.theString) > 0 {
		rp := regexp.MustCompile(`\W|\s|_`)
		f.theString = rp.ReplaceAllString(f.theString, "")
	}
}

func (f *formatifier) urlEncodeSpaces() {
	rp := regexp.MustCompile(`\s`)
	f.theString = rp.ReplaceAllString(f.theString, "%20")
}

func randomSelect(anySlice []int) int {
	var tmpIndex int
	length := len(anySlice)
	randBytes := make([]byte, length)
	if _, err := rand.Read(randBytes); err == nil {
		tmpIndex = int(randBytes[0]) % length
	}
	return anySlice[tmpIndex]
}

// Leet speak map of string slices
var leet = map[string][]string{
	"leet":     []string{"1337"},
	"the":      []string{"teh"},
	"cool":     []string{"kewl"},
	"dude":     []string{"d00d"},
	"you":      []string{"u"},
	"noob":     []string{"n00b"},
	"noobs":    []string{"n00bs"},
	"own":      []string{"pwn"},
	"owned":    []string{"pwned"},
	"rocks":    []string{"roxx0rs"},
	"exploits": []string{"sploitz"},
	"woot":     []string{"w00t"},
	"hacker":   []string{"hax0r"},
	"hackers":  []string{"hax0rz"},
	"a":        []string{"4", "@"},
	"b":        []string{"8", "]3", "]8", "|3", "|8", "13"},
	"c":        []string{"(", "{"},
	"d":        []string{")", "[}", "|)", "|}", "|>"},
	"e":        []string{"3"},
	"f":        []string{"|=", "ph"},
	"g":        []string{"6", "9", "&"},
	"h":        []string{"#", "|-|"},
	"i":        []string{"1", "!", "|"},
	"j":        []string{"_|", "u|"},
	"k":        []string{"|<", "|{"},
	"l":        []string{"|", "1", "|_"},
	"m":        []string{"/\\/\\", "|\\/|"},
	"n":        []string{"/\\/", "|\\|"},
	"o":        []string{"0", "()"},
	"p":        []string{"|D", "|*"},
	"q":        []string{"(,)", "O\\", "[]\\"},
	"r":        []string{"|2", "|?", "][2"},
	"s":        []string{"5", "$"},
	"t":        []string{"7", "+"},
	"u":        []string{"(_)", "|_|"},
	"v":        []string{"\\/", "\\\\//"},
	"w":        []string{"\\/\\/", "|/\\|", "VV"},
	"x":        []string{"><", "}{"},
	"y":        []string{"'/", "%"},
	"z":        []string{"2", "7_"},
}

var irsa = map[string]string{
	" ": " | ",
	"a": "alfa",
	"b": "bravo",
	"c": "charlie",
	"d": "delta",
	"e": "echo",
	"f": "foxtrot",
	"g": "golf",
	"h": "hotel",
	"i": "india",
	"j": "juliet",
	"k": "kilo",
	"l": "lima",
	"m": "mike",
	"n": "november",
	"o": "oscar",
	"p": "papa",
	"q": "quebec",
	"r": "romeo",
	"s": "sierra",
	"t": "tango",
	"u": "uniform",
	"v": "victor",
	"w": "whiskey",
	"x": "x-ray",
	"y": "yankee",
	"z": "zulu",
}

var morse = map[string]string{
	"a":  ". _",
	"b":  "_ . . .",
	"c":  "_ . _ .",
	"d":  "_ . .",
	"e":  ".",
	"f":  ". . _ .",
	"g":  "_ _ .",
	"h":  ". . . .",
	"i":  ". .",
	"j":  ". _ _ _",
	"k":  "_ . _",
	"l":  ". _ . .",
	"m":  "_ _",
	"n":  "_ .",
	"o":  "_ _ _",
	"p":  ". _ _ .",
	"q":  "_ _ . _",
	"r":  ". _ .",
	"s":  ". . .",
	"t":  "_",
	"u":  ". . _",
	"v":  ". . . _",
	"w":  ". _ _",
	"x":  "_ . . _",
	"y":  "_ . _ _",
	"z":  "_ _ . .",
	"0":  "_ _ _ _ _",
	"1":  ". _ _ _ _",
	"2":  ". . _ _ _",
	"3":  ". . . _ _",
	"4":  ". . . . _",
	"5":  ". . . . .",
	"6":  "_ . . . .",
	"7":  "_ _ . . .",
	"8":  "_ _ _ . .",
	"9":  "_ _ _ _ .",
	".":  "·–·–·– ",
	",":  "––··–– ",
	"?":  "··––·· ",
	"'":  "·––––· ",
	"!":  "–·–·–– ",
	"/":  "–··–· ",
	"(":  "–·––· ",
	")":  "–·––·– ",
	"&":  "·–··· ",
	":":  "–––··· ",
	";":  "–·–·–· ",
	"=":  "–···– ",
	"+":  "·–·–· ",
	"–":  "–····– ",
	"_":  "··––·– ",
	"\"": "·–··–· ",
	"$":  "···–··– ",
	"@":  "·––·–· ",
}
<commit_msg>small change<commit_after>// Copyright 2014 Brian J. Downs
//
// 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 formatifier is a library to easily format strings in a user defined
// and predefined manner.
package formatifier

import (
	"crypto/rand"
	"regexp"
	"strings"
)

const (
	//lengthError = "ERROR: String not long enough to convert."
	lengthError = "ERROR: String not long enough to convert."
	pirateLink  = "http://www.isithackday.com/arrpi.php?text=%s"
)

// Type to hold user input string.
type formatifier struct {
	theString string
}

// New will create an instance of the String object.
func New(s string) *formatifier {
	return &formatifier{theString: s}
}

// Makes the user entered string lower case.
func (f *formatifier) makeLower() {
	f.theString = strings.ToLower(f.theString)
}

// Remove any non digit characters from the string.
func (f *formatifier) removeNonDigits() {
	rp := regexp.MustCompile(`\D`)
	f.theString = rp.ReplaceAllString(f.theString, "")
}

// Remove all non word characters.
func (f *formatifier) removeNonWordChars() {
	if len(f.theString) > 0 {
		rp := regexp.MustCompile(`\W|\s|_`)
		f.theString = rp.ReplaceAllString(f.theString, "")
	}
}

func (f *formatifier) urlEncodeSpaces() {
	rp := regexp.MustCompile(`\s`)
	f.theString = rp.ReplaceAllString(f.theString, "%20")
}

func randomSelect(anySlice []int) int {
	var tmpIndex int
	length := len(anySlice)
	randBytes := make([]byte, length)
	if _, err := rand.Read(randBytes); err == nil {
		tmpIndex = int(randBytes[0]) % length
	}
	return anySlice[tmpIndex]
}

// Leet speak map of string slices
var leet = map[string][]string{
	"leet":     []string{"1337"},
	"the":      []string{"teh"},
	"cool":     []string{"kewl"},
	"dude":     []string{"d00d"},
	"you":      []string{"u"},
	"noob":     []string{"n00b"},
	"noobs":    []string{"n00bs"},
	"own":      []string{"pwn"},
	"owned":    []string{"pwned"},
	"rocks":    []string{"roxx0rs"},
	"exploits": []string{"sploitz"},
	"woot":     []string{"w00t"},
	"hacker":   []string{"hax0r"},
	"hackers":  []string{"hax0rz"},
	"a":        []string{"4", "@"},
	"b":        []string{"8", "]3", "]8", "|3", "|8", "13"},
	"c":        []string{"(", "{"},
	"d":        []string{")", "[}", "|)", "|}", "|>"},
	"e":        []string{"3"},
	"f":        []string{"|=", "ph"},
	"g":        []string{"6", "9", "&"},
	"h":        []string{"#", "|-|"},
	"i":        []string{"1", "!", "|"},
	"j":        []string{"_|", "u|"},
	"k":        []string{"|<", "|{"},
	"l":        []string{"|", "1", "|_"},
	"m":        []string{"/\\/\\", "|\\/|"},
	"n":        []string{"/\\/", "|\\|"},
	"o":        []string{"0", "()"},
	"p":        []string{"|D", "|*"},
	"q":        []string{"(,)", "O\\", "[]\\"},
	"r":        []string{"|2", "|?", "][2"},
	"s":        []string{"5", "$"},
	"t":        []string{"7", "+"},
	"u":        []string{"(_)", "|_|"},
	"v":        []string{"\\/", "\\\\//"},
	"w":        []string{"\\/\\/", "|/\\|", "VV"},
	"x":        []string{"><", "}{"},
	"y":        []string{"'/", "%"},
	"z":        []string{"2", "7_"},
}

var irsa = map[string]string{
	" ": " | ",
	"a": "alfa",
	"b": "bravo",
	"c": "charlie",
	"d": "delta",
	"e": "echo",
	"f": "foxtrot",
	"g": "golf",
	"h": "hotel",
	"i": "india",
	"j": "juliet",
	"k": "kilo",
	"l": "lima",
	"m": "mike",
	"n": "november",
	"o": "oscar",
	"p": "papa",
	"q": "quebec",
	"r": "romeo",
	"s": "sierra",
	"t": "tango",
	"u": "uniform",
	"v": "victor",
	"w": "whiskey",
	"x": "x-ray",
	"y": "yankee",
	"z": "zulu",
}

var morse = map[string]string{
	"a":  ". _",
	"b":  "_ . . .",
	"c":  "_ . _ .",
	"d":  "_ . .",
	"e":  ".",
	"f":  ". . _ .",
	"g":  "_ _ .",
	"h":  ". . . .",
	"i":  ". .",
	"j":  ". _ _ _",
	"k":  "_ . _",
	"l":  ". _ . .",
	"m":  "_ _",
	"n":  "_ .",
	"o":  "_ _ _",
	"p":  ". _ _ .",
	"q":  "_ _ . _",
	"r":  ". _ .",
	"s":  ". . .",
	"t":  "_",
	"u":  ". . _",
	"v":  ". . . _",
	"w":  ". _ _",
	"x":  "_ . . _",
	"y":  "_ . _ _",
	"z":  "_ _ . .",
	"0":  "_ _ _ _ _",
	"1":  ". _ _ _ _",
	"2":  ". . _ _ _",
	"3":  ". . . _ _",
	"4":  ". . . . _",
	"5":  ". . . . .",
	"6":  "_ . . . .",
	"7":  "_ _ . . .",
	"8":  "_ _ _ . .",
	"9":  "_ _ _ _ .",
	".":  "·–·–·– ",
	",":  "––··–– ",
	"?":  "··––·· ",
	"'":  "·––––· ",
	"!":  "–·–·–– ",
	"/":  "–··–· ",
	"(":  "–·––· ",
	")":  "–·––·– ",
	"&":  "·–··· ",
	":":  "–––··· ",
	";":  "–·–·–· ",
	"=":  "–···– ",
	"+":  "·–·–· ",
	"–":  "–····– ",
	"_":  "··––·– ",
	"\"": "·–··–· ",
	"$":  "···–··– ",
	"@":  "·––·–· ",
}
<|endoftext|>