<commit_before>package sup

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"gopkg.in/yaml.v2"
)

// EnvVar represents an environment variable
type EnvVar struct {
	Key   string
	Value string
}

func (e EnvVar) String() string {
	return e.Key + `=` + e.Value
}

// AsExport returns the environment variable as a bash export statement
func (e EnvVar) AsExport() string {
	return `export ` + e.Key + `="` + e.Value + `";`
}

// EnvList is a list of environment variables that maps to a YAML map,
// but maintains order, enabling late variables to reference early variables.
type EnvList []EnvVar

func (e *EnvList) UnmarshalYAML(unmarshal func(interface{}) error) error {
	items := []yaml.MapItem{}

	err := unmarshal(&items)
	if err != nil {
		return err
	}

	*e = make(EnvList, 0, len(items))

	for _, v := range items {
		e.Set(fmt.Sprintf("%v", v.Key), fmt.Sprintf("%v", v.Value))
	}

	return nil
}

// Set key to be equal value in this list.
func (e *EnvList) Set(key, value string) {
	// if key exists result will be redefinition
	*e = append(*e, EnvVar{
		Key:   key,
		Value: value,
	})
}

// Supfile represents the Stack Up configuration YAML file.
type Supfile struct {
	Networks map[string]Network  `yaml:"networks"`
	Commands map[string]Command  `yaml:"commands"`
	Targets  map[string][]string `yaml:"targets"`
	Env      EnvList             `yaml:"env"`
	Version  string              `yaml:"version"`
}

// Network is group of hosts with extra custom env vars.
type Network struct {
	Env       EnvList  `yaml:"env"`
	Inventory string   `yaml:"inventory"`
	Hosts     []string `yaml:"hosts"`
	Bastion   string   `yaml:"bastion"` // Jump host for the environment
}

// Command represents command(s) to be run remotely.
type Command struct {
	Name   string   `yaml:"-"`      // Command name.
	Desc   string   `yaml:"desc"`   // Command description.
	Local  string   `yaml:"local"`  // Command(s) to be run locally.
	Run    string   `yaml:"run"`    // Command(s) to be run remotelly.
	Script string   `yaml:"script"` // Load command(s) from script and run it remotelly.
	Upload []Upload `yaml:"upload"` // See Upload struct.
	Stdin  bool     `yaml:"stdin"`  // Attach localhost STDOUT to remote commands' STDIN?
	Once   bool     `yaml:"once"`   // The command should be run "once" (on one host only).
	Serial int      `yaml:"serial"` // Max number of clients processing a task in parallel.

	// API backward compatibility. Will be deprecated in v1.0.
	RunOnce bool `yaml:"run_once"` // The command should be run once only.
}

// Upload represents file copy operation from localhost Src path to Dst
// path of every host in a given Network.
type Upload struct {
	Src string `yaml:"src"`
	Dst string `yaml:"dst"`
	Exc string `yaml:"exclude"`
}

// NewSupfile parses configuration file and returns Supfile or error.
func NewSupfile(file string) (*Supfile, error) {
	var conf Supfile
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(data, &conf)
	if err != nil {
		return nil, err
	}

	// API backward compatibility. Will be deprecated in v1.0.
	switch conf.Version {
	case "":
		conf.Version = "0.1"
		fallthrough
	case "0.1":
		for _, cmd := range conf.Commands {
			if cmd.RunOnce {
				return nil, errors.New("command.run_once is not supported in Supfile v" + conf.Version)
			}
		}
		fallthrough
	case "0.2":
		for _, cmd := range conf.Commands {
			if cmd.Once {
				return nil, errors.New("command.once is not supported in Supfile v" + conf.Version)
			}
			if cmd.Local != "" {
				return nil, errors.New("command.local is not supported in Supfile v" + conf.Version)
			}
			if cmd.Serial != 0 {
				return nil, errors.New("command.serial is not supported in Supfile v" + conf.Version)
			}
		}
		for _, network := range conf.Networks {
			if network.Inventory != "" {
				return nil, errors.New("network.inventory is not supported in Supfile v" + conf.Version)
			}
		}
		fallthrough
	case "0.3":
		var warning string
		for key, cmd := range conf.Commands {
			if cmd.RunOnce {
				warning = "Warning: command.run_once was deprecated by command.once in Supfile v" + conf.Version + "\n"
				cmd.Once = true
				conf.Commands[key] = cmd
			}
		}
		if warning != "" {
			fmt.Fprintf(os.Stderr, warning)
		}
	default:
		return nil, errors.New("unsupported version, please update sup by `go get -u github.com/pressly/sup`")
	}

	for i, network := range conf.Networks {
		hosts, err := network.ParseInventory()
		if err != nil {
			return nil, err
		}
		network.Hosts = append(network.Hosts, hosts...)
		conf.Networks[i] = network
	}

	return &conf, nil
}

// ParseInventory runs the inventory command, if provided, and appends
// the command's output lines to the manually defined list of hosts.
func (n Network) ParseInventory() ([]string, error) {
	if n.Inventory == "" {
		return nil, nil
	}

	cmd := exec.Command("/bin/sh", "-c", n.Inventory)
	cmd.Stderr = os.Stderr
	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var hosts []string
	buf := bytes.NewBuffer(output)
	for {
		host, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		host = strings.TrimSpace(host)
		// skip empty lines and comments
		if host == "" || host[:1] == "#" {
			continue
		}

		hosts = append(hosts, host)
	}
	return hosts, nil
}
<commit_msg>Override the old value instead of relying on order<commit_after>package sup

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"gopkg.in/yaml.v2"
)

// EnvVar represents an environment variable
type EnvVar struct {
	Key   string
	Value string
}

func (e EnvVar) String() string {
	return e.Key + `=` + e.Value
}

// AsExport returns the environment variable as a bash export statement
func (e EnvVar) AsExport() string {
	return `export ` + e.Key + `="` + e.Value + `";`
}

// EnvList is a list of environment variables that maps to a YAML map,
// but maintains order, enabling late variables to reference early variables.
type EnvList []EnvVar

func (e *EnvList) UnmarshalYAML(unmarshal func(interface{}) error) error {
	items := []yaml.MapItem{}

	err := unmarshal(&items)
	if err != nil {
		return err
	}

	*e = make(EnvList, 0, len(items))

	for _, v := range items {
		e.Set(fmt.Sprintf("%v", v.Key), fmt.Sprintf("%v", v.Value))
	}

	return nil
}

// Set key to be equal value in this list.
func (e *EnvList) Set(key, value string) {

	for _, v := range *e {
		if v.Key == key {
			v.Value = value
			return
		}
	}

	*e = append(*e, EnvVar{
		Key:   key,
		Value: value,
	})
}

// Supfile represents the Stack Up configuration YAML file.
type Supfile struct {
	Networks map[string]Network  `yaml:"networks"`
	Commands map[string]Command  `yaml:"commands"`
	Targets  map[string][]string `yaml:"targets"`
	Env      EnvList             `yaml:"env"`
	Version  string              `yaml:"version"`
}

// Network is group of hosts with extra custom env vars.
type Network struct {
	Env       EnvList  `yaml:"env"`
	Inventory string   `yaml:"inventory"`
	Hosts     []string `yaml:"hosts"`
	Bastion   string   `yaml:"bastion"` // Jump host for the environment
}

// Command represents command(s) to be run remotely.
type Command struct {
	Name   string   `yaml:"-"`      // Command name.
	Desc   string   `yaml:"desc"`   // Command description.
	Local  string   `yaml:"local"`  // Command(s) to be run locally.
	Run    string   `yaml:"run"`    // Command(s) to be run remotelly.
	Script string   `yaml:"script"` // Load command(s) from script and run it remotelly.
	Upload []Upload `yaml:"upload"` // See Upload struct.
	Stdin  bool     `yaml:"stdin"`  // Attach localhost STDOUT to remote commands' STDIN?
	Once   bool     `yaml:"once"`   // The command should be run "once" (on one host only).
	Serial int      `yaml:"serial"` // Max number of clients processing a task in parallel.

	// API backward compatibility. Will be deprecated in v1.0.
	RunOnce bool `yaml:"run_once"` // The command should be run once only.
}

// Upload represents file copy operation from localhost Src path to Dst
// path of every host in a given Network.
type Upload struct {
	Src string `yaml:"src"`
	Dst string `yaml:"dst"`
	Exc string `yaml:"exclude"`
}

// NewSupfile parses configuration file and returns Supfile or error.
func NewSupfile(file string) (*Supfile, error) {
	var conf Supfile
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(data, &conf)
	if err != nil {
		return nil, err
	}

	// API backward compatibility. Will be deprecated in v1.0.
	switch conf.Version {
	case "":
		conf.Version = "0.1"
		fallthrough
	case "0.1":
		for _, cmd := range conf.Commands {
			if cmd.RunOnce {
				return nil, errors.New("command.run_once is not supported in Supfile v" + conf.Version)
			}
		}
		fallthrough
	case "0.2":
		for _, cmd := range conf.Commands {
			if cmd.Once {
				return nil, errors.New("command.once is not supported in Supfile v" + conf.Version)
			}
			if cmd.Local != "" {
				return nil, errors.New("command.local is not supported in Supfile v" + conf.Version)
			}
			if cmd.Serial != 0 {
				return nil, errors.New("command.serial is not supported in Supfile v" + conf.Version)
			}
		}
		for _, network := range conf.Networks {
			if network.Inventory != "" {
				return nil, errors.New("network.inventory is not supported in Supfile v" + conf.Version)
			}
		}
		fallthrough
	case "0.3":
		var warning string
		for key, cmd := range conf.Commands {
			if cmd.RunOnce {
				warning = "Warning: command.run_once was deprecated by command.once in Supfile v" + conf.Version + "\n"
				cmd.Once = true
				conf.Commands[key] = cmd
			}
		}
		if warning != "" {
			fmt.Fprintf(os.Stderr, warning)
		}
	default:
		return nil, errors.New("unsupported version, please update sup by `go get -u github.com/pressly/sup`")
	}

	for i, network := range conf.Networks {
		hosts, err := network.ParseInventory()
		if err != nil {
			return nil, err
		}
		network.Hosts = append(network.Hosts, hosts...)
		conf.Networks[i] = network
	}

	return &conf, nil
}

// ParseInventory runs the inventory command, if provided, and appends
// the command's output lines to the manually defined list of hosts.
func (n Network) ParseInventory() ([]string, error) {
	if n.Inventory == "" {
		return nil, nil
	}

	cmd := exec.Command("/bin/sh", "-c", n.Inventory)
	cmd.Stderr = os.Stderr
	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var hosts []string
	buf := bytes.NewBuffer(output)
	for {
		host, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		host = strings.TrimSpace(host)
		// skip empty lines and comments
		if host == "" || host[:1] == "#" {
			continue
		}

		hosts = append(hosts, host)
	}
	return hosts, nil
}
<|endoftext|>


<commit_before>package server

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	ConnectionType = "tcp"
)

type Networker struct {
	s *Server
}

func (n *Networker) Handle(conn net.Conn, id uint) {
	rc := &Conn{
		NConn: conn,
		ID:    id,

		log: n.s.log,
	}

	c := NewComConn(rc)

	for {
		cc, err := c.Read()
		if err != nil {
			c.Raw.log.Println("Couldn't create child, closing connection:", err)
			c.Raw.Close()
			return
		}

		go func(cc *ChildConn) {
			com, err := cc.Com()
			if err != nil {
				c.Raw.log.Println("Couldn't read command, closing connection:", err)
				c.Raw.Close()
				return
			}

			err = n.s.Room.Handle(com.Command, cc)
			if err != nil {
				c.Raw.log.Println("Couldn't handle com:", err)
			}

		}(cc)

		time.Sleep(time.Second / time.Duration(n.s.Opts.ReadSpeed))
	}
}

type ChildConn struct {
	buf *bytes.Buffer
	p   *ComConn
}

func NewChildConn(b *bytes.Buffer, p *ComConn) *ChildConn {
	return &ChildConn{
		buf: b,
		p:   p,
	}
}

func (cc *ChildConn) Com() (Communication, error) {
	com := Communication{}

	err := json.Unmarshal(cc.buf.Bytes(), &com)
	return com, err
}

func (cc *ChildConn) Read(p Preparer) error {
	if cc.buf.Len() == 0 {
		return ErrEmptyBuffer
	}

	err := json.Unmarshal(cc.buf.Bytes(), p)
	if err != nil {
		return err
	}

	cc.buf.Reset()
	return nil
}

func (cc *ChildConn) Send(cmd string, v Preparer) error {
	return cc.Parent().Send(cmd, v)
}

func (cc *ChildConn) Parent() *ComConn {
	return cc.p
}

func (cc *ChildConn) log() *log.Logger {
	return cc.Parent().log()
}

type ComConn struct {
	Raw *Conn

	delayers     []chan struct{}
	delayersLock sync.RWMutex
	sendLock     sync.RWMutex
}

func NewComConn(c *Conn) *ComConn {
	cc := &ComConn{
		Raw: c,

		delayers: make([]chan struct{}, 0),
	}

	return cc
}

func (c *ComConn) Read() (*ChildConn, error) {
	buf, err := c.Raw.ReadRaw()
	if err != nil {
		return nil, err
	}

	return NewChildConn(buf, c), nil
}

// NOTE: Do note use in a configuration!
func (c *ComConn) ExpectAndRead(cmd string, v Preparer) error {
	cc, err := c.Read()
	if err != nil {
		return err
	}

	com, err := cc.Com()
	if err != nil {
		return err
	}

	if com.Command != cmd {
		return ErrUnexpectedCom
	}

	err = cc.Read(v)

	return err
}

func (c *ComConn) Send(cmd string, v Preparer) error {
	ch := c.wait()

	c.sendLock.Lock()
	err := c.Raw.Send(cmd, v)
	c.sendLock.Unlock()

	ch <- struct{}{}

	return err
}

func (c *ComConn) Done() {
	c.delayersLock.Lock()

	for _, ch := range c.delayers {
		ch <- struct{}{}
		<-ch
		close(ch)
	}

	c.delayers = c.delayers[0:0]

	c.delayersLock.Unlock()
}

func (c *ComConn) wait() chan struct{} {
	c.delayersLock.Lock()
	ch := make(chan struct{})

	c.delayers = append(c.delayers, ch)

	c.delayersLock.Unlock()

	<-ch

	return ch
}

func (c *ComConn) log() *log.Logger {
	return c.Raw.log
}

type Conn struct {
	NConn net.Conn
	ID    uint

	connBuf  *bufio.Reader
	connLock sync.RWMutex
	log      *log.Logger
}

func (c *Conn) ReadRaw() (*bytes.Buffer, error) {
	if c.connBuf == nil {
		c.connBuf = bufio.NewReader(c.NConn)
	}

	c.connLock.Lock()
	defer c.connLock.Unlock()

	lenSli, err := c.connBuf.ReadSlice('\n')
	if err != nil {
		return nil, err
	}

	lenSli = bytes.TrimSpace(lenSli)
	l, err := strconv.Atoi(string(lenSli))
	if err != nil {
		return nil, err
	}

	buf := make([]byte, l)
	_, err = c.connBuf.Read(buf)
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(buf), nil
}

func (c *Conn) Read(v Preparer) error {
	r, err := c.ReadRaw()
	if err != nil {
		return err
	}

	err = json.Unmarshal(r.Bytes(), v)
	if err != nil {
		return err
	}

	return nil
}

func (c *Conn) ReadCom() (Communication, error) {
	com := Communication{}

	err := c.Read(&com)
	return com, err
}

func (c *Conn) Send(cmd string, v Preparer) error {
	v.Prepare(cmd)

	out, err := json.Marshal(v)
	if err != nil {
		return err
	}

	return c.SendRaw(bytes.NewBuffer(out))
}

func (c *Conn) SendRaw(r io.Reader) error {
	rBuf := &bytes.Buffer{}
	_, err := io.Copy(rBuf, r)
	if err != nil {
		return err
	}

	buf := &bytes.Buffer{}

	_, err = buf.WriteString(fmt.Sprintf("%v\n", rBuf.Len()))
	if err != nil {
		return err
	}

	_, err = rBuf.WriteTo(buf)
	if err != nil {
		return err
	}

	_, err = io.Copy(c.NConn, buf)
	return err
}

func (c *Conn) SendRawString(s string) error {
	return c.SendRaw(strings.NewReader(s))
}

func (c *Conn) Close() error {
	return c.NConn.Close()
}

func (c *Conn) Expect(cmd string) error {
	com, err := c.ReadCom()
	if err != nil {
		return err
	}

	if com.Command != cmd {
		return ErrUnexpectedCom
	}

	return nil
}

func (c *Conn) ExpectAndRead(cmd string, v Preparer) error {
	err := c.Expect(cmd)
	if err != nil {
		return err
	}

	return c.Read(v)
}
<commit_msg>Add slightly prettier logging<commit_after>package server

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	ConnectionType = "tcp"
)

type Networker struct {
	s *Server
}

func (n *Networker) Handle(conn net.Conn, id uint) {
	rc := &Conn{
		NConn: conn,
		ID:    id,

		log: n.s.log,
	}

	c := NewComConn(rc)

	for {
		cc, err := c.Read()
		if err != nil {
			c.Raw.log.Println("Couldn't create child, closing connection:", err)
			c.Raw.Close()
			return
		}

		go func(cc *ChildConn) {
			com, err := cc.Com()
			if err != nil {
				c.Raw.log.Println("Couldn't read command, closing connection:", err)
				c.Raw.Close()
				return
			}

			err = n.s.Room.Handle(com.Command, cc)
			if err != nil {
				c.Raw.log.Printf("Couldn't handle com (%s): %v", com.Command, err)
			}

		}(cc)

		time.Sleep(time.Second / time.Duration(n.s.Opts.ReadSpeed))
	}
}

type ChildConn struct {
	buf *bytes.Buffer
	p   *ComConn
}

func NewChildConn(b *bytes.Buffer, p *ComConn) *ChildConn {
	return &ChildConn{
		buf: b,
		p:   p,
	}
}

func (cc *ChildConn) Com() (Communication, error) {
	com := Communication{}

	err := json.Unmarshal(cc.buf.Bytes(), &com)
	return com, err
}

func (cc *ChildConn) Read(p Preparer) error {
	if cc.buf.Len() == 0 {
		return ErrEmptyBuffer
	}

	err := json.Unmarshal(cc.buf.Bytes(), p)
	if err != nil {
		return err
	}

	cc.buf.Reset()
	return nil
}

func (cc *ChildConn) Send(cmd string, v Preparer) error {
	return cc.Parent().Send(cmd, v)
}

func (cc *ChildConn) Parent() *ComConn {
	return cc.p
}

func (cc *ChildConn) log() *log.Logger {
	return cc.Parent().log()
}

type ComConn struct {
	Raw *Conn

	delayers     []chan struct{}
	delayersLock sync.RWMutex
	sendLock     sync.RWMutex
}

func NewComConn(c *Conn) *ComConn {
	cc := &ComConn{
		Raw: c,

		delayers: make([]chan struct{}, 0),
	}

	return cc
}

func (c *ComConn) Read() (*ChildConn, error) {
	buf, err := c.Raw.ReadRaw()
	if err != nil {
		return nil, err
	}

	return NewChildConn(buf, c), nil
}

// NOTE: Do note use in a configuration!
func (c *ComConn) ExpectAndRead(cmd string, v Preparer) error {
	cc, err := c.Read()
	if err != nil {
		return err
	}

	com, err := cc.Com()
	if err != nil {
		return err
	}

	if com.Command != cmd {
		return ErrUnexpectedCom
	}

	err = cc.Read(v)

	return err
}

func (c *ComConn) Send(cmd string, v Preparer) error {
	ch := c.wait()

	c.sendLock.Lock()
	err := c.Raw.Send(cmd, v)
	c.sendLock.Unlock()

	ch <- struct{}{}

	return err
}

func (c *ComConn) Done() {
	c.delayersLock.Lock()

	for _, ch := range c.delayers {
		ch <- struct{}{}
		<-ch
		close(ch)
	}

	c.delayers = c.delayers[0:0]

	c.delayersLock.Unlock()
}

func (c *ComConn) wait() chan struct{} {
	c.delayersLock.Lock()
	ch := make(chan struct{})

	c.delayers = append(c.delayers, ch)

	c.delayersLock.Unlock()

	<-ch

	return ch
}

func (c *ComConn) log() *log.Logger {
	return c.Raw.log
}

type Conn struct {
	NConn net.Conn
	ID    uint

	connBuf  *bufio.Reader
	connLock sync.RWMutex
	log      *log.Logger
}

func (c *Conn) ReadRaw() (*bytes.Buffer, error) {
	if c.connBuf == nil {
		c.connBuf = bufio.NewReader(c.NConn)
	}

	c.connLock.Lock()
	defer c.connLock.Unlock()

	lenSli, err := c.connBuf.ReadSlice('\n')
	if err != nil {
		return nil, err
	}

	lenSli = bytes.TrimSpace(lenSli)
	l, err := strconv.Atoi(string(lenSli))
	if err != nil {
		return nil, err
	}

	buf := make([]byte, l)
	_, err = c.connBuf.Read(buf)
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(buf), nil
}

func (c *Conn) Read(v Preparer) error {
	r, err := c.ReadRaw()
	if err != nil {
		return err
	}

	err = json.Unmarshal(r.Bytes(), v)
	if err != nil {
		return err
	}

	return nil
}

func (c *Conn) ReadCom() (Communication, error) {
	com := Communication{}

	err := c.Read(&com)
	return com, err
}

func (c *Conn) Send(cmd string, v Preparer) error {
	v.Prepare(cmd)

	out, err := json.Marshal(v)
	if err != nil {
		return err
	}

	return c.SendRaw(bytes.NewBuffer(out))
}

func (c *Conn) SendRaw(r io.Reader) error {
	rBuf := &bytes.Buffer{}
	_, err := io.Copy(rBuf, r)
	if err != nil {
		return err
	}

	buf := &bytes.Buffer{}

	_, err = buf.WriteString(fmt.Sprintf("%v\n", rBuf.Len()))
	if err != nil {
		return err
	}

	_, err = rBuf.WriteTo(buf)
	if err != nil {
		return err
	}

	_, err = io.Copy(c.NConn, buf)
	return err
}

func (c *Conn) SendRawString(s string) error {
	return c.SendRaw(strings.NewReader(s))
}

func (c *Conn) Close() error {
	return c.NConn.Close()
}

func (c *Conn) Expect(cmd string) error {
	com, err := c.ReadCom()
	if err != nil {
		return err
	}

	if com.Command != cmd {
		return ErrUnexpectedCom
	}

	return nil
}

func (c *Conn) ExpectAndRead(cmd string, v Preparer) error {
	err := c.Expect(cmd)
	if err != nil {
		return err
	}

	return c.Read(v)
}
<|endoftext|>


<commit_before>// Package svg minifies SVG1.1 following the specifications at http://www.w3.org/TR/SVG11/.
package svg // import "github.com/tdewolff/minify/svg"

import (
	"io"

	"github.com/tdewolff/buffer"
	"github.com/tdewolff/minify"
	minifyCSS "github.com/tdewolff/minify/css"
	"github.com/tdewolff/parse"
	"github.com/tdewolff/parse/css"
	"github.com/tdewolff/parse/svg"
	"github.com/tdewolff/parse/xml"
)

var (
	ltBytes         = []byte("<")
	gtBytes         = []byte(">")
	voidBytes       = []byte("/>")
	isBytes         = []byte("=")
	spaceBytes      = []byte(" ")
	endBytes        = []byte("</")
	cdataStartBytes = []byte("<![CDATA[")
	cdataEndBytes   = []byte("]]>")
)

////////////////////////////////////////////////////////////////

type Minifier struct{}

func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
	return (&Minifier{}).Minify(m, w, r, params)
}

// Minify minifies SVG data, it reads from r and writes to w.
func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
	var tag svg.Hash
	defaultStyleType := "text/css"
	defaultInlineStyleType := "text/css;inline=1"

	attrMinifyBuffer := buffer.NewWriter(make([]byte, 0, 64))
	attrByteBuffer := make([]byte, 0, 64)

	l := xml.NewLexer(r)
	tb := NewTokenBuffer(l)
	for {
		t := *tb.Shift()
		if t.TokenType == xml.CDATAToken {
			var useCDATA bool
			if t.Data, useCDATA = xml.EscapeCDATAVal(&attrByteBuffer, t.Data); !useCDATA {
				t.TokenType = xml.TextToken
			}
		}
	SWITCH:
		switch t.TokenType {
		case xml.ErrorToken:
			if l.Err() == io.EOF {
				return nil
			}
			return l.Err()
		case xml.TextToken:
			t.Data = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.Data))
			if tag == svg.Style && len(t.Data) > 0 {
				if err := m.Minify(defaultStyleType, w, buffer.NewReader(t.Data)); err != nil {
					if err == minify.ErrNotExist { // no minifier, write the original
						if _, err := w.Write(t.Data); err != nil {
							return err
						}
					} else {
						return err
					}
				}
			} else if _, err := w.Write(t.Data); err != nil {
				return err
			}
		case xml.CDATAToken:
			if _, err := w.Write(cdataStartBytes); err != nil {
				return err
			}
			t.Data = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.Data))
			if tag == svg.Style && len(t.Data) > 0 {
				if err := m.Minify(defaultStyleType, w, buffer.NewReader(t.Data)); err != nil {
					if err == minify.ErrNotExist { // no minifier, write the original
						if _, err := w.Write(t.Data); err != nil {
							return err
						}
					} else {
						return err
					}
				}
			} else if _, err := w.Write(t.Data); err != nil {
				return err
			}
			if _, err := w.Write(cdataEndBytes); err != nil {
				return err
			}
		case xml.StartTagPIToken:
			for {
				if t := *tb.Shift(); t.TokenType == xml.StartTagClosePIToken || t.TokenType == xml.ErrorToken {
					break
				}
			}
		case xml.StartTagToken:
			tag = t.Hash
			if containerTagMap[t.Hash] { // skip empty containers
				i := 0
				for {
					next := tb.Peek(i)
					i++
					if next.TokenType == xml.EndTagToken && next.Hash == t.Hash || next.TokenType == xml.StartTagCloseVoidToken || next.TokenType == xml.ErrorToken {
						for j := 0; j < i; j++ {
							tb.Shift()
						}
						break SWITCH
					} else if next.TokenType != xml.AttributeToken && next.TokenType != xml.StartTagCloseToken {
						break
					}
				}
			} else if t.Hash == svg.Metadata {
				for {
					if next := *tb.Shift(); (next.TokenType == xml.EndTagToken || next.TokenType == xml.StartTagCloseVoidToken) && next.Hash == t.Hash || next.TokenType == xml.ErrorToken {
						break
					}
				}
				break
			} else if t.Hash == svg.Line || t.Hash == svg.Rect {
				// TODO: shape2path also for polygon and polyline
				// x1, y1, x2, y2 float64 := 0, 0, 0, 0
				// valid := true
				// i := 0
				// for {
				// 	next := tb.Peek(i)
				// 	i++
				// 	if next.TokenType != xml.AttributeToken {
				// 		break
				// 	}
				// 	v *int
				// 	attr := svg.ToHash(next.Data)
				// 	if tag == svg.Line {
				// 		if attr == svg.X1 {
				// 			v = &x1
				// 		} else if attr == svg.Y1 {
				// 			v = &y1
				// 		} else if attr == svg.X2 {
				// 			v = &x2
				// 		} else if attr == svg.Y2 {
				// 			v = &Y2
				// 		} else {
				// 			continue
				// 		}
				// 	} else if attr == svg.X { // rect
				// 		v = &x1
				// 	} else if attr == svg.Y {
				// 		v = &y1
				// 	} else if attr == svg.Width {
				// 		v = &x2
				// 	} else if attr == svg.Height {
				// 		v = &Y2
				// 	} else if attr == svg.Rx || attr == svg.Ry {
				// 		valid = false
				// 		break
				// 	} else {
				// 		continue
				// 	}

				// }
				// if valid {
				// 	t.Data = pathBytes
				// }
			}
			if _, err := w.Write(ltBytes); err != nil {
				return err
			}
			if _, err := w.Write(t.Data); err != nil {
				return err
			}
		case xml.AttributeToken:
			if len(t.AttrVal) < 2 {
				continue
			}
			val := parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.AttrVal[1 : len(t.AttrVal)-1]))
			if tag == svg.Svg && t.Hash == svg.Version {
				continue
			}

			if _, err := w.Write(spaceBytes); err != nil {
				return err
			}
			if _, err := w.Write(t.Data); err != nil {
				return err
			}
			if _, err := w.Write(isBytes); err != nil {
				return err
			}

			if tag == svg.Svg && t.Hash == svg.ContentStyleType {
				val = minify.ContentType(val)
				defaultStyleType = string(val)
				defaultInlineStyleType = defaultStyleType + ";inline=1"
			} else if t.Hash == svg.Style {
				attrMinifyBuffer.Reset()
				if m.Minify(defaultInlineStyleType, attrMinifyBuffer, buffer.NewReader(val)) == nil {
					val = attrMinifyBuffer.Bytes()
				}
			} else if t.Hash == svg.D {
				val = shortenPathData(val)
			} else if t.Hash == svg.ViewBox {
				j := 0
				newVal := val[:0]
				for i := 0; i < 4; i++ {
					if i != 0 {
						if j >= len(val) || val[j] != ' ' && val[j] != ',' {
							newVal = append(newVal, val[j:]...)
							break
						}
						newVal = append(newVal, ' ')
						j++
					}
					if dim, n := shortenDimension(val[j:]); n > 0 {
						newVal = append(newVal, dim...)
						j += n
					} else {
						newVal = append(newVal, val[j:]...)
						break
					}
				}
				val = newVal
			} else if colorAttrMap[t.Hash] && len(val) > 0 {
				parse.ToLower(val)
				if val[0] == '#' {
					if name, ok := minifyCSS.ShortenColorHex[string(val)]; ok {
						val = name
					} else if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
						val[2] = val[3]
						val[3] = val[5]
						val = val[:4]
					}
				} else if hex, ok := minifyCSS.ShortenColorName[css.ToHash(val)]; ok {
					val = hex
				} else if len(val) > 5 && parse.Equal(val[:4], []byte("rgb(")) && val[len(val)-1] == ')' {
					// TODO: handle rgb(x, y, z) and hsl(x, y, z)
				}
			} else if n, m := parse.Dimension(val); n+m == len(val) { // TODO: inefficient, temporary measure
				val, _ = shortenDimension(val)
			}

			// prefer single or double quotes depending on what occurs more often in value
			val = xml.EscapeAttrVal(&attrByteBuffer, val)
			if _, err := w.Write(val); err != nil {
				return err
			}
		case xml.StartTagCloseToken:
			next := tb.Peek(0)
			skipExtra := false
			if next.TokenType == xml.TextToken && parse.IsAllWhitespace(next.Data) {
				next = tb.Peek(1)
				skipExtra = true
			}
			if next.TokenType == xml.EndTagToken {
				// collapse empty tags to single void tag
				tb.Shift()
				if skipExtra {
					tb.Shift()
				}
				if _, err := w.Write(voidBytes); err != nil {
					return err
				}
			} else {
				if _, err := w.Write(gtBytes); err != nil {
					return err
				}
			}
		case xml.StartTagCloseVoidToken:
			if _, err := w.Write(voidBytes); err != nil {
				return err
			}
		case xml.EndTagToken:
			if _, err := w.Write(endBytes); err != nil {
				return err
			}
			if _, err := w.Write(t.Data); err != nil {
				return err
			}
			if _, err := w.Write(gtBytes); err != nil {
				return err
			}
		}
	}
}

func shortenPathData(b []byte) []byte {
	cmd := byte(0)
	prevDigit := false
	prevDigitRequiresSpace := true
	j := 0
	start := 0
	for i := 0; i < len(b); i++ {
		c := b[i]
		if c == ' ' || c == ',' || c == '\t' || c == '\n' || c == '\r' {
			if start != 0 {
				j += copy(b[j:], b[start:i])
			} else {
				j += i
			}
			start = i + 1
		} else if n := parse.Number(b[i:]); n > 0 {
			if start != 0 {
				j += copy(b[j:], b[start:i])
			} else {
				j += i
			}
			num := minify.Number(b[i : i+n])
			if prevDigit && (num[0] >= '0' && num[0] <= '9' || num[0] == '.' && prevDigitRequiresSpace) {
				b[j] = ' '
				j++
			}
			prevDigit = true
			prevDigitRequiresSpace = true
			for i := 0; i < len(num); i++ {
				c := num[i]
				if c == '.' || c == 'e' || c == 'E' {
					prevDigitRequiresSpace = false
					break
				}
			}
			j += copy(b[j:], num)
			start = i + n
			i += n - 1
		} else {
			if cmd == c {
				if start != 0 {
					j += copy(b[j:], b[start:i])
				} else {
					j += i
				}
				start = i + 1
			} else {
				cmd = c
				prevDigit = false
			}
		}
	}
	if start != 0 {
		j += copy(b[j:], b[start:])
		return b[:j]
	}
	return b
}

func shortenDimension(b []byte) ([]byte, int) {
	if n, m := parse.Dimension(b); n > 0 {
		unit := b[n : n+m]
		b = minify.Number(b[:n])
		if len(b) != 1 || b[0] != '0' {
			if m == 2 && unit[0] == 'p' && unit[1] == 'x' {
				unit = nil
			} else if m > 1 { // only percentage is length 1
				parse.ToLower(unit)
			}
			b = append(b, unit...)
		}
		return b, n + m
	}
	return b, 0
}
<commit_msg>Use new cdata escape return<commit_after>// Package svg minifies SVG1.1 following the specifications at http://www.w3.org/TR/SVG11/.
package svg // import "github.com/tdewolff/minify/svg"

import (
	"io"

	"github.com/tdewolff/buffer"
	"github.com/tdewolff/minify"
	minifyCSS "github.com/tdewolff/minify/css"
	"github.com/tdewolff/parse"
	"github.com/tdewolff/parse/css"
	"github.com/tdewolff/parse/svg"
	"github.com/tdewolff/parse/xml"
)

var (
	ltBytes         = []byte("<")
	gtBytes         = []byte(">")
	voidBytes       = []byte("/>")
	isBytes         = []byte("=")
	spaceBytes      = []byte(" ")
	endBytes        = []byte("</")
	cdataStartBytes = []byte("<![CDATA[")
	cdataEndBytes   = []byte("]]>")
)

////////////////////////////////////////////////////////////////

type Minifier struct{}

func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
	return (&Minifier{}).Minify(m, w, r, params)
}

// Minify minifies SVG data, it reads from r and writes to w.
func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
	var tag svg.Hash
	defaultStyleType := "text/css"
	defaultInlineStyleType := "text/css;inline=1"

	attrMinifyBuffer := buffer.NewWriter(make([]byte, 0, 64))
	attrByteBuffer := make([]byte, 0, 64)

	l := xml.NewLexer(r)
	tb := NewTokenBuffer(l)
	for {
		t := *tb.Shift()
		if t.TokenType == xml.CDATAToken {
			var useText bool
			if t.Data, useText = xml.EscapeCDATAVal(&attrByteBuffer, t.Data); useText {
				t.TokenType = xml.TextToken
			}
		}
	SWITCH:
		switch t.TokenType {
		case xml.ErrorToken:
			if l.Err() == io.EOF {
				return nil
			}
			return l.Err()
		case xml.TextToken:
			t.Data = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.Data))
			if tag == svg.Style && len(t.Data) > 0 {
				if err := m.Minify(defaultStyleType, w, buffer.NewReader(t.Data)); err != nil {
					if err == minify.ErrNotExist { // no minifier, write the original
						if _, err := w.Write(t.Data); err != nil {
							return err
						}
					} else {
						return err
					}
				}
			} else if _, err := w.Write(t.Data); err != nil {
				return err
			}
		case xml.CDATAToken:
			if _, err := w.Write(cdataStartBytes); err != nil {
				return err
			}
			t.Data = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.Data))
			if tag == svg.Style && len(t.Data) > 0 {
				if err := m.Minify(defaultStyleType, w, buffer.NewReader(t.Data)); err != nil {
					if err == minify.ErrNotExist { // no minifier, write the original
						if _, err := w.Write(t.Data); err != nil {
							return err
						}
					} else {
						return err
					}
				}
			} else if _, err := w.Write(t.Data); err != nil {
				return err
			}
			if _, err := w.Write(cdataEndBytes); err != nil {
				return err
			}
		case xml.StartTagPIToken:
			for {
				if t := *tb.Shift(); t.TokenType == xml.StartTagClosePIToken || t.TokenType == xml.ErrorToken {
					break
				}
			}
		case xml.StartTagToken:
			tag = t.Hash
			if containerTagMap[t.Hash] { // skip empty containers
				i := 0
				for {
					next := tb.Peek(i)
					i++
					if next.TokenType == xml.EndTagToken && next.Hash == t.Hash || next.TokenType == xml.StartTagCloseVoidToken || next.TokenType == xml.ErrorToken {
						for j := 0; j < i; j++ {
							tb.Shift()
						}
						break SWITCH
					} else if next.TokenType != xml.AttributeToken && next.TokenType != xml.StartTagCloseToken {
						break
					}
				}
			} else if t.Hash == svg.Metadata {
				for {
					if next := *tb.Shift(); (next.TokenType == xml.EndTagToken || next.TokenType == xml.StartTagCloseVoidToken) && next.Hash == t.Hash || next.TokenType == xml.ErrorToken {
						break
					}
				}
				break
			} else if t.Hash == svg.Line || t.Hash == svg.Rect {
				// TODO: shape2path also for polygon and polyline
				// x1, y1, x2, y2 float64 := 0, 0, 0, 0
				// valid := true
				// i := 0
				// for {
				// 	next := tb.Peek(i)
				// 	i++
				// 	if next.TokenType != xml.AttributeToken {
				// 		break
				// 	}
				// 	v *int
				// 	attr := svg.ToHash(next.Data)
				// 	if tag == svg.Line {
				// 		if attr == svg.X1 {
				// 			v = &x1
				// 		} else if attr == svg.Y1 {
				// 			v = &y1
				// 		} else if attr == svg.X2 {
				// 			v = &x2
				// 		} else if attr == svg.Y2 {
				// 			v = &Y2
				// 		} else {
				// 			continue
				// 		}
				// 	} else if attr == svg.X { // rect
				// 		v = &x1
				// 	} else if attr == svg.Y {
				// 		v = &y1
				// 	} else if attr == svg.Width {
				// 		v = &x2
				// 	} else if attr == svg.Height {
				// 		v = &Y2
				// 	} else if attr == svg.Rx || attr == svg.Ry {
				// 		valid = false
				// 		break
				// 	} else {
				// 		continue
				// 	}

				// }
				// if valid {
				// 	t.Data = pathBytes
				// }
			}
			if _, err := w.Write(ltBytes); err != nil {
				return err
			}
			if _, err := w.Write(t.Data); err != nil {
				return err
			}
		case xml.AttributeToken:
			if len(t.AttrVal) < 2 {
				continue
			}
			val := parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.AttrVal[1 : len(t.AttrVal)-1]))
			if tag == svg.Svg && t.Hash == svg.Version {
				continue
			}

			if _, err := w.Write(spaceBytes); err != nil {
				return err
			}
			if _, err := w.Write(t.Data); err != nil {
				return err
			}
			if _, err := w.Write(isBytes); err != nil {
				return err
			}

			if tag == svg.Svg && t.Hash == svg.ContentStyleType {
				val = minify.ContentType(val)
				defaultStyleType = string(val)
				defaultInlineStyleType = defaultStyleType + ";inline=1"
			} else if t.Hash == svg.Style {
				attrMinifyBuffer.Reset()
				if m.Minify(defaultInlineStyleType, attrMinifyBuffer, buffer.NewReader(val)) == nil {
					val = attrMinifyBuffer.Bytes()
				}
			} else if t.Hash == svg.D {
				val = shortenPathData(val)
			} else if t.Hash == svg.ViewBox {
				j := 0
				newVal := val[:0]
				for i := 0; i < 4; i++ {
					if i != 0 {
						if j >= len(val) || val[j] != ' ' && val[j] != ',' {
							newVal = append(newVal, val[j:]...)
							break
						}
						newVal = append(newVal, ' ')
						j++
					}
					if dim, n := shortenDimension(val[j:]); n > 0 {
						newVal = append(newVal, dim...)
						j += n
					} else {
						newVal = append(newVal, val[j:]...)
						break
					}
				}
				val = newVal
			} else if colorAttrMap[t.Hash] && len(val) > 0 {
				parse.ToLower(val)
				if val[0] == '#' {
					if name, ok := minifyCSS.ShortenColorHex[string(val)]; ok {
						val = name
					} else if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
						val[2] = val[3]
						val[3] = val[5]
						val = val[:4]
					}
				} else if hex, ok := minifyCSS.ShortenColorName[css.ToHash(val)]; ok {
					val = hex
				} else if len(val) > 5 && parse.Equal(val[:4], []byte("rgb(")) && val[len(val)-1] == ')' {
					// TODO: handle rgb(x, y, z) and hsl(x, y, z)
				}
			} else if n, m := parse.Dimension(val); n+m == len(val) { // TODO: inefficient, temporary measure
				val, _ = shortenDimension(val)
			}

			// prefer single or double quotes depending on what occurs more often in value
			val = xml.EscapeAttrVal(&attrByteBuffer, val)
			if _, err := w.Write(val); err != nil {
				return err
			}
		case xml.StartTagCloseToken:
			next := tb.Peek(0)
			skipExtra := false
			if next.TokenType == xml.TextToken && parse.IsAllWhitespace(next.Data) {
				next = tb.Peek(1)
				skipExtra = true
			}
			if next.TokenType == xml.EndTagToken {
				// collapse empty tags to single void tag
				tb.Shift()
				if skipExtra {
					tb.Shift()
				}
				if _, err := w.Write(voidBytes); err != nil {
					return err
				}
			} else {
				if _, err := w.Write(gtBytes); err != nil {
					return err
				}
			}
		case xml.StartTagCloseVoidToken:
			if _, err := w.Write(voidBytes); err != nil {
				return err
			}
		case xml.EndTagToken:
			if _, err := w.Write(endBytes); err != nil {
				return err
			}
			if _, err := w.Write(t.Data); err != nil {
				return err
			}
			if _, err := w.Write(gtBytes); err != nil {
				return err
			}
		}
	}
}

func shortenPathData(b []byte) []byte {
	cmd := byte(0)
	prevDigit := false
	prevDigitRequiresSpace := true
	j := 0
	start := 0
	for i := 0; i < len(b); i++ {
		c := b[i]
		if c == ' ' || c == ',' || c == '\t' || c == '\n' || c == '\r' {
			if start != 0 {
				j += copy(b[j:], b[start:i])
			} else {
				j += i
			}
			start = i + 1
		} else if n := parse.Number(b[i:]); n > 0 {
			if start != 0 {
				j += copy(b[j:], b[start:i])
			} else {
				j += i
			}
			num := minify.Number(b[i : i+n])
			if prevDigit && (num[0] >= '0' && num[0] <= '9' || num[0] == '.' && prevDigitRequiresSpace) {
				b[j] = ' '
				j++
			}
			prevDigit = true
			prevDigitRequiresSpace = true
			for i := 0; i < len(num); i++ {
				c := num[i]
				if c == '.' || c == 'e' || c == 'E' {
					prevDigitRequiresSpace = false
					break
				}
			}
			j += copy(b[j:], num)
			start = i + n
			i += n - 1
		} else {
			if cmd == c {
				if start != 0 {
					j += copy(b[j:], b[start:i])
				} else {
					j += i
				}
				start = i + 1
			} else {
				cmd = c
				prevDigit = false
			}
		}
	}
	if start != 0 {
		j += copy(b[j:], b[start:])
		return b[:j]
	}
	return b
}

func shortenDimension(b []byte) ([]byte, int) {
	if n, m := parse.Dimension(b); n > 0 {
		unit := b[n : n+m]
		b = minify.Number(b[:n])
		if len(b) != 1 || b[0] != '0' {
			if m == 2 && unit[0] == 'p' && unit[1] == 'x' {
				unit = nil
			} else if m > 1 { // only percentage is length 1
				parse.ToLower(unit)
			}
			b = append(b, unit...)
		}
		return b, n + m
	}
	return b, 0
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 command

import (
	"errors"
	"os"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/client"
)

// NewUpdateDirCommand returns the CLI command for "updatedir".
func NewUpdateDirCommand() cli.Command {
	return cli.Command{
		Name:      "updatedir",
		Usage:     "update an existing directory",
		ArgsUsage: "<key> <value>",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live"},
		},
		Action: func(c *cli.Context) {
			updatedirCommandFunc(c, mustNewKeyAPI(c))
		},
	}
}

// updatedirCommandFunc executes the "updatedir" command.
func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	value, err := argOrStdin(c.Args(), os.Stdin, 1)
	if err != nil {
		handleError(ExitBadArgs, errors.New("value required"))
	}

	ttl := c.Int("ttl")

	ctx, cancel := contextWithTotalTimeout(c)
	_, err = ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist})
	cancel()
	if err != nil {
		handleError(ExitServerError, err)
	}
}
<commit_msg>etcdctl: ignore value in updatedir command<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 command

import (
	"errors"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/client"
)

// NewUpdateDirCommand returns the CLI command for "updatedir".
func NewUpdateDirCommand() cli.Command {
	return cli.Command{
		Name:      "updatedir",
		Usage:     "update an existing directory",
		ArgsUsage: "<key> <value>",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live"},
		},
		Action: func(c *cli.Context) {
			updatedirCommandFunc(c, mustNewKeyAPI(c))
		},
	}
}

// updatedirCommandFunc executes the "updatedir" command.
func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	ttl := c.Int("ttl")
	ctx, cancel := contextWithTotalTimeout(c)
	_, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist})
	cancel()
	if err != nil {
		handleError(ExitServerError, err)
	}
}
<|endoftext|>


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

package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"io"
	"time"

	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/armor"
	"golang.org/x/crypto/openpgp/packet"
	_ "golang.org/x/crypto/sha3"
)

//A fake static time for when key's are (were) created
var KeyDate time.Time = time.Date(1979, time.April, 10, 14, 15, 0, 0, time.FixedZone("VET", -16200))

var config = &packet.Config{
	DefaultHash:   crypto.SHA3_512,
	DefaultCipher: packet.CipherAES256,
}

func prompter(keys []openpgp.Key, symmetric bool) (passphrase []byte, err error) {
	return
}

func gpgEncrypt(rsaPubKey *rsa.PublicKey, inFile io.Reader, outFile io.Writer) {

	aesKey := make([]byte, packet.CipherAES256.KeySize())
	rand.Read(aesKey)

	pubKey := packet.NewRSAPublicKey(KeyDate, rsaPubKey)

	outArmor, err := armor.Encode(outFile, "SSH-CRYPT-MESSAGE", make(map[string]string))
	if err != nil {
		panic(err)
	}
	defer outArmor.Close()

	err = packet.SerializeEncryptedKey(outArmor, pubKey, packet.CipherAES256, aesKey, config)
	if err != nil {
		panic(err)
	}

	encryptedData, err := packet.SerializeSymmetricallyEncrypted(outArmor, packet.CipherAES256, aesKey, config)
	if err != nil {
		panic(err)
	}
	defer encryptedData.Close()

	hints := &openpgp.FileHints{}
	var epochSeconds uint32
	if !hints.ModTime.IsZero() {
		epochSeconds = uint32(hints.ModTime.Unix())
	}
	writer, err := packet.SerializeLiteral(encryptedData, hints.IsBinary, hints.FileName, epochSeconds)
	if err != nil {
		panic(err)
	}
	defer writer.Close()

	// Copy the input file to the output file, encrypting as we go.
	if _, err := io.Copy(writer, inFile); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the decrypted result.
}

func gpgDecrypt(rsaPrivKey *rsa.PrivateKey, inFile io.Reader, outFile io.Writer) {

	privKey := packet.NewRSAPrivateKey(KeyDate, rsaPrivKey)

	armorBlock, err := armor.Decode(inFile)
	if err != nil {
		panic(err)
	}

	var keyRing openpgp.EntityList
	keyRing = append(keyRing, &openpgp.Entity{
		PrivateKey: privKey,
		PrimaryKey: packet.NewRSAPublicKey(KeyDate, rsaPrivKey.Public().(*rsa.PublicKey)),
	})

	md, err := openpgp.ReadMessage(armorBlock.Body, keyRing, nil, config)
	if err != nil {
		panic(err)
	}

	// Copy the input file to the output file, decrypting as we go.
	if _, err := io.Copy(outFile, md.UnverifiedBody); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the output.
}
<commit_msg>Add compression!<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"io"
	"time"

	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/armor"
	"golang.org/x/crypto/openpgp/packet"
	_ "golang.org/x/crypto/sha3"
)

//A fake static time for when key's are (were) created
var KeyDate time.Time = time.Date(1979, time.April, 10, 14, 15, 0, 0, time.FixedZone("VET", -16200))

var config = &packet.Config{
	DefaultHash:            crypto.SHA3_512,
	DefaultCipher:          packet.CipherAES256,
	DefaultCompressionAlgo: packet.CompressionZLIB,
	CompressionConfig:      &packet.CompressionConfig{Level: 7},
}

func prompter(keys []openpgp.Key, symmetric bool) (passphrase []byte, err error) {
	return
}

func gpgEncrypt(rsaPubKey *rsa.PublicKey, inFile io.Reader, outFile io.Writer) {

	aesKey := make([]byte, packet.CipherAES256.KeySize())
	rand.Read(aesKey)

	pubKey := packet.NewRSAPublicKey(KeyDate, rsaPubKey)

	outArmor, err := armor.Encode(outFile, "SSH-CRYPT-MESSAGE", make(map[string]string))
	if err != nil {
		panic(err)
	}
	defer outArmor.Close()

	err = packet.SerializeEncryptedKey(outArmor, pubKey, packet.CipherAES256, aesKey, config)
	if err != nil {
		panic(err)
	}

	encryptedData, err := packet.SerializeSymmetricallyEncrypted(outArmor, packet.CipherAES256, aesKey, config)
	if err != nil {
		panic(err)
	}
	defer encryptedData.Close()

	hints := &openpgp.FileHints{}
	var epochSeconds uint32
	if !hints.ModTime.IsZero() {
		epochSeconds = uint32(hints.ModTime.Unix())
	}

	compressedData, err := packet.SerializeCompressed(encryptedData, config.DefaultCompressionAlgo, config.CompressionConfig)
	if err != nil {
		panic(err)
	}
	defer compressedData.Close()

	writer, err := packet.SerializeLiteral(compressedData, hints.IsBinary, hints.FileName, epochSeconds)
	if err != nil {
		panic(err)
	}
	defer writer.Close()

	// Copy the input file to the output file, encrypting as we go.
	if _, err := io.Copy(writer, inFile); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the decrypted result.
}

func gpgDecrypt(rsaPrivKey *rsa.PrivateKey, inFile io.Reader, outFile io.Writer) {

	privKey := packet.NewRSAPrivateKey(KeyDate, rsaPrivKey)

	armorBlock, err := armor.Decode(inFile)
	if err != nil {
		panic(err)
	}

	var keyRing openpgp.EntityList
	keyRing = append(keyRing, &openpgp.Entity{
		PrivateKey: privKey,
		PrimaryKey: packet.NewRSAPublicKey(KeyDate, rsaPrivKey.Public().(*rsa.PublicKey)),
	})

	md, err := openpgp.ReadMessage(armorBlock.Body, keyRing, nil, config)
	if err != nil {
		panic(err)
	}

	// Copy the input file to the output file, decrypting as we go.
	if _, err := io.Copy(outFile, md.UnverifiedBody); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the output.
}
<|endoftext|>


<commit_before>// Copyright 2015 The Mangos Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use 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 mangos

// The following are Options used by SetOption, GetOption.

const (
	// OptionRaw is used to enable RAW mode processing.  The details of
	// how this varies from normal mode vary from protocol to protocol.
	// RAW mode corresponds to AF_SP_RAW in the C variant, and must be
	// used with Devices.  In particular, RAW mode sockets are completely
	// stateless -- any state between recv/send messages is included in
	// the message headers.  Protocol names starting with "X" default
	// to the RAW mode of the same protocol without the leading "X".
	// The value passed is a bool.
	OptionRaw = "RAW"

	// OptionRecvDeadline is the time until the next Recv times out.  The
	// value is a time.Duration.  Zero value may be passed to indicate that
	// no timeout should be applied.  A negative value indicates a
	// non-blocking operation.  By default there is no timeout.
	OptionRecvDeadline = "RECV-DEADLINE"

	// OptionSendDeadline is the time until the next Send times out.  The
	// value is a time.Duration.  Zero value may be passed to indicate that
	// no timeout should be applied.  A negative value indicates a
	// non-blocking operation.  By default there is no timeout.
	OptionSendDeadline = "SEND-DEADLINE"

	// OptionRetryTime is used by REQ.  The argument is a time.Duration.
	// When a request has been replied to within the given duration,
	// the request will automatically be resent to an available peer.
	// This value should be longer than the maximum possible processing
	// and transport time.  The value zero indicates that no automatic
	// retries should be sent.  The default value is one minute.
	//
	// Note that changing this option is only guaranteed to affect requests
	// sent after the option is set.  Changing the value while a request
	// is outstanding may not have the desired effect.
	OptionRetryTime = "RETRY-TIME"

	// OptionSubscribe is used by SUB/XSUB.  The argument is a []byte.
	// The application will receive messages that start with this prefix.
	// Multiple subscriptions may be in effect on a given socket.  The
	// application will not receive messages that do not match any current
	// subscriptions.  (If there are no subscriptions for a SUB/XSUB
	// socket, then the application will not receive any messages.  An
	// empty prefix can be used to subscribe to all messages.)
	OptionSubscribe = "SUBSCRIBE"

	// OptionUnsubscribe is used by SUB/XSUB.  The argument is a []byte,
	// representing a previously established subscription, which will be
	// removed from the socket.
	OptionUnsubscribe = "UNSUBSCRIBE"

	// OptionSurveyTime is used to indicate the deadline for survey
	// responses, when used with a SURVEYOR socket.  Messages arriving
	// after this will be discarded.  Additionally, this will set the
	// OptionRecvDeadline when starting the survey, so that attempts to
	// receive messages fail with ErrRecvTimeout when the survey is
	// concluded.  The value is a time.Duration.  Zero can be passed to
	// indicate an infinite time.  Default is 1 second.
	OptionSurveyTime = "SURVEY-TIME"

	// OptionTlsConfig is used to supply TLS configuration details.
	// The parameter is a tls.Config pointer.
	OptionTlsConfig = "TLS-CONFIG"

	// OptionLocalAddress is used to get the local address an accepter is
	// listening on in string form. Generally this is known when Listen is
	// called because it is provided, but this option is useful in the
	// event that the port is assigned by the OS (i.e. port "0").
	OptionLocalAddress = "LOCAL-ADDRESS"

	// OptionWriteQLen is used to set the size, in messages, of the write
	// queue channel. By default, it's 128. This option cannot be set if
	// Dial or Listen has been called on the socket.
	OptionWriteQLen = "WRITEQ-LEN"

	// OptionReadQLen is used to set the size, in messages, of the read
	// queue channel. By default, it's 128. This option cannot be set if
	// Dial or Listen has been called on the socket.
	OptionReadQLen = "READQ-LEN"

	// OptionKeepAlive is used to set TCP KeepAlive.  Value is a boolean.
	// Default is true.
	OptionKeepAlive = "KEEPALIVE"

	// OptionNoDelay is used to configure Nagle -- when true messages are
	// sent as soon as possible, otherwise some buffering may occur.
	// Value is a boolean.  Default is true.
	OptionNoDelay = "NO-DELAY"

	// OptionLinger is used to set the linger property.  This is the amount
	// of time to wait for send queues to drain when Close() is called.
	// Close() may block for up to this long if there is unsent data, but
	// will return as soon as all data is delivered to the transport.
	// Value is a time.Duration.  Default is one second.
	OptionLinger = "LINGER"

	// OptionTTL is used to set the maximum time-to-live for messages.
	// Note that not all protocols can honor this at this time, but for
	// those that do, if a message traverses more than this many devices,
	// it will be dropped.  This is used to provide protection against
	// loops in the topology.  The default is protocol specific.
	OptionTtl = "TTL"
)
<commit_msg>fixes #125 OptionLocalAddress is no longer used<commit_after>// Copyright 2015 The Mangos Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use 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 mangos

// The following are Options used by SetOption, GetOption.

const (
	// OptionRaw is used to enable RAW mode processing.  The details of
	// how this varies from normal mode vary from protocol to protocol.
	// RAW mode corresponds to AF_SP_RAW in the C variant, and must be
	// used with Devices.  In particular, RAW mode sockets are completely
	// stateless -- any state between recv/send messages is included in
	// the message headers.  Protocol names starting with "X" default
	// to the RAW mode of the same protocol without the leading "X".
	// The value passed is a bool.
	OptionRaw = "RAW"

	// OptionRecvDeadline is the time until the next Recv times out.  The
	// value is a time.Duration.  Zero value may be passed to indicate that
	// no timeout should be applied.  A negative value indicates a
	// non-blocking operation.  By default there is no timeout.
	OptionRecvDeadline = "RECV-DEADLINE"

	// OptionSendDeadline is the time until the next Send times out.  The
	// value is a time.Duration.  Zero value may be passed to indicate that
	// no timeout should be applied.  A negative value indicates a
	// non-blocking operation.  By default there is no timeout.
	OptionSendDeadline = "SEND-DEADLINE"

	// OptionRetryTime is used by REQ.  The argument is a time.Duration.
	// When a request has been replied to within the given duration,
	// the request will automatically be resent to an available peer.
	// This value should be longer than the maximum possible processing
	// and transport time.  The value zero indicates that no automatic
	// retries should be sent.  The default value is one minute.
	//
	// Note that changing this option is only guaranteed to affect requests
	// sent after the option is set.  Changing the value while a request
	// is outstanding may not have the desired effect.
	OptionRetryTime = "RETRY-TIME"

	// OptionSubscribe is used by SUB/XSUB.  The argument is a []byte.
	// The application will receive messages that start with this prefix.
	// Multiple subscriptions may be in effect on a given socket.  The
	// application will not receive messages that do not match any current
	// subscriptions.  (If there are no subscriptions for a SUB/XSUB
	// socket, then the application will not receive any messages.  An
	// empty prefix can be used to subscribe to all messages.)
	OptionSubscribe = "SUBSCRIBE"

	// OptionUnsubscribe is used by SUB/XSUB.  The argument is a []byte,
	// representing a previously established subscription, which will be
	// removed from the socket.
	OptionUnsubscribe = "UNSUBSCRIBE"

	// OptionSurveyTime is used to indicate the deadline for survey
	// responses, when used with a SURVEYOR socket.  Messages arriving
	// after this will be discarded.  Additionally, this will set the
	// OptionRecvDeadline when starting the survey, so that attempts to
	// receive messages fail with ErrRecvTimeout when the survey is
	// concluded.  The value is a time.Duration.  Zero can be passed to
	// indicate an infinite time.  Default is 1 second.
	OptionSurveyTime = "SURVEY-TIME"

	// OptionTlsConfig is used to supply TLS configuration details.
	// The parameter is a tls.Config pointer.
	OptionTlsConfig = "TLS-CONFIG"

	// OptionWriteQLen is used to set the size, in messages, of the write
	// queue channel. By default, it's 128. This option cannot be set if
	// Dial or Listen has been called on the socket.
	OptionWriteQLen = "WRITEQ-LEN"

	// OptionReadQLen is used to set the size, in messages, of the read
	// queue channel. By default, it's 128. This option cannot be set if
	// Dial or Listen has been called on the socket.
	OptionReadQLen = "READQ-LEN"

	// OptionKeepAlive is used to set TCP KeepAlive.  Value is a boolean.
	// Default is true.
	OptionKeepAlive = "KEEPALIVE"

	// OptionNoDelay is used to configure Nagle -- when true messages are
	// sent as soon as possible, otherwise some buffering may occur.
	// Value is a boolean.  Default is true.
	OptionNoDelay = "NO-DELAY"

	// OptionLinger is used to set the linger property.  This is the amount
	// of time to wait for send queues to drain when Close() is called.
	// Close() may block for up to this long if there is unsent data, but
	// will return as soon as all data is delivered to the transport.
	// Value is a time.Duration.  Default is one second.
	OptionLinger = "LINGER"

	// OptionTTL is used to set the maximum time-to-live for messages.
	// Note that not all protocols can honor this at this time, but for
	// those that do, if a message traverses more than this many devices,
	// it will be dropped.  This is used to provide protection against
	// loops in the topology.  The default is protocol specific.
	OptionTtl = "TTL"
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/bitly/oauth2_proxy/providers"
)

// Configuration Options that can be set by Command Line Flag, or Config File
type Options struct {
	ProxyPrefix  string `flag:"proxy-prefix" cfg:"proxy-prefix"`
	HttpAddress  string `flag:"http-address" cfg:"http_address"`
	HttpsAddress string `flag:"https-address" cfg:"https_address"`
	RedirectUrl  string `flag:"redirect-url" cfg:"redirect_url"`
	ClientID     string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
	ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
	TLSCertFile  string `flag:"tls-cert" cfg:"tls_cert_file"`
	TLSKeyFile   string `flag:"tls-key" cfg:"tls_key_file"`

	AuthenticatedEmailsFile string   `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
	EmailDomains            []string `flag:"email-domain" cfg:"email_domains"`
	GitHubOrg               string   `flag:"github-org" cfg:"github_org"`
	GitHubTeam              string   `flag:"github-team" cfg:"github_team"`
	HtpasswdFile            string   `flag:"htpasswd-file" cfg:"htpasswd_file"`
	DisplayHtpasswdForm     bool     `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"`
	CustomTemplatesDir      string   `flag:"custom-templates-dir" cfg:"custom_templates_dir"`

	CookieName     string        `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
	CookieSecret   string        `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
	CookieDomain   string        `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
	CookieExpire   time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
	CookieRefresh  time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
	CookieSecure   bool          `flag:"cookie-secure" cfg:"cookie_secure"`
	CookieHttpOnly bool          `flag:"cookie-httponly" cfg:"cookie_httponly"`

	Upstreams         []string `flag:"upstream" cfg:"upstreams"`
	SkipAuthRegex     []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
	PassBasicAuth     bool     `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
	BasicAuthPassword string   `flag:"basic-auth-password" cfg:"basic_auth_password"`
	PassAccessToken   bool     `flag:"pass-access-token" cfg:"pass_access_token"`
	PassHostHeader    bool     `flag:"pass-host-header" cfg:"pass_host_header"`

	// These options allow for other providers besides Google, with
	// potential overrides.
	Provider    string `flag:"provider" cfg:"provider"`
	LoginUrl    string `flag:"login-url" cfg:"login_url"`
	RedeemUrl   string `flag:"redeem-url" cfg:"redeem_url"`
	ProfileUrl  string `flag:"profile-url" cfg:"profile_url"`
	ValidateUrl string `flag:"validate-url" cfg:"validate_url"`
	Scope       string `flag:"scope" cfg:"scope"`

	RequestLogging bool `flag:"request-logging" cfg:"request_logging"`

	// internal values that are set after config validation
	redirectUrl   *url.URL
	proxyUrls     []*url.URL
	CompiledRegex []*regexp.Regexp
	provider      providers.Provider
}

func NewOptions() *Options {
	return &Options{
		ProxyPrefix:         "/oauth2",
		HttpAddress:         "127.0.0.1:4180",
		HttpsAddress:        ":443",
		DisplayHtpasswdForm: true,
		CookieName:          "_oauth2_proxy",
		CookieSecure:        true,
		CookieHttpOnly:      true,
		CookieExpire:        time.Duration(168) * time.Hour,
		CookieRefresh:       time.Duration(0),
		PassBasicAuth:       true,
		PassAccessToken:     false,
		PassHostHeader:      true,
		RequestLogging:      true,
	}
}

func parseUrl(to_parse string, urltype string, msgs []string) (*url.URL, []string) {
	parsed, err := url.Parse(to_parse)
	if err != nil {
		return nil, append(msgs, fmt.Sprintf(
			"error parsing %s-url=%q %s", urltype, to_parse, err))
	}
	return parsed, msgs
}

func (o *Options) Validate() error {
	msgs := make([]string, 0)
	if len(o.Upstreams) < 1 {
		msgs = append(msgs, "missing setting: upstream")
	}
	if o.CookieSecret == "" {
		msgs = append(msgs, "missing setting: cookie-secret")
	}
	if o.ClientID == "" {
		msgs = append(msgs, "missing setting: client-id")
	}
	if o.ClientSecret == "" {
		msgs = append(msgs, "missing setting: client-secret")
	}

	o.redirectUrl, msgs = parseUrl(o.RedirectUrl, "redirect", msgs)

	for _, u := range o.Upstreams {
		upstreamUrl, err := url.Parse(u)
		if err != nil {
			msgs = append(msgs, fmt.Sprintf(
				"error parsing upstream=%q %s",
				upstreamUrl, err))
		}
		if upstreamUrl.Path == "" {
			upstreamUrl.Path = "/"
		}
		o.proxyUrls = append(o.proxyUrls, upstreamUrl)
	}

	for _, u := range o.SkipAuthRegex {
		CompiledRegex, err := regexp.Compile(u)
		if err != nil {
			msgs = append(msgs, fmt.Sprintf(
				"error compiling regex=%q %s", u, err))
		}
		o.CompiledRegex = append(o.CompiledRegex, CompiledRegex)
	}
	msgs = parseProviderInfo(o, msgs)

	if o.PassAccessToken || (o.CookieRefresh != time.Duration(0)) {
		valid_cookie_secret_size := false
		for _, i := range []int{16, 24, 32} {
			if len(o.CookieSecret) == i {
				valid_cookie_secret_size = true
			}
		}
		if valid_cookie_secret_size == false {
			msgs = append(msgs, fmt.Sprintf(
				"cookie_secret must be 16, 24, or 32 bytes "+
					"to create an AES cipher when "+
					"pass_access_token == true or "+
					"cookie_refresh != 0, but is %d bytes",
				len(o.CookieSecret)))
		}
	}

	if o.CookieRefresh >= o.CookieExpire {
		msgs = append(msgs, fmt.Sprintf(
			"cookie_refresh (%s) must be less than "+
				"cookie_expire (%s)",
			o.CookieRefresh.String(),
			o.CookieExpire.String()))
	}

	if len(msgs) != 0 {
		return fmt.Errorf("Invalid configuration:\n  %s",
			strings.Join(msgs, "\n  "))
	}
	return nil
}

func parseProviderInfo(o *Options, msgs []string) []string {
	p := &providers.ProviderData{Scope: o.Scope, ClientID: o.ClientID, ClientSecret: o.ClientSecret}
	p.LoginUrl, msgs = parseUrl(o.LoginUrl, "login", msgs)
	p.RedeemUrl, msgs = parseUrl(o.RedeemUrl, "redeem", msgs)
	p.ProfileUrl, msgs = parseUrl(o.ProfileUrl, "profile", msgs)
	p.ValidateUrl, msgs = parseUrl(o.ValidateUrl, "validate", msgs)

	o.provider = providers.New(o.Provider, p)
	switch p := o.provider.(type) {
	case *providers.GitHubProvider:
		p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
	}
	return msgs
}
<commit_msg>clarify required email validation settings<commit_after>package main

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/bitly/oauth2_proxy/providers"
)

// Configuration Options that can be set by Command Line Flag, or Config File
type Options struct {
	ProxyPrefix  string `flag:"proxy-prefix" cfg:"proxy-prefix"`
	HttpAddress  string `flag:"http-address" cfg:"http_address"`
	HttpsAddress string `flag:"https-address" cfg:"https_address"`
	RedirectUrl  string `flag:"redirect-url" cfg:"redirect_url"`
	ClientID     string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
	ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
	TLSCertFile  string `flag:"tls-cert" cfg:"tls_cert_file"`
	TLSKeyFile   string `flag:"tls-key" cfg:"tls_key_file"`

	AuthenticatedEmailsFile string   `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
	EmailDomains            []string `flag:"email-domain" cfg:"email_domains"`
	GitHubOrg               string   `flag:"github-org" cfg:"github_org"`
	GitHubTeam              string   `flag:"github-team" cfg:"github_team"`
	HtpasswdFile            string   `flag:"htpasswd-file" cfg:"htpasswd_file"`
	DisplayHtpasswdForm     bool     `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"`
	CustomTemplatesDir      string   `flag:"custom-templates-dir" cfg:"custom_templates_dir"`

	CookieName     string        `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
	CookieSecret   string        `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
	CookieDomain   string        `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
	CookieExpire   time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
	CookieRefresh  time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
	CookieSecure   bool          `flag:"cookie-secure" cfg:"cookie_secure"`
	CookieHttpOnly bool          `flag:"cookie-httponly" cfg:"cookie_httponly"`

	Upstreams         []string `flag:"upstream" cfg:"upstreams"`
	SkipAuthRegex     []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
	PassBasicAuth     bool     `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
	BasicAuthPassword string   `flag:"basic-auth-password" cfg:"basic_auth_password"`
	PassAccessToken   bool     `flag:"pass-access-token" cfg:"pass_access_token"`
	PassHostHeader    bool     `flag:"pass-host-header" cfg:"pass_host_header"`

	// These options allow for other providers besides Google, with
	// potential overrides.
	Provider    string `flag:"provider" cfg:"provider"`
	LoginUrl    string `flag:"login-url" cfg:"login_url"`
	RedeemUrl   string `flag:"redeem-url" cfg:"redeem_url"`
	ProfileUrl  string `flag:"profile-url" cfg:"profile_url"`
	ValidateUrl string `flag:"validate-url" cfg:"validate_url"`
	Scope       string `flag:"scope" cfg:"scope"`

	RequestLogging bool `flag:"request-logging" cfg:"request_logging"`

	// internal values that are set after config validation
	redirectUrl   *url.URL
	proxyUrls     []*url.URL
	CompiledRegex []*regexp.Regexp
	provider      providers.Provider
}

func NewOptions() *Options {
	return &Options{
		ProxyPrefix:         "/oauth2",
		HttpAddress:         "127.0.0.1:4180",
		HttpsAddress:        ":443",
		DisplayHtpasswdForm: true,
		CookieName:          "_oauth2_proxy",
		CookieSecure:        true,
		CookieHttpOnly:      true,
		CookieExpire:        time.Duration(168) * time.Hour,
		CookieRefresh:       time.Duration(0),
		PassBasicAuth:       true,
		PassAccessToken:     false,
		PassHostHeader:      true,
		RequestLogging:      true,
	}
}

func parseUrl(to_parse string, urltype string, msgs []string) (*url.URL, []string) {
	parsed, err := url.Parse(to_parse)
	if err != nil {
		return nil, append(msgs, fmt.Sprintf(
			"error parsing %s-url=%q %s", urltype, to_parse, err))
	}
	return parsed, msgs
}

func (o *Options) Validate() error {
	msgs := make([]string, 0)
	if len(o.Upstreams) < 1 {
		msgs = append(msgs, "missing setting: upstream")
	}
	if o.CookieSecret == "" {
		msgs = append(msgs, "missing setting: cookie-secret")
	}
	if o.ClientID == "" {
		msgs = append(msgs, "missing setting: client-id")
	}
	if o.ClientSecret == "" {
		msgs = append(msgs, "missing setting: client-secret")
	}
	if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
		msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required.\n      use email-domain=* to authorize all email addresses")
	}

	o.redirectUrl, msgs = parseUrl(o.RedirectUrl, "redirect", msgs)

	for _, u := range o.Upstreams {
		upstreamUrl, err := url.Parse(u)
		if err != nil {
			msgs = append(msgs, fmt.Sprintf(
				"error parsing upstream=%q %s",
				upstreamUrl, err))
		}
		if upstreamUrl.Path == "" {
			upstreamUrl.Path = "/"
		}
		o.proxyUrls = append(o.proxyUrls, upstreamUrl)
	}

	for _, u := range o.SkipAuthRegex {
		CompiledRegex, err := regexp.Compile(u)
		if err != nil {
			msgs = append(msgs, fmt.Sprintf(
				"error compiling regex=%q %s", u, err))
		}
		o.CompiledRegex = append(o.CompiledRegex, CompiledRegex)
	}
	msgs = parseProviderInfo(o, msgs)

	if o.PassAccessToken || (o.CookieRefresh != time.Duration(0)) {
		valid_cookie_secret_size := false
		for _, i := range []int{16, 24, 32} {
			if len(o.CookieSecret) == i {
				valid_cookie_secret_size = true
			}
		}
		if valid_cookie_secret_size == false {
			msgs = append(msgs, fmt.Sprintf(
				"cookie_secret must be 16, 24, or 32 bytes "+
					"to create an AES cipher when "+
					"pass_access_token == true or "+
					"cookie_refresh != 0, but is %d bytes",
				len(o.CookieSecret)))
		}
	}

	if o.CookieRefresh >= o.CookieExpire {
		msgs = append(msgs, fmt.Sprintf(
			"cookie_refresh (%s) must be less than "+
				"cookie_expire (%s)",
			o.CookieRefresh.String(),
			o.CookieExpire.String()))
	}

	if len(msgs) != 0 {
		return fmt.Errorf("Invalid configuration:\n  %s",
			strings.Join(msgs, "\n  "))
	}
	return nil
}

func parseProviderInfo(o *Options, msgs []string) []string {
	p := &providers.ProviderData{Scope: o.Scope, ClientID: o.ClientID, ClientSecret: o.ClientSecret}
	p.LoginUrl, msgs = parseUrl(o.LoginUrl, "login", msgs)
	p.RedeemUrl, msgs = parseUrl(o.RedeemUrl, "redeem", msgs)
	p.ProfileUrl, msgs = parseUrl(o.ProfileUrl, "profile", msgs)
	p.ValidateUrl, msgs = parseUrl(o.ValidateUrl, "validate", msgs)

	o.provider = providers.New(o.Provider, p)
	switch p := o.provider.(type) {
	case *providers.GitHubProvider:
		p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
	}
	return msgs
}
<|endoftext|>


<commit_before>package bridge

import (
	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/vishvananda/netlink"
)

// SetupDevice create a new bridge interface/
func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
	var setMac bool

	// We only attempt to create the bridge when the requested device name is
	// the default one.
	if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge {
		return NonDefaultBridgeExistError(config.BridgeName)
	}

	// Set the bridgeInterface netlink.Bridge.
	i.Link = &netlink.Bridge{
		LinkAttrs: netlink.LinkAttrs{
			Name: config.BridgeName,
		},
	}

	// Only set the bridge's MAC address if the kernel version is > 3.3, as it
	// was not supported before that.
	kv, err := kernel.GetKernelVersion()
	if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
		setMac = true
	}

	return ioctlCreateBridge(config.BridgeName, setMac)
}

// SetupDeviceUp ups the given bridge interface.
func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error {
	err := netlink.LinkSetUp(i.Link)
	if err != nil {
		return err
	}

	// Attempt to update the bridge interface to refresh the flags status,
	// ignoring any failure to do so.
	if lnk, err := netlink.LinkByName(config.BridgeName); err == nil {
		i.Link = lnk
	}
	return nil
}
<commit_msg>Incorrect kernel version check in bridge<commit_after>package bridge

import (
	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/vishvananda/netlink"
)

// SetupDevice create a new bridge interface/
func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
	var setMac bool

	// We only attempt to create the bridge when the requested device name is
	// the default one.
	if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge {
		return NonDefaultBridgeExistError(config.BridgeName)
	}

	// Set the bridgeInterface netlink.Bridge.
	i.Link = &netlink.Bridge{
		LinkAttrs: netlink.LinkAttrs{
			Name: config.BridgeName,
		},
	}

	// Only set the bridge's MAC address if the kernel version is > 3.3, as it
	// was not supported before that.
	kv, err := kernel.GetKernelVersion()
	if err != nil {
		logrus.Errorf("Failed to check kernel versions: %v. Will not assign a MAC address to the bridge interface", err)
	} else {
		setMac = kv.Kernel > 3 || (kv.Kernel == 3 && kv.Major >= 3)
	}

	return ioctlCreateBridge(config.BridgeName, setMac)
}

// SetupDeviceUp ups the given bridge interface.
func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error {
	err := netlink.LinkSetUp(i.Link)
	if err != nil {
		return err
	}

	// Attempt to update the bridge interface to refresh the flags status,
	// ignoring any failure to do so.
	if lnk, err := netlink.LinkByName(config.BridgeName); err == nil {
		i.Link = lnk
	}
	return nil
}
<|endoftext|>


<commit_before>package lru

import (
	"math/rand"
	"strconv"
	"testing"

	"github.com/bradberger/gocache/cache"
	"github.com/stretchr/testify/assert"
)

func randKey() string {
	return strconv.Itoa(rand.Intn(10000000))
}

func TestNewSharded(t *testing.T) {
	c := New()
	assert.NotNil(t, c)
	assert.NotNil(t, c.shards)
	assert.NotNil(t, c.ch)
}

func TestShardedGetSet(t *testing.T) {
	c := NewSharded(2<<10, Megabyte, 256)

	var v string
	assert.False(t, c.Exists("foo"))
	assert.NoError(t, c.Set("foo", "bar", 0))
	assert.NoError(t, c.Touch("foo", 0))
	assert.Error(t, c.Add("foo", "bar", 0))
	assert.NoError(t, c.Replace("foo", "bar"))
	assert.NoError(t, c.Prepend("foo", "foo"))
	assert.NoError(t, c.Append("foo", "foo"))
	assert.True(t, c.Exists("foo"))
	assert.NoError(t, c.Get("foo", &v))
	assert.NoError(t, c.Del("foo"))
	assert.False(t, c.Exists("foo"))
	assert.Equal(t, "foobarfoo", v)

	assert.NoError(t, c.Set("ct", uint64(2), 0))
	ct, err := c.Increment("ct", 10)

	assert.Equal(t, uint64(12), ct)
	assert.NoError(t, err)

	ct, err = c.Decrement("ct", 1)
	assert.NoError(t, err)
	assert.Equal(t, uint64(11), ct)

	assert.NoError(t, c.Get("ct", &ct))
	assert.Equal(t, uint64(11), ct)
}

func TestShardedSetItem(t *testing.T) {
	c := NewSharded(2<<10, Megabyte, 256)

	assert.Equal(t, cache.ErrNotStored, c.CompareAndSwap("foo", "bar", 0, 1))
	assert.NoError(t, c.SetItem("foo", "bar", 0, 1))

	var v string
	cas, err := c.GetItem("foo", &v)
	assert.Equal(t, uint64(1), cas)
	assert.NoError(t, err)

	assert.NoError(t, c.CompareAndSwap("foo", "bar", 0, 1))
	assert.NoError(t, c.SetItem("foo", "bar", 0, 2))
	assert.Equal(t, cache.ErrCASConflict, c.CompareAndSwap("foo", "bar", 0, 1))
}

func BenchmarkGetSharded(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		var s string
		c.Get(randKey(), &s)
	}
}

func BenchmarkGetShardedParallel(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			var s string
			c.Get(randKey(), &s)
		}
	})
}

func BenchmarkSetSharded(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		c.Set(randKey(), "bar", 0)
	}
}

func BenchmarkSetShardedParallel(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			c.Set(randKey(), "bar", 0)
		}
	})
}
<commit_msg>Removes large cache for CI process issues<commit_after>package lru

import (
	"math/rand"
	"strconv"
	"testing"

	"github.com/bradberger/gocache/cache"
	"github.com/stretchr/testify/assert"
)

func randKey() string {
	return strconv.Itoa(rand.Intn(10000000))
}

func TestShardedGetSet(t *testing.T) {
	c := NewSharded(2<<10, Megabyte, 256)

	var v string
	assert.False(t, c.Exists("foo"))
	assert.NoError(t, c.Set("foo", "bar", 0))
	assert.NoError(t, c.Touch("foo", 0))
	assert.Error(t, c.Add("foo", "bar", 0))
	assert.NoError(t, c.Replace("foo", "bar"))
	assert.NoError(t, c.Prepend("foo", "foo"))
	assert.NoError(t, c.Append("foo", "foo"))
	assert.True(t, c.Exists("foo"))
	assert.NoError(t, c.Get("foo", &v))
	assert.NoError(t, c.Del("foo"))
	assert.False(t, c.Exists("foo"))
	assert.Equal(t, "foobarfoo", v)

	assert.NoError(t, c.Set("ct", uint64(2), 0))
	ct, err := c.Increment("ct", 10)

	assert.Equal(t, uint64(12), ct)
	assert.NoError(t, err)

	ct, err = c.Decrement("ct", 1)
	assert.NoError(t, err)
	assert.Equal(t, uint64(11), ct)

	assert.NoError(t, c.Get("ct", &ct))
	assert.Equal(t, uint64(11), ct)
}

func TestShardedSetItem(t *testing.T) {
	c := NewSharded(2<<10, Megabyte, 256)

	assert.Equal(t, cache.ErrNotStored, c.CompareAndSwap("foo", "bar", 0, 1))
	assert.NoError(t, c.SetItem("foo", "bar", 0, 1))

	var v string
	cas, err := c.GetItem("foo", &v)
	assert.Equal(t, uint64(1), cas)
	assert.NoError(t, err)

	assert.NoError(t, c.CompareAndSwap("foo", "bar", 0, 1))
	assert.NoError(t, c.SetItem("foo", "bar", 0, 2))
	assert.Equal(t, cache.ErrCASConflict, c.CompareAndSwap("foo", "bar", 0, 1))
}

func BenchmarkGetSharded(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		var s string
		c.Get(randKey(), &s)
	}
}

func BenchmarkGetShardedParallel(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			var s string
			c.Get(randKey(), &s)
		}
	})
}

func BenchmarkSetSharded(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		c.Set(randKey(), "bar", 0)
	}
}

func BenchmarkSetShardedParallel(b *testing.B) {
	b.StopTimer()
	c := NewSharded(2<<10, Megabyte, 256)
	b.StartTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			c.Set(randKey(), "bar", 0)
		}
	})
}
<|endoftext|>


<commit_before>package toscaviewer

// This is a basic example
// Thanks http://thenewstack.io/make-a-restful-json-api-go/ for the tutorial
import (
	"fmt"
	"github.com/owulveryck/toscalib"
	"io"
)

// PrintDot convert the ToscaDefinitionure in dot format
// in order to generate a graph with graphviz
// This function is mostly used for debugging purpose and may change a lot in the future
func PrintDot(w io.Writer, topology toscalib.ToscaDefinition) {
	fmt.Fprintf(w, "digraph G {\n")
	fmt.Fprintf(w, "\tgraph [ rankdir = \"LR\" ];\n")
	fmt.Printf("digraph G {\n")
	fmt.Printf("\tgraph [ rankdir = \"LR\" ];\n")
	for nodeName, nodeDetail := range topology.TopologyTemplate.NodeTemplates {
	        // For each node, create a record
		fmt.Fprintf(w,"\t%v [label=\"<nodeName> %v|<nodeType> %v",nodeName, nodeName, nodeDetail.Type)
		fmt.Printf("\t%v [label=\"<nodeName> %v|<nodeType> %v",nodeName, nodeName, nodeDetail.Type)
		//Display the properties
		if nodeDetail.Properties != nil  {
		    fmt.Fprintf(w, "|{Properties|{")
		    fmt.Printf("|{Properties|{")
		    for property, _ := range nodeDetail.Properties {
			fmt.Fprintf(w,"%v|",property)
			fmt.Printf("%v|",property)
		    }
		    fmt.Fprintf(w, "}}")
		    fmt.Printf("}}")
		}
		fmt.Fprintf(w,"\" shape=record style=rounded color=blue]\n")
		fmt.Printf("\" shape=record style=rounded color=blue]\n")
		// If requirements are found
//		fmt.Fprintf(w, "\t\"%v\" [ label = \"%v\" shape = circle color=blue]\n", nodeName, nodeName)
		fmt.Fprintf(w, "\t\"%v\" [ label = \"%v\" shape = record color=red]\n", nodeDetail.Type, nodeDetail.Type)
		//fmt.Fprintf(w, "\t\"%v\" -> \"%v\" [ color = red ]\n", nodeDetail.Type, nodeName)
		if nodeDetail.Requirements != nil {
			for _, requirementAssignements := range nodeDetail.Requirements {
			    for requirementName , requirementAssignement := range requirementAssignements {
				fmt.Fprintf(w, "\t%v -> %v [label = %v];\n", nodeName, requirementAssignement.Node, requirementName)
			    }
			}
		}
	}
	fmt.Fprintf(w, "}\n")
}
<commit_msg>Visualizing the requirements and the properties of a topology template.<commit_after>package toscaviewer

// This is a basic example
// Thanks http://thenewstack.io/make-a-restful-json-api-go/ for the tutorial
import (
	"fmt"
	"github.com/owulveryck/toscalib"
	"io"
)

// PrintDot convert the ToscaDefinitionure in dot format
// in order to generate a graph with graphviz
// This function is mostly used for debugging purpose and may change a lot in the future
func PrintDot(w io.Writer, topology toscalib.ToscaDefinition) {
	dotCode := fmt.Sprintf("digraph G {\n")
	dotCode = fmt.Sprintf("%v\tgraph [ rankdir = \"LR\" ];\n", dotCode)
	for nodeName, nodeDetail := range topology.TopologyTemplate.NodeTemplates {
		// For each node, create a record
		dotCode = fmt.Sprintf("%v\t%v [label=\"<nodeName> %v|<nodeType> %v", dotCode, nodeName, nodeName, nodeDetail.Type)
		//Display the properties
		if nodeDetail.Properties != nil {
			dotCode = fmt.Sprintf("%v|{Properties|{", dotCode)
			for property, _ := range nodeDetail.Properties {
				dotCode = fmt.Sprintf("%v%v|", dotCode, property)
			}
			dotCode = fmt.Sprintf("%v}}", dotCode)
		}
		// Display the requirements
		if nodeDetail.Requirements != nil {
			dotCode = fmt.Sprintf("%v|{Requirements|{", dotCode)
			i := 0
			pipe := "|"
			for _, requirementAssignements := range nodeDetail.Requirements {
				for requirement, _ := range requirementAssignements {
					if i == len(requirementAssignements) {
						pipe = ""
					}
					i = i + 1
					dotCode = fmt.Sprintf("%v<%v>%v%v", dotCode, requirement, requirement, pipe)
				}
			}
			dotCode = fmt.Sprintf("%v}}", dotCode)
		}
		// Display the capabilities
		dotCode = fmt.Sprintf("%v|{<capabilities>Capabilities|{", dotCode)
		if nodeDetail.Capabilities != nil {
			i := 1
			pipe := "|"
			for capability, _ := range nodeDetail.Capabilities {
				if i == len(nodeDetail.Capabilities) {
					pipe = ""
				}
				i = i + 1
				dotCode = fmt.Sprintf("%v<%v>%v%v", dotCode, capability, capability, pipe)
			}
		}
		dotCode = fmt.Sprintf("%v}}", dotCode)

		dotCode = fmt.Sprintf("%v\" shape=record style=rounded color=blue]\n", dotCode)
		// If requirements are found
		//		dotCode = fmt.Sprintf( "\t\"%v\" [ label = \"%v\" shape = circle color=blue]\n", nodeName, nodeName)
		//		dotCode = fmt.Sprintf( "\t\"%v\" [ label = \"%v\" shape = record color=red]\n", nodeDetail.Type, nodeDetail.Type)
		//dotCode = fmt.Sprintf( "\t\"%v\" -> \"%v\" [ color = red ]\n", nodeDetail.Type, nodeName)
		if nodeDetail.Requirements != nil {
			for _, requirementAssignements := range nodeDetail.Requirements {
				for requirementName, requirementAssignement := range requirementAssignements {

					dotCode = fmt.Sprintf("%v\t%v:%v -> %v:capabilities [label = %v color=red];\n", dotCode, nodeName, requirementName, requirementAssignement.Node, requirementName)
				}
			}
		}
	}
	dotCode = fmt.Sprintf("%v}\n", dotCode)
	fmt.Printf("DEBUG: \n%v\n", dotCode)
	fmt.Fprintf(w, "%v", dotCode)
}
<|endoftext|>


<commit_before>package stub

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
)

//InteractiveOptions renders an interactve prompt at out, in to generate an
//Options from the user. If in or out are nil, StdIn or StdOut will be used
//implicitly.
func InteractiveOptions(in, out *os.File, gameName string) *Options {

	if in == nil {
		in = os.Stdin
	}

	if out == nil {
		out = os.Stdout
	}

	result := &Options{
		Name: gameName,
	}

	if displayName := getString(out, in, "What is the human-readable display name for this game? (e.g. 'Checkers', 'Tic Tac Toe')", result.Name); displayName != result.Name {
		result.DisplayName = displayName
	}

	if description := getString(out, in, "What is the description of the game?", ""); description != "" {
		result.Description = description
	}

	numPlayersString := getString(out, in, "Range of valid players counts", "2-4")

	min, max, defaultNum, err := parseNumPlayers(numPlayersString)

	if err != nil {
		fmt.Println("That value is not valid: " + err.Error())
		return nil
	}

	result.MinNumPlayers = min
	result.MaxNumPlayers = max
	result.DefaultNumPlayers = defaultNum

	extras := getBool(out, in, "Generate useful extras like a test, CurrentPhase, and CurrentPlayer?", true)

	if !extras {
		result.SuppressExtras()
	}

	client := getBool(out, in, "Generate stub client renderers?", true)

	if !client {
		result.SuppressClient()
	}

	tutorial := getBool(out, in, "Generate extra tutorial content that demonstrate how to wire up more things?", false)

	if tutorial {
		result.EnableTutorials()
	}

	return result
}

func parseNumPlayers(in string) (min, max, defaultNum int, err error) {

	parts := strings.Split(in, "-")

	if len(parts) == 1 {
		return 0, 0, 0, errors.New("Invalid string, no '-'")
	}

	if len(parts) > 2 {
		return 0, 0, 0, errors.New("Too many '-'")
	}

	min, err = strconv.Atoi(strings.TrimSpace(parts[0]))

	if err != nil {
		return 0, 0, 0, errors.New("Min value is not an int: " + err.Error())
	}

	max, err = strconv.Atoi(strings.TrimSpace(parts[1]))

	if err != nil {
		return 0, 0, 0, errors.New("Max value is not an int: " + err.Error())
	}

	return min, max, min, nil

}

func getString(out, in *os.File, prompt, defaultValue string) string {

	if defaultValue != "" {
		prompt += " [" + defaultValue + "]"
	}

	prompt += ": "
	fmt.Fprint(out, prompt)
	var response string
	fmt.Fscanln(in, &response)

	response = strings.TrimSpace(response)

	if defaultValue != "" && response == "" {
		return defaultValue
	}

	return response
}

func getBool(out, in *os.File, message string, defaultVal bool) bool {
	if defaultVal {
		message += " [Y/n]"
	} else {
		message += " [y/N]"
	}

	response := getString(out, in, message, "")

	yesResponses := []string{"Yes", "Y", "yes", "y"}
	noResponses := []string{"No", "N", "no", "n"}

	if defaultVal {
		for _, responseToTest := range noResponses {
			if response == responseToTest {
				return false
			}
		}
		return true
	} else {
		for _, responseToTest := range yesResponses {
			if response == responseToTest {
				return true
			}
		}
	}
	return false
}
<commit_msg>Print "leave ENTER for blank" for getString calls (like description) with no ddefault. Part of #663.<commit_after>package stub

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
)

//InteractiveOptions renders an interactve prompt at out, in to generate an
//Options from the user. If in or out are nil, StdIn or StdOut will be used
//implicitly.
func InteractiveOptions(in, out *os.File, gameName string) *Options {

	if in == nil {
		in = os.Stdin
	}

	if out == nil {
		out = os.Stdout
	}

	result := &Options{
		Name: gameName,
	}

	if displayName := getString(out, in, "What is the human-readable display name for this game? (e.g. 'Checkers', 'Tic Tac Toe')", result.Name); displayName != result.Name {
		result.DisplayName = displayName
	}

	if description := getString(out, in, "What is the description of the game?", ""); description != "" {
		result.Description = description
	}

	numPlayersString := getString(out, in, "Range of valid players counts", "2-4")

	min, max, defaultNum, err := parseNumPlayers(numPlayersString)

	if err != nil {
		fmt.Println("That value is not valid: " + err.Error())
		return nil
	}

	result.MinNumPlayers = min
	result.MaxNumPlayers = max
	result.DefaultNumPlayers = defaultNum

	extras := getBool(out, in, "Generate useful extras like a test, CurrentPhase, and CurrentPlayer?", true)

	if !extras {
		result.SuppressExtras()
	}

	client := getBool(out, in, "Generate stub client renderers?", true)

	if !client {
		result.SuppressClient()
	}

	tutorial := getBool(out, in, "Generate extra tutorial content that demonstrate how to wire up more things?", false)

	if tutorial {
		result.EnableTutorials()
	}

	return result
}

func parseNumPlayers(in string) (min, max, defaultNum int, err error) {

	parts := strings.Split(in, "-")

	if len(parts) == 1 {
		return 0, 0, 0, errors.New("Invalid string, no '-'")
	}

	if len(parts) > 2 {
		return 0, 0, 0, errors.New("Too many '-'")
	}

	min, err = strconv.Atoi(strings.TrimSpace(parts[0]))

	if err != nil {
		return 0, 0, 0, errors.New("Min value is not an int: " + err.Error())
	}

	max, err = strconv.Atoi(strings.TrimSpace(parts[1]))

	if err != nil {
		return 0, 0, 0, errors.New("Max value is not an int: " + err.Error())
	}

	return min, max, min, nil

}

func getString(out, in *os.File, prompt, defaultValue string) string {

	if defaultValue == "" {
		prompt += " [press ENTER for blank]"
	} else {
		prompt += " [" + defaultValue + "]"
	}

	prompt += ": "
	fmt.Fprint(out, prompt)
	var response string
	fmt.Fscanln(in, &response)

	response = strings.TrimSpace(response)

	if response == "" {
		return defaultValue
	}

	return response
}

func getBool(out, in *os.File, message string, defaultVal bool) bool {
	if defaultVal {
		message += " [Y/n]"
	} else {
		message += " [y/N]"
	}

	response := getString(out, in, message, "")

	yesResponses := []string{"Yes", "Y", "yes", "y"}
	noResponses := []string{"No", "N", "no", "n"}

	if defaultVal {
		for _, responseToTest := range noResponses {
			if response == responseToTest {
				return false
			}
		}
		return true
	} else {
		for _, responseToTest := range yesResponses {
			if response == responseToTest {
				return true
			}
		}
	}
	return false
}
<|endoftext|>