<commit_before>package MySQLProtocol

import "testing"
import "github.com/stretchr/testify/assert"
import "fmt"

var fields_test_packets = []struct {
	field_type byte
	packet     Proto
	context    Context
}{
	{field_type: MYSQL_TYPE_STRING,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_VARCHAR,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_VAR_STRING,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_ENUM,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_SET,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_LONG_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_MEDIUM_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TINY_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_GEOMETRY,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_BIT,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DECIMAL,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_NEWDECIMAL,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_LONGLONG,
		packet:  Proto{data: StringToPacket(`01 00 00 00 00 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_LONG,
		packet:  Proto{data: StringToPacket(`01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_INT24,
		packet:  Proto{data: StringToPacket(`01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_SHORT,
		packet:  Proto{data: StringToPacket(`01 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_YEAR,
		packet:  Proto{data: StringToPacket(`01 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TINY,
		packet:  Proto{data: StringToPacket(`01`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DOUBLE,
		packet:  Proto{data: StringToPacket(`66 66 66 66 66 66 24 40`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_FLOAT,
		packet:  Proto{data: StringToPacket(`33 33 23 41`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DATE,
		packet:  Proto{data: StringToPacket(`04 da 07 0a 11`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_NEWDATE,
		packet:  Proto{data: StringToPacket(`04 da 07 0a 11`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DATETIME,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DATETIME2,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIMESTAMP,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIMESTAMP2,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME,
		packet:  Proto{data: StringToPacket(`0c 01 78 00 00 00 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME,
		packet:  Proto{data: StringToPacket(`08 01 78 00 00 00 13 1b 1e`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME,
		packet:  Proto{data: StringToPacket(`01`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME2,
		packet:  Proto{data: StringToPacket(`0c 01 78 00 00 00 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME2,
		packet:  Proto{data: StringToPacket(`08 01 78 00 00 00 13 1b 1e`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME2,
		packet:  Proto{data: StringToPacket(`01`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_NULL,
		packet:  Proto{data: StringToPacket(``)},
		context: Context{}},
}

func Test_Packet_fields(t *testing.T) {
	var field Field_interface
	var packet Proto
	var error_msg string

	for _, value := range fields_test_packets {
		field = value.packet.GetField(value.field_type)

		error_msg = fmt.Sprintf("Error with type %d", value.field_type)

		assert.Equal(t, field.GetType(), value.field_type, error_msg)
		assert.Equal(t, field.Build(), value.packet.data, error_msg)

		error_msg = fmt.Sprintf("Error with type %d, size %d expected %d", value.field_type, int(field.PacketSize()), len(value.packet.data))

		assert.Equal(t, int(field.PacketSize()), len(value.packet.data), error_msg)
	}

	// Test unknown fields
	packet = Proto{data: StringToPacket(``)}
	field = packet.GetField(MYSQL_TYPE_UNKNOWN)
	assert.Equal(t, field, nil, "Unknown field does not return nil")
}
<commit_msg>Factor out a bunch of hard coded function calls<commit_after>package MySQLProtocol

import "testing"
import "github.com/stretchr/testify/assert"
import "fmt"

var fields_test_packets = []struct {
	field_type byte
	packet     Proto
	context    Context
}{
	{field_type: MYSQL_TYPE_STRING,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_VARCHAR,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_VAR_STRING,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_ENUM,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_SET,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_LONG_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_MEDIUM_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TINY_BLOB,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_GEOMETRY,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_BIT,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DECIMAL,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_NEWDECIMAL,
		packet:  Proto{data: StringToPacket(`03 66 6f 6f`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_LONGLONG,
		packet:  Proto{data: StringToPacket(`01 00 00 00 00 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_LONG,
		packet:  Proto{data: StringToPacket(`01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_INT24,
		packet:  Proto{data: StringToPacket(`01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_SHORT,
		packet:  Proto{data: StringToPacket(`01 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_YEAR,
		packet:  Proto{data: StringToPacket(`01 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TINY,
		packet:  Proto{data: StringToPacket(`01`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DOUBLE,
		packet:  Proto{data: StringToPacket(`66 66 66 66 66 66 24 40`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_FLOAT,
		packet:  Proto{data: StringToPacket(`33 33 23 41`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DATE,
		packet:  Proto{data: StringToPacket(`04 da 07 0a 11`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_NEWDATE,
		packet:  Proto{data: StringToPacket(`04 da 07 0a 11`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DATETIME,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_DATETIME2,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIMESTAMP,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIMESTAMP2,
		packet:  Proto{data: StringToPacket(`0b da 07 0a 11 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME,
		packet:  Proto{data: StringToPacket(`0c 01 78 00 00 00 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME,
		packet:  Proto{data: StringToPacket(`08 01 78 00 00 00 13 1b 1e`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME,
		packet:  Proto{data: StringToPacket(`01`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME2,
		packet:  Proto{data: StringToPacket(`0c 01 78 00 00 00 13 1b 1e 01 00 00 00`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME2,
		packet:  Proto{data: StringToPacket(`08 01 78 00 00 00 13 1b 1e`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_TIME2,
		packet:  Proto{data: StringToPacket(`01`)},
		context: Context{}},

	{field_type: MYSQL_TYPE_NULL,
		packet:  Proto{data: StringToPacket(``)},
		context: Context{}},
}

func Test_Packet_fields(t *testing.T) {
	var field Field_interface
	var packet Proto
	var error_msg string
    var expected_size int
    var size int

	for _, value := range fields_test_packets {
		field = value.packet.GetField(value.field_type)

		error_msg = fmt.Sprintf("Error with type %d", value.field_type)

		assert.Equal(t, field.GetType(), value.field_type, error_msg)
		assert.Equal(t, field.Build(), value.packet.data, error_msg)

        expected_size = int(field.PacketSize())
        size = len(value.packet.data)

		error_msg = fmt.Sprintf("Error with type %d, size %d expected %d", value.field_type, expected_size, size)

		assert.Equal(t, expected_size, size, error_msg)
	}

	// Test unknown fields
	packet = Proto{data: StringToPacket(``)}
	field = packet.GetField(MYSQL_TYPE_UNKNOWN)
	assert.Equal(t, field, nil, "Unknown field does not return nil")
}
<|endoftext|>


<commit_before>package testwriter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"sync"
	"time"
	"veyron.io/veyron/veyron/services/wsprd/lib"
)

type TestHarness interface {
	Errorf(fmt string, a ...interface{})
}

type Response struct {
	Type    lib.ResponseType
	Message interface{}
}

type Writer struct {
	sync.Mutex
	Stream []Response
	err    error
	// If this channel is set then a message will be sent
	// to this channel after recieving a call to FinishMessage()
	notifier chan bool
}

func (w *Writer) Send(responseType lib.ResponseType, msg interface{}) error {
	// We serialize and deserialize the reponse so that we can do deep equal with
	// messages that contain non-exported structs.
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(Response{Type: responseType, Message: msg}); err != nil {
		return err
	}

	var r Response

	if err := json.NewDecoder(&buf).Decode(&r); err != nil {
		return err
	}

	w.Lock()
	defer w.Unlock()
	w.Stream = append(w.Stream, r)
	if w.notifier != nil {
		w.notifier <- true
	}
	return nil

}

func (w *Writer) Error(err error) {
	w.err = err
}

func (w *Writer) streamLength() int {
	w.Lock()
	defer w.Unlock()
	return len(w.Stream)
}

// Waits until there is at least n messages in w.Stream. Returns an error if we timeout
// waiting for the given number of messages.
func (w *Writer) WaitForMessage(n int) error {
	if w.streamLength() >= n {
		return nil
	}
	w.Lock()
	w.notifier = make(chan bool, 1)
	w.Unlock()
	for w.streamLength() < n {
		select {
		case <-w.notifier:
			continue
		case <-time.After(time.Second):
			return fmt.Errorf("timed out")
		}
	}
	w.Lock()
	fmt.Printf("Looking for %d, got %d: %v", n, len(w.Stream), w.Stream)
	w.notifier = nil
	w.Unlock()
	return nil
}

func CheckResponses(w *Writer, expectedStream []Response, err error, t TestHarness) {
	if !reflect.DeepEqual(expectedStream, w.Stream) {
		t.Errorf("streams don't match: expected %v, got %v", expectedStream, w.Stream)
	}

	if !reflect.DeepEqual(err, w.err) {
		t.Errorf("unexpected error, got: %v, expected: %v", w.err, err)
	}
}
<commit_msg>veyron/services/wsprd:  Add more logging to track down race.<commit_after>package testwriter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"sync"
	"time"
	"veyron.io/veyron/veyron/services/wsprd/lib"
)

type TestHarness interface {
	Errorf(fmt string, a ...interface{})
}

type Response struct {
	Type    lib.ResponseType
	Message interface{}
}

type Writer struct {
	sync.Mutex
	Stream []Response
	err    error
	// If this channel is set then a message will be sent
	// to this channel after recieving a call to FinishMessage()
	notifier chan bool
}

func (w *Writer) Send(responseType lib.ResponseType, msg interface{}) error {
	// We serialize and deserialize the reponse so that we can do deep equal with
	// messages that contain non-exported structs.
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(Response{Type: responseType, Message: msg}); err != nil {
		return err
	}

	var r Response

	if err := json.NewDecoder(&buf).Decode(&r); err != nil {
		return err
	}

	w.Lock()
	defer w.Unlock()
	w.Stream = append(w.Stream, r)
	if w.notifier != nil {
		w.notifier <- true
	}
	return nil

}

func (w *Writer) Error(err error) {
	w.err = err
}

func (w *Writer) streamLength() int {
	w.Lock()
	defer w.Unlock()
	return len(w.Stream)
}

// Waits until there is at least n messages in w.Stream. Returns an error if we timeout
// waiting for the given number of messages.
func (w *Writer) WaitForMessage(n int) error {
	fmt.Println("Looking for", n)
	if w.streamLength() >= n {
		fmt.Println("Found ", w.Stream)
		return nil
	}
	w.Lock()
	w.notifier = make(chan bool, 1)
	w.Unlock()
	for w.streamLength() < n {
		select {
		case <-w.notifier:
			continue
		case <-time.After(time.Second):
			return fmt.Errorf("timed out")
		}
	}
	w.Lock()
	fmt.Printf("Looking for %d, got %d: %v", n, len(w.Stream), w.Stream)
	w.notifier = nil
	w.Unlock()
	return nil
}

func CheckResponses(w *Writer, expectedStream []Response, err error, t TestHarness) {
	if !reflect.DeepEqual(expectedStream, w.Stream) {
		t.Errorf("streams don't match: expected %v, got %v", expectedStream, w.Stream)
	}

	if !reflect.DeepEqual(err, w.err) {
		t.Errorf("unexpected error, got: %v, expected: %v", w.err, err)
	}
}
<|endoftext|>


<commit_before>package shellexec

import (
	"bytes"
	"errors"
	"os"
	"os/exec"
	"unicode"
	"unicode/utf8"
)

// Command parses line using a shell-like syntax and returns
// the os/exec.Cmd struct to execute the line.
func Command(line string) (*exec.Cmd, error) {
	s := parser{s: line, getenv: os.Getenv}

	c, err := s.parseLine()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command(c.cmd, c.args...)
	cmd.Env = c.env
	return cmd, nil
}

type cmd struct {
	cmd  string
	args []string
	env  []string
}

type parser struct {
	buf    bytes.Buffer
	s      string
	last   rune
	peeked *rune
	getenv func(key string) string

	identBuf bytes.Buffer
}

const eof rune = utf8.MaxRune + 1

func (p *parser) next() rune {
	if p.peeked != nil {
		r := *p.peeked
		p.peeked = nil
		return r
	}

	if len(p.s) == 0 {
		p.last = eof
		return eof
	}
	var size int
	p.last, size = utf8.DecodeRuneInString(p.s)
	p.s = p.s[size:]

	if p.last == '\\' && p.s != "" && p.s[0] == '\n' {
		// line continuation; remove it from the input
		p.s = p.s[1:]
		return p.next()
	}

	return p.last
}

func (p *parser) backup() {
	p.peeked = &p.last
}

func (p *parser) token() string {
	t := p.buf.String()
	p.buf.Reset()
	return t
}

func (p *parser) parseLine() (cmd, error) {
	var c cmd
loop:
	for {
		r := p.next()
		switch {
		case unicode.IsSpace(r):
			continue
		case r == eof:
			break loop
		}
		p.backup()

		if c.cmd == "" {
			if err := p.parseVarAssign(); err == errNotAssign {
				c.cmd = p.token()
			} else if err != nil {
				return cmd{}, err
			} else {
				c.env = append(c.env, p.token())
			}
		} else {
			if err := p.parseField(); err != nil {
				return cmd{}, err
			}
			c.args = append(c.args, p.token())
		}
	}
	if c.cmd == "" {
		return cmd{}, errors.New("empty command")
	}
	return c, nil
}

var errNotAssign = errors.New("not assignment")

func (p *parser) parseVarAssign() error {
	v := p.parseIdent()
	p.buf.WriteString(v)
	err := errNotAssign
	if v != "" && p.next() == '=' {
		err = nil
	}
	p.backup()

	if err := p.parseField(); err != nil {
		return err
	}
	return err
}

func (p *parser) parseField() error {
	esc := false
	for {
		r := p.next()
		if r == eof {
			break
		}

		if esc {
			p.buf.WriteRune(r)
			esc = false
			continue
		}
		if unicode.IsSpace(r) {
			break
		}
		switch r {
		case '\'':
			if err := p.parseSingleQuotes(); err != nil {
				return err
			}
		case '"':
			if err := p.parseDoubleQuotes(); err != nil {
				return err
			}
		case '\\':
			esc = true
			continue
		case '$':
			if err := p.parseVarExpr(); err != nil {
				return err
			}
			p.backup()
			continue
		case '|', '&', ';', '<', '>', '(', ')', '`',
			// Forbid these characters as they may need to be
			// quoted under certain circumstances.
			'*', '?', '[', '#', '~':
			return errors.New("unsupported character: " + string(r))
		default:
			p.buf.WriteRune(r)
		}
	}
	return nil
}

func (p *parser) parseSingleQuotes() error {
	for {
		switch r := p.next(); r {
		case '\'':
			return nil
		case eof:
			return errors.New("string not terminated")
		default:
			p.buf.WriteRune(r)
		}
	}
	panic("unreachable")
}

func (p *parser) parseDoubleQuotes() error {
	var esc bool
	for {
		r := p.next()
		if r == eof {
			return errors.New("string not terminated")
		}

		if esc {
			switch r {
			default:
				p.buf.WriteRune('\\')
				fallthrough
			case '$', '`', '"', '\\':
				p.buf.WriteRune(r)
			}
			esc = false
			continue
		}
		switch r {
		case '"':
			return nil
		case '\\':
			esc = true
			continue
		case '$':
			if err := p.parseVarExpr(); err != nil {
				return err
			}
			continue
		case '`':
			return errors.New("unsupported character inside string: " + string(r))
		}
		p.buf.WriteRune(r)
	}
	panic("unreachable")
}

func (p *parser) parseVarExpr() error {
	switch p.next() {
	case '(':
		return errors.New("command substitution or arithmetic expansion not supported")
	case '{':
		return errors.New("parameter expansion not supported")
	case '@', '*', '#', '?', '-', '$', '!', '0':
		return errors.New("special parameters not supported")
	case '1', '2', '3', '4', '5', '6', '7', '8', '9':
		return errors.New("positional parameters not supported")
	}
	p.backup()

	v := "$"
	if name := p.parseIdent(); name != "" {
		v = p.getenv(name)
	}
	p.buf.WriteString(v)
	return nil
}

func (p *parser) parseIdent() string {
	p.identBuf.Reset()
	for {
		r := p.next()
		if !(r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '_' ||
			p.identBuf.Len() > 0 && r >= '0' && r <= '9') {
			p.backup()
			return p.identBuf.String()
		}
		p.identBuf.WriteRune(r)
	}
	panic("unreachable")
}
<commit_msg>Fix typo<commit_after>package shellexec

import (
	"bytes"
	"errors"
	"os"
	"os/exec"
	"unicode"
	"unicode/utf8"
)

// Command parses line using a shell-like syntax and returns
// the os/exec.Cmd struct to execute the line.
func Command(line string) (*exec.Cmd, error) {
	p := parser{s: line, getenv: os.Getenv}

	c, err := p.parseLine()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command(c.cmd, c.args...)
	cmd.Env = c.env
	return cmd, nil
}

type cmd struct {
	cmd  string
	args []string
	env  []string
}

type parser struct {
	buf    bytes.Buffer
	s      string
	last   rune
	peeked *rune
	getenv func(key string) string

	identBuf bytes.Buffer
}

const eof rune = utf8.MaxRune + 1

func (p *parser) next() rune {
	if p.peeked != nil {
		r := *p.peeked
		p.peeked = nil
		return r
	}

	if len(p.s) == 0 {
		p.last = eof
		return eof
	}
	var size int
	p.last, size = utf8.DecodeRuneInString(p.s)
	p.s = p.s[size:]

	if p.last == '\\' && p.s != "" && p.s[0] == '\n' {
		// line continuation; remove it from the input
		p.s = p.s[1:]
		return p.next()
	}

	return p.last
}

func (p *parser) backup() {
	p.peeked = &p.last
}

func (p *parser) token() string {
	t := p.buf.String()
	p.buf.Reset()
	return t
}

func (p *parser) parseLine() (cmd, error) {
	var c cmd
loop:
	for {
		r := p.next()
		switch {
		case unicode.IsSpace(r):
			continue
		case r == eof:
			break loop
		}
		p.backup()

		if c.cmd == "" {
			if err := p.parseVarAssign(); err == errNotAssign {
				c.cmd = p.token()
			} else if err != nil {
				return cmd{}, err
			} else {
				c.env = append(c.env, p.token())
			}
		} else {
			if err := p.parseField(); err != nil {
				return cmd{}, err
			}
			c.args = append(c.args, p.token())
		}
	}
	if c.cmd == "" {
		return cmd{}, errors.New("empty command")
	}
	return c, nil
}

var errNotAssign = errors.New("not assignment")

func (p *parser) parseVarAssign() error {
	v := p.parseIdent()
	p.buf.WriteString(v)
	err := errNotAssign
	if v != "" && p.next() == '=' {
		err = nil
	}
	p.backup()

	if err := p.parseField(); err != nil {
		return err
	}
	return err
}

func (p *parser) parseField() error {
	esc := false
	for {
		r := p.next()
		if r == eof {
			break
		}

		if esc {
			p.buf.WriteRune(r)
			esc = false
			continue
		}
		if unicode.IsSpace(r) {
			break
		}
		switch r {
		case '\'':
			if err := p.parseSingleQuotes(); err != nil {
				return err
			}
		case '"':
			if err := p.parseDoubleQuotes(); err != nil {
				return err
			}
		case '\\':
			esc = true
			continue
		case '$':
			if err := p.parseVarExpr(); err != nil {
				return err
			}
			p.backup()
			continue
		case '|', '&', ';', '<', '>', '(', ')', '`',
			// Forbid these characters as they may need to be
			// quoted under certain circumstances.
			'*', '?', '[', '#', '~':
			return errors.New("unsupported character: " + string(r))
		default:
			p.buf.WriteRune(r)
		}
	}
	return nil
}

func (p *parser) parseSingleQuotes() error {
	for {
		switch r := p.next(); r {
		case '\'':
			return nil
		case eof:
			return errors.New("string not terminated")
		default:
			p.buf.WriteRune(r)
		}
	}
	panic("unreachable")
}

func (p *parser) parseDoubleQuotes() error {
	var esc bool
	for {
		r := p.next()
		if r == eof {
			return errors.New("string not terminated")
		}

		if esc {
			switch r {
			default:
				p.buf.WriteRune('\\')
				fallthrough
			case '$', '`', '"', '\\':
				p.buf.WriteRune(r)
			}
			esc = false
			continue
		}
		switch r {
		case '"':
			return nil
		case '\\':
			esc = true
			continue
		case '$':
			if err := p.parseVarExpr(); err != nil {
				return err
			}
			continue
		case '`':
			return errors.New("unsupported character inside string: " + string(r))
		}
		p.buf.WriteRune(r)
	}
	panic("unreachable")
}

func (p *parser) parseVarExpr() error {
	switch p.next() {
	case '(':
		return errors.New("command substitution or arithmetic expansion not supported")
	case '{':
		return errors.New("parameter expansion not supported")
	case '@', '*', '#', '?', '-', '$', '!', '0':
		return errors.New("special parameters not supported")
	case '1', '2', '3', '4', '5', '6', '7', '8', '9':
		return errors.New("positional parameters not supported")
	}
	p.backup()

	v := "$"
	if name := p.parseIdent(); name != "" {
		v = p.getenv(name)
	}
	p.buf.WriteString(v)
	return nil
}

func (p *parser) parseIdent() string {
	p.identBuf.Reset()
	for {
		r := p.next()
		if !(r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '_' ||
			p.identBuf.Len() > 0 && r >= '0' && r <= '9') {
			p.backup()
			return p.identBuf.String()
		}
		p.identBuf.WriteRune(r)
	}
	panic("unreachable")
}
<|endoftext|>


<commit_before>package iso

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/mitchellh/packer/packer"
)

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"iso_checksum":      "foo",
		"iso_checksum_type": "md5",
		"iso_url":           "http://www.packer.io",
		"shutdown_command":  "foo",
		"ssh_username":      "foo",

		packer.BuildNameConfigKey: "foo",
	}
}

func TestBuilder_ImplementsBuilder(t *testing.T) {
	var raw interface{}
	raw = &Builder{}
	if _, ok := raw.(packer.Builder); !ok {
		t.Error("Builder must implement builder.")
	}
}

func TestBuilderPrepare_Defaults(t *testing.T) {
	var b Builder
	config := testConfig()
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskName != "disk" {
		t.Errorf("bad disk name: %s", b.config.DiskName)
	}

	if b.config.OutputDir != "output-foo" {
		t.Errorf("bad output dir: %s", b.config.OutputDir)
	}

	if b.config.Version != "9" {
		t.Errorf("bad Version: %s", b.config.Version)
	}

	if b.config.VMName != "packer-foo" {
		t.Errorf("bad vm name: %s", b.config.VMName)
	}
}

func TestBuilderPrepare_DiskSize(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "disk_size")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if b.config.DiskSize != 40000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}

	config["disk_size"] = 60000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskSize != 60000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}
}

func TestBuilderPrepare_FloppyFiles(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "floppy_files")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if len(b.config.FloppyFiles) != 0 {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}

	floppies_path := "../../../common/test-fixtures/floppies"
	config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	expected := []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}
}

func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
	var b Builder
	config := testConfig()
	config["floppy_files"] = []string{"nonexistant.bat", "nonexistant.ps1"}
	b = Builder{}
	_, errs := b.Prepare(config)
	if errs == nil {
		t.Fatalf("Non existant floppies should trigger multierror")
	}

	if len(errs.(*packer.MultiError).Errors) != 2 {
		t.Fatalf("Multierror should work and report 2 errors")
	}
}

func TestBuilderPrepare_RemoteType(t *testing.T) {
	var b Builder
	config := testConfig()

	config["format"] = "ovf"
	config["remote_host"] = "foobar.example.com"
	// Bad
	config["remote_type"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	config["remote_host"] = ""
	config["remote_type"] = "esx5"
	// Bad
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["remote_type"] = "esx5"
	config["remote_host"] = "foobar.example.com"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_Format(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["format"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	goodFormats := []string{"ova", "ovf", "vmx"}

	for _, format := range goodFormats {
		// Good
		config["format"] = format
		b = Builder{}
		warns, err = b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}
	}
}

func TestBuilderPrepare_InvalidKey(t *testing.T) {
	var b Builder
	config := testConfig()

	// Add a random key
	config["i_should_not_be_valid"] = true
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_OutputDir(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test with existing dir
	dir, err := ioutil.TempDir("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(dir)

	config["output_directory"] = dir
	b = Builder{}
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a good one
	config["output_directory"] = "i-hope-i-dont-exist"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test a default
	delete(config, "tools_upload_path")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if b.config.ToolsUploadPath == "" {
		t.Fatalf("bad value: %s", b.config.ToolsUploadPath)
	}

	// Test with a bad value
	config["tools_upload_path"] = "{{{nope}"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	config["tools_upload_path"] = "hey"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test bad
	config["vmx_template_path"] = "/i/dont/exist/forreal"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test good
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	if _, err := tf.Write([]byte("HELLO!")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Bad template
	tf2, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf2.Name())
	defer tf2.Close()

	if _, err := tf2.Write([]byte("{{foo}")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf2.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_VNCPort(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["vnc_port_min"] = 1000
	config["vnc_port_max"] = 500
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Bad
	config["vnc_port_min"] = -500
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["vnc_port_min"] = 500
	config["vnc_port_max"] = 1000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_CommConfig(t *testing.T) {
	// Test Winrm
	{
		config := testConfig()
		config["communicator"] = "winrm"
		config["winrm_username"] = "username"
		config["winrm_password"] = "password"
		config["winrm_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.WinRMUser != "username" {
			t.Errorf("bad winrm_username: %s", b.config.CommConfig.WinRMUser)
		}
		if b.config.CommConfig.WinRMPassword != "password" {
			t.Errorf("bad winrm_password: %s", b.config.CommConfig.WinRMPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}

	// Test SSH
	{
		config := testConfig()
		config["communicator"] = "ssh"
		config["ssh_username"] = "username"
		config["ssh_password"] = "password"
		config["ssh_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.SSHUsername != "username" {
			t.Errorf("bad ssh_username: %s", b.config.CommConfig.SSHUsername)
		}
		if b.config.CommConfig.SSHPassword != "password" {
			t.Errorf("bad ssh_password: %s", b.config.CommConfig.SSHPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}
}
<commit_msg>Add test for empty remote_type value<commit_after>package iso

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/mitchellh/packer/packer"
)

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"iso_checksum":      "foo",
		"iso_checksum_type": "md5",
		"iso_url":           "http://www.packer.io",
		"shutdown_command":  "foo",
		"ssh_username":      "foo",

		packer.BuildNameConfigKey: "foo",
	}
}

func TestBuilder_ImplementsBuilder(t *testing.T) {
	var raw interface{}
	raw = &Builder{}
	if _, ok := raw.(packer.Builder); !ok {
		t.Error("Builder must implement builder.")
	}
}

func TestBuilderPrepare_Defaults(t *testing.T) {
	var b Builder
	config := testConfig()
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskName != "disk" {
		t.Errorf("bad disk name: %s", b.config.DiskName)
	}

	if b.config.OutputDir != "output-foo" {
		t.Errorf("bad output dir: %s", b.config.OutputDir)
	}

	if b.config.Version != "9" {
		t.Errorf("bad Version: %s", b.config.Version)
	}

	if b.config.VMName != "packer-foo" {
		t.Errorf("bad vm name: %s", b.config.VMName)
	}
}

func TestBuilderPrepare_DiskSize(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "disk_size")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if b.config.DiskSize != 40000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}

	config["disk_size"] = 60000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskSize != 60000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}
}

func TestBuilderPrepare_FloppyFiles(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "floppy_files")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if len(b.config.FloppyFiles) != 0 {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}

	floppies_path := "../../../common/test-fixtures/floppies"
	config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	expected := []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}
}

func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
	var b Builder
	config := testConfig()
	config["floppy_files"] = []string{"nonexistant.bat", "nonexistant.ps1"}
	b = Builder{}
	_, errs := b.Prepare(config)
	if errs == nil {
		t.Fatalf("Non existant floppies should trigger multierror")
	}

	if len(errs.(*packer.MultiError).Errors) != 2 {
		t.Fatalf("Multierror should work and report 2 errors")
	}
}

func TestBuilderPrepare_RemoteType(t *testing.T) {
	var b Builder
	config := testConfig()

	config["format"] = "ovf"
	config["remote_host"] = "foobar.example.com"
	// Bad
	config["remote_type"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	config["remote_host"] = ""
	config["remote_type"] = ""
	// Bad
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["remote_type"] = "esx5"
	config["remote_host"] = "foobar.example.com"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Good
	config["remote_type"] = "esx5"
	config["remote_host"] = "foobar.example.com"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_Format(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["format"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	goodFormats := []string{"ova", "ovf", "vmx"}

	for _, format := range goodFormats {
		// Good
		config["format"] = format
		b = Builder{}
		warns, err = b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}
	}
}

func TestBuilderPrepare_InvalidKey(t *testing.T) {
	var b Builder
	config := testConfig()

	// Add a random key
	config["i_should_not_be_valid"] = true
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_OutputDir(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test with existing dir
	dir, err := ioutil.TempDir("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(dir)

	config["output_directory"] = dir
	b = Builder{}
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a good one
	config["output_directory"] = "i-hope-i-dont-exist"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test a default
	delete(config, "tools_upload_path")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if b.config.ToolsUploadPath == "" {
		t.Fatalf("bad value: %s", b.config.ToolsUploadPath)
	}

	// Test with a bad value
	config["tools_upload_path"] = "{{{nope}"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	config["tools_upload_path"] = "hey"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test bad
	config["vmx_template_path"] = "/i/dont/exist/forreal"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test good
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	if _, err := tf.Write([]byte("HELLO!")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Bad template
	tf2, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf2.Name())
	defer tf2.Close()

	if _, err := tf2.Write([]byte("{{foo}")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf2.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_VNCPort(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["vnc_port_min"] = 1000
	config["vnc_port_max"] = 500
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Bad
	config["vnc_port_min"] = -500
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["vnc_port_min"] = 500
	config["vnc_port_max"] = 1000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_CommConfig(t *testing.T) {
	// Test Winrm
	{
		config := testConfig()
		config["communicator"] = "winrm"
		config["winrm_username"] = "username"
		config["winrm_password"] = "password"
		config["winrm_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.WinRMUser != "username" {
			t.Errorf("bad winrm_username: %s", b.config.CommConfig.WinRMUser)
		}
		if b.config.CommConfig.WinRMPassword != "password" {
			t.Errorf("bad winrm_password: %s", b.config.CommConfig.WinRMPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}

	// Test SSH
	{
		config := testConfig()
		config["communicator"] = "ssh"
		config["ssh_username"] = "username"
		config["ssh_password"] = "password"
		config["ssh_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.SSHUsername != "username" {
			t.Errorf("bad ssh_username: %s", b.config.CommConfig.SSHUsername)
		}
		if b.config.CommConfig.SSHPassword != "password" {
			t.Errorf("bad ssh_password: %s", b.config.CommConfig.SSHPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}
}
<|endoftext|>


<commit_before>package resource

import (
	"database/sql"
	"errors"
	"reflect"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
)

// ErrProcessorSkipLeft skip left processors error, if returned this error in validation, before callbacks, then qor will stop process following processors
var ErrProcessorSkipLeft = errors.New("resource: skip left")

type processor struct {
	Result     interface{}
	Resource   Resourcer
	Context    *qor.Context
	MetaValues *MetaValues
	SkipLeft   bool
	newRecord  bool
}

// DecodeToResource decode meta values to resource result
func DecodeToResource(res Resourcer, result interface{}, metaValues *MetaValues, context *qor.Context) *processor {
	scope := &gorm.Scope{Value: result}
	return &processor{Resource: res, Result: result, Context: context, MetaValues: metaValues, newRecord: scope.PrimaryKeyZero()}
}

func (processor *processor) checkSkipLeft(errs ...error) bool {
	if processor.SkipLeft {
		return true
	}

	for _, err := range errs {
		if err == ErrProcessorSkipLeft {
			processor.SkipLeft = true
			break
		}
	}
	return processor.SkipLeft
}

// Initialize initialize a processor
func (processor *processor) Initialize() error {
	err := processor.Resource.CallFindOne(processor.Result, processor.MetaValues, processor.Context)
	processor.checkSkipLeft(err)
	return err
}

// Validate run validators
func (processor *processor) Validate() error {
	var errors qor.Errors
	if processor.checkSkipLeft() {
		return nil
	}

	for _, v := range processor.Resource.GetResource().Validators {
		if errors.AddError(v.Handler(processor.Result, processor.MetaValues, processor.Context)); !errors.HasError() {
			if processor.checkSkipLeft(errors.GetErrors()...) {
				break
			}
		}
	}
	return errors
}

func (processor *processor) decode() (errors []error) {
	if processor.checkSkipLeft() || processor.MetaValues == nil {
		return
	}

	if destroy := processor.MetaValues.Get("_destroy"); destroy != nil {
		return
	}

	for _, metaValue := range processor.MetaValues.Values {
		meta := metaValue.Meta
		if meta == nil {
			continue
		}

		if processor.newRecord && !meta.HasPermission(roles.Create, processor.Context) {
			continue
		} else if !processor.newRecord && !meta.HasPermission(roles.Update, processor.Context) {
			continue
		}

		if setter := meta.GetSetter(); setter != nil {
			setter(processor.Result, metaValue, processor.Context)
		}

		if metaValue.MetaValues != nil && len(metaValue.MetaValues.Values) > 0 {
			if res := metaValue.Meta.GetResource(); res != nil && !reflect.ValueOf(res).IsNil() {
				field := reflect.Indirect(reflect.ValueOf(processor.Result)).FieldByName(meta.GetFieldName())
				// Only decode nested meta value into struct if no Setter defined
				if meta.GetSetter() == nil || reflect.Indirect(field).Type() == utils.ModelType(res.NewStruct()) {
					if _, ok := field.Addr().Interface().(sql.Scanner); !ok {
						decodeMetaValuesToField(res, field, metaValue, processor.Context)
					}
				}
			}
		}
	}

	return
}

// Start start processor
func (processor *processor) Start() error {
	var errors qor.Errors
	processor.Initialize()
	if errors.AddError(processor.Validate()); !errors.HasError() {
		errors.AddError(processor.Commit())
	}
	if errors.HasError() {
		return errors
	}
	return nil
}

// Commit commit data into result
func (processor *processor) Commit() error {
	var errors qor.Errors
	errors.AddError(processor.decode()...)
	if processor.checkSkipLeft(errors.GetErrors()...) {
		return nil
	}

	for _, p := range processor.Resource.GetResource().Processors {
		if err := p.Handler(processor.Result, processor.MetaValues, processor.Context); err != nil {
			if processor.checkSkipLeft(err) {
				break
			}
			errors.AddError(err)
		}
	}
	return errors
}
<commit_msg>Refactor processor<commit_after>package resource

import (
	"database/sql"
	"errors"
	"reflect"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
)

// ErrProcessorSkipLeft skip left processors error, if returned this error in validation, before callbacks, then qor will stop process following processors
var ErrProcessorSkipLeft = errors.New("resource: skip left")

type processor struct {
	Result     interface{}
	Resource   Resourcer
	Context    *qor.Context
	MetaValues *MetaValues
	SkipLeft   bool
	newRecord  bool
}

// DecodeToResource decode meta values to resource result
func DecodeToResource(res Resourcer, result interface{}, metaValues *MetaValues, context *qor.Context) *processor {
	scope := &gorm.Scope{Value: result}
	return &processor{Resource: res, Result: result, Context: context, MetaValues: metaValues, newRecord: scope.PrimaryKeyZero()}
}

func (processor *processor) checkSkipLeft(errs ...error) bool {
	if processor.SkipLeft {
		return true
	}

	for _, err := range errs {
		if err == ErrProcessorSkipLeft {
			processor.SkipLeft = true
			break
		}
	}
	return processor.SkipLeft
}

// Initialize initialize a processor
func (processor *processor) Initialize() error {
	err := processor.Resource.CallFindOne(processor.Result, processor.MetaValues, processor.Context)
	processor.checkSkipLeft(err)
	return err
}

// Validate run validators
func (processor *processor) Validate() error {
	var errs qor.Errors
	if processor.checkSkipLeft() {
		return nil
	}

	for _, v := range processor.Resource.GetResource().Validators {
		if errs.AddError(v.Handler(processor.Result, processor.MetaValues, processor.Context)); !errs.HasError() {
			if processor.checkSkipLeft(errs.GetErrors()...) {
				break
			}
		}
	}
	return errs
}

func (processor *processor) decode() (errs []error) {
	if processor.checkSkipLeft() || processor.MetaValues == nil {
		return
	}

	if destroy := processor.MetaValues.Get("_destroy"); destroy != nil {
		return
	}

	for _, metaValue := range processor.MetaValues.Values {
		meta := metaValue.Meta
		if meta == nil {
			continue
		}

		if processor.newRecord && !meta.HasPermission(roles.Create, processor.Context) {
			continue
		} else if !processor.newRecord && !meta.HasPermission(roles.Update, processor.Context) {
			continue
		}

		if setter := meta.GetSetter(); setter != nil {
			setter(processor.Result, metaValue, processor.Context)
		}

		if metaValue.MetaValues != nil && len(metaValue.MetaValues.Values) > 0 {
			if res := metaValue.Meta.GetResource(); res != nil && !reflect.ValueOf(res).IsNil() {
				field := reflect.Indirect(reflect.ValueOf(processor.Result)).FieldByName(meta.GetFieldName())
				// Only decode nested meta value into struct if no Setter defined
				if meta.GetSetter() == nil || reflect.Indirect(field).Type() == utils.ModelType(res.NewStruct()) {
					if _, ok := field.Addr().Interface().(sql.Scanner); !ok {
						decodeMetaValuesToField(res, field, metaValue, processor.Context)
					}
				}
			}
		}
	}

	return
}

// Start start processor
func (processor *processor) Start() error {
	var errs qor.Errors
	processor.Initialize()
	if errs.AddError(processor.Validate()); !errs.HasError() {
		errs.AddError(processor.Commit())
	}
	if errs.HasError() {
		return errs
	}
	return nil
}

// Commit commit data into result
func (processor *processor) Commit() error {
	var errs qor.Errors
	errs.AddError(processor.decode()...)
	if processor.checkSkipLeft(errs.GetErrors()...) {
		return nil
	}

	for _, p := range processor.Resource.GetResource().Processors {
		if err := p.Handler(processor.Result, processor.MetaValues, processor.Context); err != nil {
			if processor.checkSkipLeft(err) {
				break
			}
			errs.AddError(err)
		}
	}
	return errs
}
<|endoftext|>


<commit_before>package sched

import (
	"bytes"
	"log"
	"strings"

	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/tsaf/conf"
	"github.com/jordan-wright/email"
)

func (s *Schedule) Email(name string, group opentsdb.TagSet) {
	a := s.Conf.Alerts[name]
	if a == nil {
		log.Println("sched: unknown alert name during email:", name)
	}
	if a.Owner == "" {
		return
	}
	body := new(bytes.Buffer)
	subject := new(bytes.Buffer)
	data := struct {
		Alert *conf.Alert
		Tags  opentsdb.TagSet
	}{
		a,
		group,
	}
	if a.Template.Body != nil {
		err := a.Template.Body.Execute(body, &data)
		if err != nil {
			log.Println(err)
			return
		}
	}
	if a.Template.Subject != nil {
		err := a.Template.Subject.Execute(subject, &data)
		if err != nil {
			log.Println(err)
			return
		}
	}
	e := email.NewEmail()
	e.From = "tsaf@stackexchange.com"
	e.To = strings.Split(a.Owner, ",")
	e.Subject = subject.String()
	e.Text = body.String()
	err := e.Send(s.SmtpHost, nil)
	if err != nil {
		log.Println(err)
		return
	}
	ak := AlertKey{a.Name, group.String()}
	state := s.Status[ak]
	state.Emailed = true
}
<commit_msg>API changed, update<commit_after>package sched

import (
	"bytes"
	"log"
	"strings"

	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/tsaf/conf"
	"github.com/jordan-wright/email"
)

func (s *Schedule) Email(name string, group opentsdb.TagSet) {
	a := s.Conf.Alerts[name]
	if a == nil {
		log.Println("sched: unknown alert name during email:", name)
	}
	if a.Owner == "" {
		return
	}
	body := new(bytes.Buffer)
	subject := new(bytes.Buffer)
	data := struct {
		Alert *conf.Alert
		Tags  opentsdb.TagSet
	}{
		a,
		group,
	}
	if a.Template.Body != nil {
		err := a.Template.Body.Execute(body, &data)
		if err != nil {
			log.Println(err)
			return
		}
	}
	if a.Template.Subject != nil {
		err := a.Template.Subject.Execute(subject, &data)
		if err != nil {
			log.Println(err)
			return
		}
	}
	e := email.NewEmail()
	e.From = "tsaf@stackexchange.com"
	e.To = strings.Split(a.Owner, ",")
	e.Subject = subject.String()
	e.Text = body.Bytes()
	err := e.Send(s.SmtpHost, nil)
	if err != nil {
		log.Println(err)
		return
	}
	ak := AlertKey{a.Name, group.String()}
	state := s.Status[ak]
	state.Emailed = true
}
<|endoftext|>


<commit_before>package main

import (
	"debug/pe"
	"errors"
	"fmt"
	"github.com/bnagy/gapstone"
	"github.com/codegangsta/cli"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"log"
	"os"
)

var PAGE_SIZE uint64 = 0x1000

var inputFlag = cli.StringFlag{
	Name:  "input_file",
	Usage: "file to decode",
}

var outputFlag = cli.StringFlag{
	Name:  "output_file",
	Usage: "where to write decoded data",
}

var verboseFlag = cli.BoolFlag{
	Name:  "verbose",
	Usage: "print debugging output",
}

var RequiredFlagNotProvidedError = errors.New("Required flag not provided.")

func checkRequiredArgs(c *cli.Context, requiredFlags []cli.StringFlag) error {
	for _, flag := range requiredFlags {
		if c.GlobalString(flag.Name) != "" {
			continue
		}
		if c.String(flag.Name) != "" {
			continue
		}
		log.Printf(fmt.Sprintf("Error: '%s' value required", flag.Name))
		return RequiredFlagNotProvidedError
	}
	return nil
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func dumpDisassemble32(data []byte, address uint64) error {
	engine, e := gapstone.New(
		gapstone.CS_ARCH_X86,
		gapstone.CS_MODE_32,
	)
	check(e)
	defer engine.Close()

	insns, e := engine.Disasm([]byte(data), address, 0 /* all instructions */)
	check(e)

	log.Printf("Disasm:\n")
	for _, insn := range insns {
		log.Printf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
	}

	return nil
}

func roundUp(i uint64, base uint64) uint64 {
	if i%base == 0x0 {
		return i
	} else {
		return i + base - (i % base)
	}
}

func roundUpToPage(i uint64) uint64 {
	return roundUp(i, PAGE_SIZE)
}

func Load(f *pe.File, u uc.Unicorn) error {
	var imageBase uint64
	var addressOfEntryPoint uint64

	if optionalHeader, ok := f.OptionalHeader.(*pe.OptionalHeader32); ok {
		fmt.Printf("section alignment: 0x%x\n", optionalHeader.SectionAlignment)
		fmt.Printf("file alignment: 0x%x\n", optionalHeader.FileAlignment)
		imageBase = uint64(optionalHeader.ImageBase)
		addressOfEntryPoint = uint64(optionalHeader.AddressOfEntryPoint)
	}
	if optionalHeader, ok := f.OptionalHeader.(*pe.OptionalHeader64); ok {
		fmt.Printf("section alignment: 0x%x\n", optionalHeader.SectionAlignment)
		fmt.Printf("file alignment: 0x%x\n", optionalHeader.FileAlignment)
		imageBase = optionalHeader.ImageBase
		addressOfEntryPoint = uint64(optionalHeader.AddressOfEntryPoint)
	}

	for _, section := range f.Sections {
		h := section.SectionHeader

		fmt.Printf("section: %s\n", section.SectionHeader.Name)
		fmt.Printf("  virtual address: 0x%x\n", section.SectionHeader.VirtualAddress)
		fmt.Printf("  virtual size: 0x%x\n", section.SectionHeader.VirtualSize)
		fmt.Printf("  file offset: 0x%x\n", section.SectionHeader.Offset)
		fmt.Printf("  file size: 0x%x\n", section.SectionHeader.Size)

		secStart := imageBase + uint64(h.VirtualAddress)
		secLength := roundUpToPage(uint64(h.VirtualSize))
		e := u.MemMap(secStart, secLength)
		check(e)

		d, e := section.Data()
		check(e)

		u.MemWrite(secStart, d)
	}

	// since we always map at ImageBase, we don't need to apply (32bit) relocs
	// TODO: check 64bit reloc types

	fmt.Printf("entry point: 0x%x\n", addressOfEntryPoint)
	fmt.Printf("entry point va : 0x%x\n", imageBase+addressOfEntryPoint)
	d, e := u.MemRead(imageBase+addressOfEntryPoint, 0x20)
	check(e)
	dumpDisassemble32(d, imageBase+addressOfEntryPoint)

	return nil
}

func doit(path string) error {
	f, e := pe.Open(path)
	check(e)

	// TODO: switch based on file type
	u, e := uc.NewUnicorn(uc.ARCH_X86, uc.MODE_32)

	return Load(f, u)
}

func main() {
	app := cli.NewApp()
	app.Version = "0.1"
	app.Name = "crystal-tiger-pe-loader"
	app.Usage = "Load PE files."
	app.Commands = []cli.Command{
		{
			Name:    "load",
			Aliases: []string{"l"},
			Usage:   "load a pe file",
			Flags:   []cli.Flag{inputFlag, verboseFlag},
			Action: func(c *cli.Context) {
				if checkRequiredArgs(c, []cli.StringFlag{inputFlag}) != nil {
					return
				}
				check(doit(c.String("input_file")))
			},
		},
	}
	app.Run(os.Args)
}
<commit_msg>pe: refactor loader<commit_after>package main

import (
	"bufio"
	"bytes"
	"debug/pe"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/bnagy/gapstone"
	"github.com/codegangsta/cli"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"io"
	"log"
	"os"
	"unicode/utf16"
)

var PAGE_SIZE uint64 = 0x1000

var inputFlag = cli.StringFlag{
	Name:  "input_file",
	Usage: "file to decode",
}

var outputFlag = cli.StringFlag{
	Name:  "output_file",
	Usage: "where to write decoded data",
}

var verboseFlag = cli.BoolFlag{
	Name:  "verbose",
	Usage: "print debugging output",
}

var RequiredFlagNotProvidedError = errors.New("Required flag not provided.")

func checkRequiredArgs(c *cli.Context, requiredFlags []cli.StringFlag) error {
	for _, flag := range requiredFlags {
		if c.GlobalString(flag.Name) != "" {
			continue
		}
		if c.String(flag.Name) != "" {
			continue
		}
		log.Printf(fmt.Sprintf("Error: '%s' value required", flag.Name))
		return RequiredFlagNotProvidedError
	}
	return nil
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func roundUp(i uint64, base uint64) uint64 {
	if i%base == 0x0 {
		return i
	} else {
		return i + base - (i % base)
	}
}

func roundUpToPage(i uint64) uint64 {
	return roundUp(i, PAGE_SIZE)
}

type Arch string
type Mode string

var ARCH_X86 Arch = "x86"
var MODE_32 Mode = "32"

var InvalidArchError = errors.New("Invalid ARCH provided.")
var InvalidModeError = errors.New("Invalid MODE provided.")

type LoadedModule struct {
	Name        string
	BaseAddress uint64 // VA (*not* RVA)
	EntryPoint  uint64 // VA (*not* RVA)
}

func (m LoadedModule) VA(rva uint64) uint64 {
	return m.BaseAddress + rva
}

func (m LoadedModule) MemRead(env *Environment, rva uint64, length uint64) ([]byte, error) {
	return env.u.MemRead(m.VA(rva), length)
}

func (m LoadedModule) MemWrite(env *Environment, rva uint64, data []byte) error {
	return env.u.MemWrite(m.VA(rva), data)
}

type MemoryRegion struct {
	Address uint64 // VA
	Length  uint64
}

type Environment struct {
	u             uc.Unicorn
	Arch          Arch
	Mode          Mode
	loadedModules []LoadedModule
	memoryRegions []MemoryRegion
}

func NewEnvironment(arch Arch, mode Mode) (*Environment, error) {

	if arch != ARCH_X86 {
		return nil, InvalidArchError
	}
	if mode != MODE_32 {
		return nil, InvalidModeError
	}

	u, e := uc.NewUnicorn(uc.ARCH_X86, uc.MODE_32)
	if e != nil {
		return nil, e
	}

	return &Environment{
		u:             u,
		Arch:          arch,
		Mode:          mode,
		loadedModules: make([]LoadedModule, 1),
		memoryRegions: make([]MemoryRegion, 5),
	}, nil
}

type ImageImportDirectory struct {
	rvaImportLookupTable  uint32
	TimeDateStamp         uint32
	ForwarderChain        uint32
	rvaModuleName         uint32
	rvaImportAddressTable uint32
}

func (env *Environment) LoadPE(name string, f *pe.File) (*LoadedModule, error) {
	var imageBase uint64
	var addressOfEntryPoint uint64
	var dataDirectory [16]pe.DataDirectory

	if optionalHeader, ok := f.OptionalHeader.(*pe.OptionalHeader32); ok {
		imageBase = uint64(optionalHeader.ImageBase)
		addressOfEntryPoint = uint64(optionalHeader.AddressOfEntryPoint)
		dataDirectory = optionalHeader.DataDirectory
	} else {
		return nil, InvalidModeError
	}

	mod := LoadedModule{
		Name:        name,
		BaseAddress: imageBase,
		EntryPoint:  imageBase + addressOfEntryPoint,
	}

	for _, section := range f.Sections {
		h := section.SectionHeader

		fmt.Printf("section: %s\n", section.SectionHeader.Name)
		fmt.Printf("  virtual address: 0x%x\n", section.SectionHeader.VirtualAddress)
		fmt.Printf("  virtual size: 0x%x\n", section.SectionHeader.VirtualSize)
		fmt.Printf("  file offset: 0x%x\n", section.SectionHeader.Offset)
		fmt.Printf("  file size: 0x%x\n", section.SectionHeader.Size)

		secStart := mod.VA(uint64(h.VirtualAddress))
		secLength := roundUpToPage(uint64(h.VirtualSize))
		e := env.u.MemMap(secStart, secLength)
		check(e)

		d, e := section.Data()
		check(e)

		e = mod.MemWrite(env, uint64(h.VirtualAddress), d)
		check(e)

		region := MemoryRegion{
			Address: secStart,
			Length:  secLength,
		}
		env.memoryRegions = append(env.memoryRegions, region)
	}

	// since we always map at ImageBase, we don't need to apply (32bit) relocs
	// TODO: check 64bit reloc types

	importDirectory := dataDirectory[1]
	importRva := importDirectory.VirtualAddress
	importSize := importDirectory.Size
	fmt.Printf("import rva: 0x%x\n", importRva)
	fmt.Printf("import size: 0x%x\n", importSize)

	// TODO: check ranges
	d, e := mod.MemRead(env, uint64(importDirectory.VirtualAddress), uint64(importDirectory.Size))
	check(e)

	p := bytes.NewBuffer(d)
	for {
		var dir ImageImportDirectory
		binary.Read(p, binary.LittleEndian, &dir.rvaImportLookupTable)
		fmt.Printf("rva import lookup table: 0x%x\n", dir.rvaImportLookupTable)
		if dir.rvaImportLookupTable == 0 {
			break
		}
		binary.Read(p, binary.LittleEndian, &dir.TimeDateStamp)
		fmt.Printf("time date stamp: 0x%x\n", dir.TimeDateStamp)

		binary.Read(p, binary.LittleEndian, &dir.ForwarderChain)
		fmt.Printf("forwarder chain: 0x%x\n", dir.ForwarderChain)

		binary.Read(p, binary.LittleEndian, &dir.rvaModuleName)
		fmt.Printf("rva module name: 0x%x\n", dir.rvaModuleName)

		moduleNameBuf, e := mod.MemRead(env, uint64(dir.rvaModuleName), 0x100)
		check(e)
		moduleName, e := readAscii(moduleNameBuf)
		check(e)

		fmt.Printf("module name: %s\n", string(moduleName))

		binary.Read(p, binary.LittleEndian, &dir.rvaImportAddressTable)
	}

	env.loadedModules = append(env.loadedModules, mod)
	return &mod, nil
}

func readAscii(buf []byte) (string, error) {
	br := bufio.NewReader(bytes.NewReader(buf))
	bytez, e := br.ReadBytes(byte(0x00))
	check(e)
	return string(bytez), nil
}

func readUtf16le(buf []byte) ([]rune, error) {
	start := 0
	end := 0

	for i := 0; i < len(buf)/2; i++ {
		if !(buf[i*2] == 0 && buf[i*2+1] == 0) {
			end = i * 2
			break
		}
	}

	numChars := (end - start) / 2
	d := make([]uint16, numChars)
	for i := 0; i < numChars; i++ {
		d[i] = uint16(buf[i*2]) | (uint16(buf[i*2+1]) << 8)
	}
	return utf16.Decode(d), nil
}

var GAPSTONE_ARCH_MAP = map[Arch]int{
	ARCH_X86: gapstone.CS_ARCH_X86,
}

var GAPSTONE_MODE_MAP = map[Mode]uint{
	MODE_32: gapstone.CS_MODE_32,
}

func (env *Environment) disassembleBytes(data []byte, address uint64, w io.Writer) error {
	// TODO: cache the engine on the Environment?

	engine, e := gapstone.New(
		GAPSTONE_ARCH_MAP[env.Arch],
		GAPSTONE_MODE_MAP[env.Mode],
	)
	check(e)
	defer engine.Close()

	insns, e := engine.Disasm([]byte(data), address, 0 /* all instructions */)
	check(e)

	w.Write([]byte(fmt.Sprintf("Disasm:\n")))
	for _, insn := range insns {
		w.Write([]byte(fmt.Sprintf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)))
	}

	return nil
}

func (env *Environment) Disassemble(address uint64, length uint64, w io.Writer) error {
	d, e := env.u.MemRead(address, length)
	check(e)
	return env.disassembleBytes(d, address, w)
}

func doit(path string) error {
	f, e := pe.Open(path)
	check(e)

	env, e := NewEnvironment(ARCH_X86, MODE_32)
	check(e)

	m, e := env.LoadPE(path, f)
	check(e)

	e = env.Disassemble(m.EntryPoint, 0x20, os.Stdout)
	check(e)

	return nil
}

func main() {
	app := cli.NewApp()
	app.Version = "0.1"
	app.Name = "crystal-tiger-pe-loader"
	app.Usage = "Load PE files."
	app.Commands = []cli.Command{
		{
			Name:    "load",
			Aliases: []string{"l"},
			Usage:   "load a pe file",
			Flags:   []cli.Flag{inputFlag, verboseFlag},
			Action: func(c *cli.Context) {
				if checkRequiredArgs(c, []cli.StringFlag{inputFlag}) != nil {
					return
				}
				check(doit(c.String("input_file")))
			},
		},
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Package terminal provides a tty emulation and session handlings that is
// supported via Screen
package terminal

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"koding/tools/pty"
	"os/exec"
	"os/user"
	"syscall"
	"time"
	"unicode/utf8"

	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

const randomStringLength = 24 // 144 bit base64 encoded

// Server is the type of object that is sent to the connected client.
// Represents a running shell process on the server.
type Server struct {
	Session          string `json:"session"`
	remote           Remote
	pty              *pty.PTY
	currentSecond    int64
	messageCounter   int
	byteCounter      int
	lineFeeedCounter int
}

type Remote struct {
	Output       dnode.Function
	SessionEnded dnode.Function
}

func KillSession(r *kite.Request) (interface{}, error) {
	var params struct {
		Session string
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ session: [string] }")
	}

	if params.Session == "" {
		return nil, errors.New("session is empty")
	}

	if err := killSession(params.Session); err != nil {
		return nil, err
	}

	return true, nil
}

func GetSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	sessions := screenSessions(user.Username)
	if len(sessions) == 0 {
		return nil, errors.New("no sessions available")
	}

	return sessions, nil
}

func Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, fmt.Errorf("{ remote: [object], session: %s, noScreen: [bool] }, err: %s",
			params.Session, err)
	}

	if params.SizeX <= 0 || params.SizeY <= 0 {
		return nil, fmt.Errorf("{ sizeX: %d, sizeY: %d } { raw JSON : %v }", params.SizeX, params.SizeY, r.Args.One())
	}

	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	command, err := newCommand(params.Mode, params.Session, user.Username)
	if err != nil {
		return nil, err
	}

	// We will return this object to the client.
	server := &Server{
		Session: command.Session,
		remote:  params.Remote,
		pty:     pty.New("/dev/pts"),
	}
	server.setSize(float64(params.SizeX), float64(params.SizeY))

	// wrap the command with sudo -i for initiation login shell. This is needed
	// in order to have Environments and other to be initialized correctly.
	args := []string{"-i", command.Name}
	args = append(args, command.Args...)
	cmd := exec.Command("/usr/bin/sudo", args...)

	// For test use this, sude is not going to work
	// cmd := exec.Command(command.Name, command.Args...)

	cmd.Env = []string{"TERM=xterm-256color", "HOME=" + user.HomeDir}
	cmd.Stdin = server.pty.Slave
	cmd.Stdout = server.pty.Slave
	cmd.Dir = user.HomeDir
	// cmd.Stderr = server.pty.Slave

	// Open in background, this is needed otherwise the process will be killed
	// if you hit close on the client side.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	err = cmd.Start()
	if err != nil {
		fmt.Println("could not start", err)
	}

	// Wait until the shell process is closed and notify the client.
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println("cmd.wait err", err)
		}

		server.pty.Slave.Close()
		server.pty.Master.Close()
		server.remote.SessionEnded.Call()
	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.pty.Master.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.pty.Master.Read(buf[n : n+1])
				n++
			}

			// Rate limiting...
			s := time.Now().Unix()
			if server.currentSecond != s {
				server.currentSecond = s
				server.messageCounter = 0
				server.byteCounter = 0
				server.lineFeeedCounter = 0
			}
			server.messageCounter += 1
			server.byteCounter += n
			server.lineFeeedCounter += bytes.Count(buf[:n], []byte{'\n'})
			if server.messageCounter > 100 || server.byteCounter > 1<<18 || server.lineFeeedCounter > 300 {
				time.Sleep(time.Second)
			}

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
	}()

	return server, nil
}

// Input is called when some text is written to the terminal.
func (s *Server) Input(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()

	// There is no need to protect the Write() with a mutex because
	// Kite Library guarantees that only one message is processed at a time.
	s.pty.Master.Write([]byte(data))
}

// ControlSequence is called when a non-printable key is pressed on the terminal.
func (s *Server) ControlSequence(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()
	s.pty.MasterEncoded.Write([]byte(data))
}

func (s *Server) SetSize(d *dnode.Partial) {
	args := d.MustSliceOfLength(2)
	x := args[0].MustFloat64()
	y := args[1].MustFloat64()
	s.setSize(x, y)
}

func (s *Server) setSize(x, y float64) {
	s.pty.SetSize(uint16(x), uint16(y))
}

func (s *Server) Close(d *dnode.Partial) {
	s.pty.Signal(syscall.SIGHUP)
}

func (s *Server) Terminate(d *dnode.Partial) {
	s.Close(nil)
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}

func randomString() string {
	r := make([]byte, randomStringLength*6/8)
	rand.Read(r)
	return base64.URLEncoding.EncodeToString(r)
}
<commit_msg>terminal: add throttling mode<commit_after>// Package terminal provides a tty emulation and session handlings that is
// supported via Screen
package terminal

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"koding/tools/pty"
	"os/exec"
	"os/user"
	"syscall"
	"time"
	"unicode/utf8"

	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

const randomStringLength = 24 // 144 bit base64 encoded

// Server is the type of object that is sent to the connected client.
// Represents a running shell process on the server.
type Server struct {
	Session          string `json:"session"`
	remote           Remote
	pty              *pty.PTY
	currentSecond    int64
	messageCounter   int
	byteCounter      int
	lineFeeedCounter int
	throttling       bool
}

type Remote struct {
	Output       dnode.Function
	SessionEnded dnode.Function
}

func KillSession(r *kite.Request) (interface{}, error) {
	var params struct {
		Session string
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ session: [string] }")
	}

	if params.Session == "" {
		return nil, errors.New("session is empty")
	}

	if err := killSession(params.Session); err != nil {
		return nil, err
	}

	return true, nil
}

func GetSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	sessions := screenSessions(user.Username)
	if len(sessions) == 0 {
		return nil, errors.New("no sessions available")
	}

	return sessions, nil
}

func Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, fmt.Errorf("{ remote: [object], session: %s, noScreen: [bool] }, err: %s",
			params.Session, err)
	}

	if params.SizeX <= 0 || params.SizeY <= 0 {
		return nil, fmt.Errorf("{ sizeX: %d, sizeY: %d } { raw JSON : %v }", params.SizeX, params.SizeY, r.Args.One())
	}

	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	command, err := newCommand(params.Mode, params.Session, user.Username)
	if err != nil {
		return nil, err
	}

	// We will return this object to the client.
	server := &Server{
		Session: command.Session,
		remote:  params.Remote,
		pty:     pty.New("/dev/pts"),
	}
	server.setSize(float64(params.SizeX), float64(params.SizeY))

	// wrap the command with sudo -i for initiation login shell. This is needed
	// in order to have Environments and other to be initialized correctly.
	args := []string{"-i", command.Name}
	args = append(args, command.Args...)
	cmd := exec.Command("/usr/bin/sudo", args...)

	// For test use this, sudo is not going to work
	// cmd := exec.Command(command.Name, command.Args...)

	cmd.Env = []string{"TERM=xterm-256color", "HOME=" + user.HomeDir}
	cmd.Stdin = server.pty.Slave
	cmd.Stdout = server.pty.Slave
	cmd.Dir = user.HomeDir
	// cmd.Stderr = server.pty.Slave

	// Open in background, this is needed otherwise the process will be killed
	// if you hit close on the client side.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	err = cmd.Start()
	if err != nil {
		fmt.Println("could not start", err)
	}

	// Wait until the shell process is closed and notify the client.
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println("cmd.wait err", err)
		}

		server.pty.Slave.Close()
		server.pty.Master.Close()
		server.remote.SessionEnded.Call()
	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.pty.Master.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.pty.Master.Read(buf[n : n+1])
				n++
			}

			// Rate limiting...
			if server.throttling {
				s := time.Now().Unix()
				if server.currentSecond != s {
					server.currentSecond = s
					server.messageCounter = 0
					server.byteCounter = 0
					server.lineFeeedCounter = 0
				}
				server.messageCounter += 1
				server.byteCounter += n
				server.lineFeeedCounter += bytes.Count(buf[:n], []byte{'\n'})
				if server.messageCounter > 100 || server.byteCounter > 1<<18 || server.lineFeeedCounter > 300 {
					time.Sleep(time.Second)
				}
			}

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
	}()

	return server, nil
}

// Input is called when some text is written to the terminal.
func (s *Server) Input(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()

	// There is no need to protect the Write() with a mutex because
	// Kite Library guarantees that only one message is processed at a time.
	s.pty.Master.Write([]byte(data))
}

// ControlSequence is called when a non-printable key is pressed on the terminal.
func (s *Server) ControlSequence(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()
	s.pty.MasterEncoded.Write([]byte(data))
}

func (s *Server) SetSize(d *dnode.Partial) {
	args := d.MustSliceOfLength(2)
	x := args[0].MustFloat64()
	y := args[1].MustFloat64()
	s.setSize(x, y)
}

func (s *Server) setSize(x, y float64) {
	s.pty.SetSize(uint16(x), uint16(y))
}

func (s *Server) Close(d *dnode.Partial) {
	s.pty.Signal(syscall.SIGHUP)
}

func (s *Server) Terminate(d *dnode.Partial) {
	s.Close(nil)
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}

func randomString() string {
	r := make([]byte, randomStringLength*6/8)
	rand.Read(r)
	return base64.URLEncoding.EncodeToString(r)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Walk 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 walk

// BindingValueProvider is the interface that a model must implement to support
// data binding with widgets like ComboBox.
type BindingValueProvider interface {
	BindingValue(index int) interface{}
}

// ListModel is the interface that a model must implement to support widgets
// like ComboBox.
type ListModel interface {
	// ItemCount returns the number of items in the model.
	ItemCount() int

	// Value returns the value that should be displayed for the given index.
	Value(index int) interface{}

	// ItemsReset returns the event that the model should publish when the
	// number of its items changes.
	ItemsReset() *Event

	// ItemChanged returns the event that the model should publish when an item
	// was changed.
	ItemChanged() *IntEvent
}

// ListModelBase implements the ItemsReset and ItemChanged methods of the
// ListModel interface.
type ListModelBase struct {
	itemsResetPublisher  EventPublisher
	itemChangedPublisher IntEventPublisher
}

func (lmb *ListModelBase) ItemsReset() *Event {
	return lmb.itemsResetPublisher.Event()
}

func (lmb *ListModelBase) ItemChanged() *IntEvent {
	return lmb.itemChangedPublisher.Event()
}

func (lmb *ListModelBase) PublishItemsReset() {
	lmb.itemsResetPublisher.Publish()
}

func (lmb *ListModelBase) PublishItemChanged(index int) {
	lmb.itemChangedPublisher.Publish(index)
}

// TableModel is the interface that a model must implement to support widgets
// like TableView.
type TableModel interface {
	// RowCount returns the number of rows in the model.
	RowCount() int

	// Value returns the value that should be displayed for the given cell.
	Value(row, col int) interface{}

	// RowsReset returns the event that the model should publish when the number
	// of its rows changes.
	RowsReset() *Event

	// RowChanged returns the event that the model should publish when a row was
	// changed.
	RowChanged() *IntEvent
}

// TableModelBase implements the RowsReset and RowChanged methods of the
// TableModel interface.
type TableModelBase struct {
	rowsResetPublisher  EventPublisher
	rowChangedPublisher IntEventPublisher
}

func (tmb *TableModelBase) RowsReset() *Event {
	return tmb.rowsResetPublisher.Event()
}

func (tmb *TableModelBase) RowChanged() *IntEvent {
	return tmb.rowChangedPublisher.Event()
}

func (tmb *TableModelBase) PublishRowsReset() {
	tmb.rowsResetPublisher.Publish()
}

func (tmb *TableModelBase) PublishRowChanged(row int) {
	tmb.rowChangedPublisher.Publish(row)
}

// ImageProvider is the interface that a model must implement to support
// displaying an item image.
type ImageProvider interface {
	// Image returns the image to display for the item at index index.
	//
	// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
	// interpreted as a file path and the icon associated with the file will be
	// used. It is not supported to use strings together with the other options
	// in the same model instance.
	Image(index int) interface{}
}

// ItemChecker is the interface that a model must implement to support check
// boxes in a widget like TableView.
type ItemChecker interface {
	// Checked returns if the specified item is checked.
	Checked(index int) bool

	// SetChecked sets if the specified item is checked.
	SetChecked(index int, checked bool) error
}

// SortOrder specifies the order by which items are sorted.
type SortOrder int

const (
	// SortAscending specifies ascending sort order.
	SortAscending SortOrder = iota

	// SortDescending specifies descending sort order.
	SortDescending
)

// Sorter is the interface that a model must implement to support sorting with a
// widget like TableView.
type Sorter interface {
	// ColumnSortable returns whether column col is sortable.
	ColumnSortable(col int) bool

	// Sort sorts column col in order order.
	//
	// If col is -1 then no column is to be sorted. Sort must publish the event
	// returned from SortChanged() after sorting.
	Sort(col int, order SortOrder) error

	// SortChanged returns an event that is published after sorting.
	SortChanged() *Event

	// SortedColumn returns the index of the currently sorted column, or -1 if
	// no column is currently sorted.
	SortedColumn() int

	// SortOrder returns the current sort order.
	SortOrder() SortOrder
}

// SorterBase implements the Sorter interface.
//
// You still need to provide your own implementation of at least the Sort method
// to actually sort and reset the model. Your Sort method should call the
// SorterBase implementation so the SortChanged event, that e.g. a TableView
// widget depends on, is published.
type SorterBase struct {
	changedPublisher EventPublisher
	col              int
	order            SortOrder
}

func (sb *SorterBase) ColumnSortable(col int) bool {
	return true
}

func (sb *SorterBase) Sort(col int, order SortOrder) error {
	sb.col, sb.order = col, order

	sb.changedPublisher.Publish()

	return nil
}

func (sb *SorterBase) SortChanged() *Event {
	return sb.changedPublisher.Event()
}

func (sb *SorterBase) SortedColumn() int {
	return sb.col
}

func (sb *SorterBase) SortOrder() SortOrder {
	return sb.order
}

// TreeItem represents an item in a TreeView widget.
type TreeItem interface {
	// Text returns the text of the item.
	Text() string

	// Parent returns the parent of the item.
	Parent() TreeItem

	// ChildCount returns the number of children of the item.
	ChildCount() int

	// ChildAt returns the child at the specified index.
	ChildAt(index int) TreeItem
}

// Imager provides access to an image of objects like tree items.
type Imager interface {
	// Image returns the image to display for an item.
	//
	// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
	// interpreted as a file path and the icon associated with the file will be
	// used. It is not supported to use strings together with the other options
	// in the same model instance.
	Image() interface{}
}

// TreeModel provides widgets like TreeView with item data.
type TreeModel interface {
	// LazyPopulation returns if the model prefers on-demand population.
	//
	// This is useful for models that potentially contain huge amounts of items,
	// e.g. a model that represents a file system.
	LazyPopulation() bool

	// RootCount returns the number of root items.
	RootCount() int

	// RootAt returns the root item at the specified index.
	RootAt(index int) TreeItem

	// ItemsReset returns the event that the model should publish when the
	// descendants of the specified item, or all items if no item is specified,
	// are reset.
	ItemsReset() *TreeItemEvent

	// ItemChanged returns the event that the model should publish when an item
	// was changed.
	ItemChanged() *TreeItemEvent
}

// TreeModelBase partially implements the TreeModel interface.
//
// You still need to provide your own implementation of at least the
// RootCount and RootAt methods. If your model needs lazy population,
// you will also have to implement LazyPopulation.
type TreeModelBase struct {
	itemsResetPublisher  TreeItemEventPublisher
	itemChangedPublisher TreeItemEventPublisher
}

func (tmb *TreeModelBase) LazyPopulation() bool {
	return false
}

func (tmb *TreeModelBase) ItemsReset() *TreeItemEvent {
	return tmb.itemsResetPublisher.Event()
}

func (tmb *TreeModelBase) ItemChanged() *TreeItemEvent {
	return tmb.itemChangedPublisher.Event()
}

func (tmb *TreeModelBase) PublishItemsReset(parent TreeItem) {
	tmb.itemsResetPublisher.Publish(parent)
}

func (tmb *TreeModelBase) PublishItemChanged(item TreeItem) {
	tmb.itemChangedPublisher.Publish(item)
}
<commit_msg>Add interfaces for reflect based models<commit_after>// Copyright 2012 The Walk 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 walk

// BindingValueProvider is the interface that a model must implement to support
// data binding with widgets like ComboBox.
type BindingValueProvider interface {
	BindingValue(index int) interface{}
}

// ListModel is the interface that a model must implement to support widgets
// like ComboBox.
type ListModel interface {
	// ItemCount returns the number of items in the model.
	ItemCount() int

	// Value returns the value that should be displayed for the given index.
	Value(index int) interface{}

	// ItemsReset returns the event that the model should publish when the
	// number of its items changes.
	ItemsReset() *Event

	// ItemChanged returns the event that the model should publish when an item
	// was changed.
	ItemChanged() *IntEvent
}

// ListModelBase implements the ItemsReset and ItemChanged methods of the
// ListModel interface.
type ListModelBase struct {
	itemsResetPublisher  EventPublisher
	itemChangedPublisher IntEventPublisher
}

func (lmb *ListModelBase) ItemsReset() *Event {
	return lmb.itemsResetPublisher.Event()
}

func (lmb *ListModelBase) ItemChanged() *IntEvent {
	return lmb.itemChangedPublisher.Event()
}

func (lmb *ListModelBase) PublishItemsReset() {
	lmb.itemsResetPublisher.Publish()
}

func (lmb *ListModelBase) PublishItemChanged(index int) {
	lmb.itemChangedPublisher.Publish(index)
}

// ReflectListModel provides an alternative to the ListModel interface. It
// uses reflection to obtain data.
type ReflectListModel interface {
	// Items returns the model data, which must be a slice of pointer to struct.
	Items() interface{}

	// ItemsReset returns the event that the model should publish when the
	// number of its items changes.
	ItemsReset() *Event

	// ItemChanged returns the event that the model should publish when an item
	// was changed.
	ItemChanged() *IntEvent

	setValueFunc(value func(index int) interface{})
}

// ReflectListModelBase implements the ItemsReset and ItemChanged methods of
// the ReflectListModel interface.
type ReflectListModelBase struct {
	ListModelBase
	value func(index int) interface{}
}

func (rlmb *ReflectListModelBase) setValueFunc(value func(index int) interface{}) {
	rlmb.value = value
}

func (rlmb *ReflectListModelBase) Value(index int) interface{} {
	return rlmb.value(index)
}

// TableModel is the interface that a model must implement to support widgets
// like TableView.
type TableModel interface {
	// RowCount returns the number of rows in the model.
	RowCount() int

	// Value returns the value that should be displayed for the given cell.
	Value(row, col int) interface{}

	// RowsReset returns the event that the model should publish when the number
	// of its rows changes.
	RowsReset() *Event

	// RowChanged returns the event that the model should publish when a row was
	// changed.
	RowChanged() *IntEvent
}

// TableModelBase implements the RowsReset and RowChanged methods of the
// TableModel interface.
type TableModelBase struct {
	rowsResetPublisher  EventPublisher
	rowChangedPublisher IntEventPublisher
}

func (tmb *TableModelBase) RowsReset() *Event {
	return tmb.rowsResetPublisher.Event()
}

func (tmb *TableModelBase) RowChanged() *IntEvent {
	return tmb.rowChangedPublisher.Event()
}

func (tmb *TableModelBase) PublishRowsReset() {
	tmb.rowsResetPublisher.Publish()
}

func (tmb *TableModelBase) PublishRowChanged(row int) {
	tmb.rowChangedPublisher.Publish(row)
}

// ReflectTableModel provides an alternative to the TableModel interface. It
// uses reflection to obtain data.
type ReflectTableModel interface {
	// Items returns the model data, which must be a slice of pointer to struct.
	Items() interface{}

	// RowsReset returns the event that the model should publish when the
	// number of its items changes.
	RowsReset() *Event

	// RowChanged returns the event that the model should publish when an item
	// was changed.
	RowChanged() *IntEvent

	setValueFunc(value func(row, col int) interface{})
}

// ReflectTableModelBase implements the ItemsReset and ItemChanged methods of
// the ReflectTableModel interface.
type ReflectTableModelBase struct {
	TableModelBase
	value func(row, col int) interface{}
}

func (rtmb *ReflectTableModelBase) setValueFunc(value func(row, col int) interface{}) {
	rtmb.value = value
}

func (rtmb *ReflectTableModelBase) Value(row, col int) interface{} {
	return rtmb.value(row, col)
}

type interceptedSorter interface {
	sorterBase() *SorterBase
	setSortFunc(sort func(col int, order SortOrder) error)
}

// SortedReflectTableModelBase implements the RowsReset and RowChanged methods
// of the ReflectTableModel interface as well as the Sorter interface for
// pre-implemented in-memory sorting.
type SortedReflectTableModelBase struct {
	ReflectTableModelBase
	SorterBase
	sort func(col int, order SortOrder) error
}

func (srtmb *SortedReflectTableModelBase) setSortFunc(sort func(col int, order SortOrder) error) {
	srtmb.sort = sort
}

func (srtmb *SortedReflectTableModelBase) sorterBase() *SorterBase {
	return &srtmb.SorterBase
}

func (srtmb *SortedReflectTableModelBase) Sort(col int, order SortOrder) error {
	if srtmb.sort != nil {
		return srtmb.sort(col, order)
	}

	return srtmb.SorterBase.Sort(col, order)
}

// Populator is an interface that can be implemented by Reflect*Models and slice
// types to populate themselves on demand.
//
// Widgets like TableView, ListBox and ComboBox support lazy population of a
// Reflect*Model or slice, if it implements this interface.
type Populator interface {
	// Populate initializes the slot specified by index.
	//
	// For best performance it is probably a good idea to populate more than a
	// single slot of the slice at once.
	Populate(index int) error
}

// ImageProvider is the interface that a model must implement to support
// displaying an item image.
type ImageProvider interface {
	// Image returns the image to display for the item at index index.
	//
	// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
	// interpreted as a file path and the icon associated with the file will be
	// used. It is not supported to use strings together with the other options
	// in the same model instance.
	Image(index int) interface{}
}

// ItemChecker is the interface that a model must implement to support check
// boxes in a widget like TableView.
type ItemChecker interface {
	// Checked returns if the specified item is checked.
	Checked(index int) bool

	// SetChecked sets if the specified item is checked.
	SetChecked(index int, checked bool) error
}

// SortOrder specifies the order by which items are sorted.
type SortOrder int

const (
	// SortAscending specifies ascending sort order.
	SortAscending SortOrder = iota

	// SortDescending specifies descending sort order.
	SortDescending
)

// Sorter is the interface that a model must implement to support sorting with a
// widget like TableView.
type Sorter interface {
	// ColumnSortable returns whether column col is sortable.
	ColumnSortable(col int) bool

	// Sort sorts column col in order order.
	//
	// If col is -1 then no column is to be sorted. Sort must publish the event
	// returned from SortChanged() after sorting.
	Sort(col int, order SortOrder) error

	// SortChanged returns an event that is published after sorting.
	SortChanged() *Event

	// SortedColumn returns the index of the currently sorted column, or -1 if
	// no column is currently sorted.
	SortedColumn() int

	// SortOrder returns the current sort order.
	SortOrder() SortOrder
}

// SorterBase implements the Sorter interface.
//
// You still need to provide your own implementation of at least the Sort method
// to actually sort and reset the model. Your Sort method should call the
// SorterBase implementation so the SortChanged event, that e.g. a TableView
// widget depends on, is published.
type SorterBase struct {
	changedPublisher EventPublisher
	col              int
	order            SortOrder
}

func (sb *SorterBase) ColumnSortable(col int) bool {
	return true
}

func (sb *SorterBase) Sort(col int, order SortOrder) error {
	sb.col, sb.order = col, order

	sb.changedPublisher.Publish()

	return nil
}

func (sb *SorterBase) SortChanged() *Event {
	return sb.changedPublisher.Event()
}

func (sb *SorterBase) SortedColumn() int {
	return sb.col
}

func (sb *SorterBase) SortOrder() SortOrder {
	return sb.order
}

// Imager provides access to an image of objects like tree items.
type Imager interface {
	// Image returns the image to display for an item.
	//
	// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
	// interpreted as a file path and the icon associated with the file will be
	// used. It is not supported to use strings together with the other options
	// in the same model instance.
	Image() interface{}
}

// TreeItem represents an item in a TreeView widget.
type TreeItem interface {
	// Text returns the text of the item.
	Text() string

	// Parent returns the parent of the item.
	Parent() TreeItem

	// ChildCount returns the number of children of the item.
	ChildCount() int

	// ChildAt returns the child at the specified index.
	ChildAt(index int) TreeItem
}

// TreeModel provides widgets like TreeView with item data.
type TreeModel interface {
	// LazyPopulation returns if the model prefers on-demand population.
	//
	// This is useful for models that potentially contain huge amounts of items,
	// e.g. a model that represents a file system.
	LazyPopulation() bool

	// RootCount returns the number of root items.
	RootCount() int

	// RootAt returns the root item at the specified index.
	RootAt(index int) TreeItem

	// ItemsReset returns the event that the model should publish when the
	// descendants of the specified item, or all items if no item is specified,
	// are reset.
	ItemsReset() *TreeItemEvent

	// ItemChanged returns the event that the model should publish when an item
	// was changed.
	ItemChanged() *TreeItemEvent
}

// TreeModelBase partially implements the TreeModel interface.
//
// You still need to provide your own implementation of at least the
// RootCount and RootAt methods. If your model needs lazy population,
// you will also have to implement LazyPopulation.
type TreeModelBase struct {
	itemsResetPublisher  TreeItemEventPublisher
	itemChangedPublisher TreeItemEventPublisher
}

func (tmb *TreeModelBase) LazyPopulation() bool {
	return false
}

func (tmb *TreeModelBase) ItemsReset() *TreeItemEvent {
	return tmb.itemsResetPublisher.Event()
}

func (tmb *TreeModelBase) ItemChanged() *TreeItemEvent {
	return tmb.itemChangedPublisher.Event()
}

func (tmb *TreeModelBase) PublishItemsReset(parent TreeItem) {
	tmb.itemsResetPublisher.Publish(parent)
}

func (tmb *TreeModelBase) PublishItemChanged(item TreeItem) {
	tmb.itemChangedPublisher.Publish(item)
}
<|endoftext|>