<commit_before>package gparselib

import (
	"fmt"
	"math"
)

// SubparserOp is a simple filter to the outside and gets the same data as the
// parent parser.
type SubparserOp func(pd *ParseData, ctx interface{}) (*ParseData, interface{})

// ParseMulti uses a subparser multiple times.
// The minimum times the subparser has to match and the maximum times the
// subparser can match have to be configured.
func ParseMulti(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
	cfgMin int,
	cfgMax int,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos
	relPos := 0
	subresults := make([]*ParseResult, 0, min(cfgMax, 128))

	for i := 0; i < cfgMax && pd.Result == nil; i++ {
		pd, ctx = pluginSubparser(pd, ctx)
		if pd.Result.ErrPos < 0 {
			subresults = append(subresults, pd.Result)
			pd.Result = nil
		}
	}

	relPos = pd.Source.pos - orgPos
	pd.Source.pos = orgPos
	if len(subresults) >= cfgMin {
		pd.Result = nil
		createMatchedResult(pd, relPos)
		saveAllValuesFeedback(pd, subresults)
	} else {
		subresult := pd.Result
		pd.Result = nil
		createUnmatchedResult(pd, relPos, fmt.Sprintf("At least %d matches expected but got only %d.", cfgMin, len(subresults)), nil)
		pd.Result.Feedback = append(pd.Result.Feedback, subresult.Feedback...)
	}
	pd.SubResults = subresults
	return handleSemantics(pluginSemantics, pd, ctx)
}

// ParseMulti0 uses its subparser at least one time without upper bound.
// But the result is still positive even if the subparser didn't match a single
// time.
func ParseMulti0(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	return ParseMulti(pd, ctx, pluginSubparser, pluginSemantics, 0, math.MaxInt32)
}

// ParseMulti1 uses its subparser at least one time without upper bound.
// The result is positive as long as the subparser matches at least one time.
func ParseMulti1(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	return ParseMulti(pd, ctx, pluginSubparser, pluginSemantics, 1, math.MaxInt32)
}

// ParseOptional uses its subparser exaclty one time.
// But the result is still positive even if the subparser didn't match.
func ParseOptional(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos

	pd, ctx = pluginSubparser(pd, ctx)

	// if error: reset to ignore
	if pd.Result.ErrPos >= 0 {
		pd.Result.ErrPos = -1
		pd.Result.Feedback = nil
		pd.Source.pos = orgPos
	}

	return pd, ctx
}

// ParseAll calls multiple subparsers and all have to match for a successful result.
func ParseAll(
	pd *ParseData,
	ctx interface{},
	pluginSubparsers []SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos
	subresults := make([]*ParseResult, len(pluginSubparsers))

	for i, subparser := range pluginSubparsers {
		pd, ctx = subparser(pd, ctx)
		if pd.Result.ErrPos >= 0 {
			pd.Source.pos = orgPos
			pd.Result.Pos = orgPos // make result 'our result'
			return pd, ctx
		}
		subresults[i] = pd.Result
		pd.Result = nil
	}

	relPos := pd.Source.pos - orgPos
	pd.Source.pos = orgPos
	createMatchedResult(pd, relPos)
	saveAllValuesFeedback(pd, subresults)
	pd.SubResults = subresults
	return handleSemantics(pluginSemantics, pd, ctx)
}

// ParseAny calls multiple subparsers until one matches.
// The result is only unsuccessful if no subparser matches.
func ParseAny(
	pd *ParseData,
	ctx interface{},
	pluginSubparsers []SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos
	allFeedback := make([]*FeedbackItem, 0, len(pluginSubparsers))
	lastPos := 0

	for _, subparser := range pluginSubparsers {
		pd, ctx = subparser(pd, ctx)
		if pd.Result.ErrPos < 0 {
			relPos := pd.Source.pos - orgPos
			pd.Source.pos = orgPos
			createMatchedResult(pd, relPos)
			return handleSemantics(pluginSemantics, pd, ctx)
		}
		lastPos = pd.Result.Pos
		pd.Source.pos = orgPos
		allFeedback = append(allFeedback, pd.Result.Feedback...)
		pd.Result = nil
	}

	relPos := lastPos - orgPos
	pd.Source.pos = orgPos
	pd.Result = nil
	createUnmatchedResult(pd, relPos, fmt.Sprintf("Any subparser should match. But all %d subparsers failed", len(pluginSubparsers)), nil)
	pd.Result.Feedback = append(pd.Result.Feedback, allFeedback...)
	return pd, ctx
}

func saveAllValuesFeedback(pd *ParseData, tmpSubresults []*ParseResult) {
	s := make([]interface{}, len(tmpSubresults))
	for i, subres := range tmpSubresults {
		s[i] = subres.Value
		pd.Result.Feedback = append(pd.Result.Feedback, subres.Feedback...)
	}
	pd.Result.Value = s
}
<commit_msg>Fix result value bug with simplification<commit_after>package gparselib

import (
	"fmt"
	"math"
)

// SubparserOp is a simple filter to the outside and gets the same data as the
// parent parser.
type SubparserOp func(pd *ParseData, ctx interface{}) (*ParseData, interface{})

// ParseMulti uses a subparser multiple times.
// The minimum times the subparser has to match and the maximum times the
// subparser can match have to be configured.
func ParseMulti(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
	cfgMin int,
	cfgMax int,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos
	relPos := 0
	subresults := make([]*ParseResult, 0, min(cfgMax, 128))

	for i := 0; i < cfgMax && pd.Result == nil; i++ {
		pd, ctx = pluginSubparser(pd, ctx)
		if pd.Result.ErrPos < 0 {
			subresults = append(subresults, pd.Result)
			pd.Result = nil
		}
	}

	relPos = pd.Source.pos - orgPos
	pd.Source.pos = orgPos
	if len(subresults) >= cfgMin {
		pd.Result = nil
		createMatchedResult(pd, relPos)
		saveAllValuesFeedback(pd, subresults)
	} else {
		subresult := pd.Result
		pd.Result = nil
		createUnmatchedResult(pd, relPos, fmt.Sprintf("At least %d matches expected but got only %d.", cfgMin, len(subresults)), nil)
		pd.Result.Feedback = append(pd.Result.Feedback, subresult.Feedback...)
	}
	pd.SubResults = subresults
	return handleSemantics(pluginSemantics, pd, ctx)
}

// ParseMulti0 uses its subparser at least one time without upper bound.
// But the result is still positive even if the subparser didn't match a single
// time.
func ParseMulti0(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	return ParseMulti(pd, ctx, pluginSubparser, pluginSemantics, 0, math.MaxInt32)
}

// ParseMulti1 uses its subparser at least one time without upper bound.
// The result is positive as long as the subparser matches at least one time.
func ParseMulti1(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	return ParseMulti(pd, ctx, pluginSubparser, pluginSemantics, 1, math.MaxInt32)
}

// ParseOptional uses its subparser exaclty one time.
// But the result is still positive even if the subparser didn't match.
func ParseOptional(
	pd *ParseData,
	ctx interface{},
	pluginSubparser SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos

	pd, ctx = pluginSubparser(pd, ctx)

	// if error: reset to ignore
	if pd.Result.ErrPos >= 0 {
		pd.Result.ErrPos = -1
		pd.Result.Feedback = nil
		pd.Source.pos = orgPos
	}

	return pd, ctx
}

// ParseAll calls multiple subparsers and all have to match for a successful result.
func ParseAll(
	pd *ParseData,
	ctx interface{},
	pluginSubparsers []SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos
	subresults := make([]*ParseResult, len(pluginSubparsers))

	for i, subparser := range pluginSubparsers {
		pd, ctx = subparser(pd, ctx)
		if pd.Result.ErrPos >= 0 {
			pd.Source.pos = orgPos
			pd.Result.Pos = orgPos // make result 'our result'
			return pd, ctx
		}
		subresults[i] = pd.Result
		pd.Result = nil
	}

	relPos := pd.Source.pos - orgPos
	pd.Source.pos = orgPos
	createMatchedResult(pd, relPos)
	saveAllValuesFeedback(pd, subresults)
	pd.SubResults = subresults
	return handleSemantics(pluginSemantics, pd, ctx)
}

// ParseAny calls multiple subparsers until one matches.
// The result is only unsuccessful if no subparser matches.
func ParseAny(
	pd *ParseData,
	ctx interface{},
	pluginSubparsers []SubparserOp,
	pluginSemantics SemanticsOp,
) (*ParseData, interface{}) {
	orgPos := pd.Source.pos
	allFeedback := make([]*FeedbackItem, 0, len(pluginSubparsers))
	lastPos := 0

	for _, subparser := range pluginSubparsers {
		pd, ctx = subparser(pd, ctx)
		if pd.Result.ErrPos < 0 {
			return handleSemantics(pluginSemantics, pd, ctx)
		}
		lastPos = pd.Result.Pos
		pd.Source.pos = orgPos
		allFeedback = append(allFeedback, pd.Result.Feedback...)
		pd.Result = nil
	}

	relPos := lastPos - orgPos
	pd.Source.pos = orgPos
	pd.Result = nil
	createUnmatchedResult(pd, relPos, fmt.Sprintf("Any subparser should match. But all %d subparsers failed", len(pluginSubparsers)), nil)
	pd.Result.Feedback = append(pd.Result.Feedback, allFeedback...)
	return pd, ctx
}

func saveAllValuesFeedback(pd *ParseData, tmpSubresults []*ParseResult) {
	s := make([]interface{}, len(tmpSubresults))
	for i, subres := range tmpSubresults {
		s[i] = subres.Value
		pd.Result.Feedback = append(pd.Result.Feedback, subres.Feedback...)
	}
	pd.Result.Value = s
}
<|endoftext|>


<commit_before>package framework

import (
	"fmt"
	"strconv"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/taskgraph/taskgraph/pkg/etcdutil"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

var (
	ErrEpochMismatch = fmt.Errorf("server epoch mismatch")
)

func (f *framework) CheckGRPCContext(ctx context.Context) error {
	md, ok := metadata.FromContext(ctx)
	if !ok {
		return fmt.Errorf("Can't get grpc.Metadata from context: %v", ctx)
	}
	epoch, err := strconv.ParseUint(md["epoch"], 10, 64)
	if err != nil {
		return err
	}
	// send it to framework central select and check epoch.
	resChan := make(chan bool, 1)
	f.epochCheckChan <- &epochCheck{
		epoch:   epoch,
		resChan: resChan,
	}
	ok = <-resChan
	if ok {
		return nil
	} else {
		return ErrEpochMismatch
	}
}

func (f *framework) DataRequest(ctx context.Context, toID uint64, method string, input proto.Message) {
	epoch, ok := ctx.Value(epochKey).(uint64)
	if !ok {
		f.log.Fatalf("Can not find epochKey or cast is in DataRequest")
	}

	// assumption here:
	// Event driven task will call this in a synchronous way so that
	// the epoch won't change at the time task sending this request.
	// Epoch may change, however, before the request is actually being sent.
	f.dataReqtoSendChan <- &dataRequest{
		ctx:    f.makeGRPCContext(ctx),
		taskID: toID,
		epoch:  epoch,
		input:  input,
		method: method,
	}
}

// encode metadata to context in grpc specific way
func (f *framework) makeGRPCContext(ctx context.Context) context.Context {
	md := metadata.MD{
		"taskID": strconv.FormatUint(f.taskID, 10),
		"epoch":  strconv.FormatUint(f.epoch, 10),
	}
	return metadata.NewContext(ctx, md)
}

func (f *framework) sendRequest(dr *dataRequest) {
	addr, err := etcdutil.GetAddress(f.etcdClient, f.name, dr.taskID)
	if err != nil {
		f.log.Printf("getAddress(%d) failed: %v", dr.taskID, err)
		go f.retrySendRequest(dr)
		return
	}
	// TODO: save ClientConn creation steps.
	cc, err := grpc.Dial(addr, grpc.WithTimeout(heartbeatInterval))
	// we need to retry if some task failed and there is a temporary Get request failure.
	if err != nil {
		f.log.Printf("grpc.Dial from task %d (addr: %s) failed: %v", dr.taskID, addr, err)
		// Should retry for other errors.
		go f.retrySendRequest(dr)
		return
	}
	defer cc.Close()
	if dr.retry {
		f.log.Printf("retry data request %s to task %d, addr %s", dr.method, dr.taskID, addr)
	} else {
		f.log.Printf("data request %s to task %d, addr %s", dr.method, dr.taskID, addr)
	}
	reply := f.task.CreateOutputMessage(dr.method)
	err = grpc.Invoke(dr.ctx, dr.method, dr.input, reply, cc)
	if err != nil {
		f.log.Printf("grpc.Invoke from task %d (addr: %s), method: %s, failed: %v", dr.taskID, addr, dr.method, err)
		go f.retrySendRequest(dr)
		return
	}

	f.dataRespChan <- &dataResponse{
		epoch:  dr.epoch,
		taskID: dr.taskID,
		method: dr.method,
		input:  dr.input,
		output: reply,
	}
}

func (f *framework) retrySendRequest(dr *dataRequest) {
	// we try again after the previous task key expires and hopefully another task
	// gets up and running.
	time.Sleep(2 * heartbeatInterval)
	dr.retry = true
	f.dataReqtoSendChan <- dr
}

// Framework http server for data request.
// Each request will be in the format: "/datareq?taskID=XXX&req=XXX".
// "taskID" indicates the requesting task. "req" is the meta data for this request.
// On success, it should respond with requested data in http body.
func (f *framework) startHTTP() {
	f.log.Printf("serving grpc on %s\n", f.ln.Addr())
	server := f.task.CreateServer()
	err := server.Serve(f.ln)
	select {
	case <-f.httpStop:
		server.Stop()
		f.log.Printf("grpc stops serving")
	default:
		if err != nil {
			f.log.Fatalf("grpc.Serve returns error: %v\n", err)
		}
	}
}

// Close listener, stop HTTP server;
// Write error message back to under-serving responses.
func (f *framework) stopHTTP() {
	close(f.httpStop)
	f.ln.Close()
}

func (f *framework) handleDataResp(ctx context.Context, resp *dataResponse) {
	f.task.DataReady(ctx, resp.taskID, resp.method, resp.output)
}
<commit_msg>grpc.WithTimeout comments<commit_after>package framework

import (
	"fmt"
	"strconv"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/taskgraph/taskgraph/pkg/etcdutil"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

var (
	ErrEpochMismatch = fmt.Errorf("server epoch mismatch")
)

func (f *framework) CheckGRPCContext(ctx context.Context) error {
	md, ok := metadata.FromContext(ctx)
	if !ok {
		return fmt.Errorf("Can't get grpc.Metadata from context: %v", ctx)
	}
	epoch, err := strconv.ParseUint(md["epoch"], 10, 64)
	if err != nil {
		return err
	}
	// send it to framework central select and check epoch.
	resChan := make(chan bool, 1)
	f.epochCheckChan <- &epochCheck{
		epoch:   epoch,
		resChan: resChan,
	}
	ok = <-resChan
	if ok {
		return nil
	} else {
		return ErrEpochMismatch
	}
}

func (f *framework) DataRequest(ctx context.Context, toID uint64, method string, input proto.Message) {
	epoch, ok := ctx.Value(epochKey).(uint64)
	if !ok {
		f.log.Fatalf("Can not find epochKey or cast is in DataRequest")
	}

	// assumption here:
	// Event driven task will call this in a synchronous way so that
	// the epoch won't change at the time task sending this request.
	// Epoch may change, however, before the request is actually being sent.
	f.dataReqtoSendChan <- &dataRequest{
		ctx:    f.makeGRPCContext(ctx),
		taskID: toID,
		epoch:  epoch,
		input:  input,
		method: method,
	}
}

// encode metadata to context in grpc specific way
func (f *framework) makeGRPCContext(ctx context.Context) context.Context {
	md := metadata.MD{
		"taskID": strconv.FormatUint(f.taskID, 10),
		"epoch":  strconv.FormatUint(f.epoch, 10),
	}
	return metadata.NewContext(ctx, md)
}

func (f *framework) sendRequest(dr *dataRequest) {
	addr, err := etcdutil.GetAddress(f.etcdClient, f.name, dr.taskID)
	if err != nil {
		f.log.Printf("getAddress(%d) failed: %v", dr.taskID, err)
		go f.retrySendRequest(dr)
		return
	}
	// TODO: save ClientConn creation steps.
	// The grpc.WithTimeout would help detect any disconnection in failfast.
	// Otherwise grpc.Invoke will keep retrying.
	cc, err := grpc.Dial(addr, grpc.WithTimeout(heartbeatInterval))
	// we need to retry if some task failed and there is a temporary Get request failure.
	if err != nil {
		f.log.Printf("grpc.Dial from task %d (addr: %s) failed: %v", dr.taskID, addr, err)
		// Should retry for other errors.
		go f.retrySendRequest(dr)
		return
	}
	defer cc.Close()
	if dr.retry {
		f.log.Printf("retry data request %s to task %d, addr %s", dr.method, dr.taskID, addr)
	} else {
		f.log.Printf("data request %s to task %d, addr %s", dr.method, dr.taskID, addr)
	}
	reply := f.task.CreateOutputMessage(dr.method)
	err = grpc.Invoke(dr.ctx, dr.method, dr.input, reply, cc)
	if err != nil {
		f.log.Printf("grpc.Invoke from task %d (addr: %s), method: %s, failed: %v", dr.taskID, addr, dr.method, err)
		go f.retrySendRequest(dr)
		return
	}

	f.dataRespChan <- &dataResponse{
		epoch:  dr.epoch,
		taskID: dr.taskID,
		method: dr.method,
		input:  dr.input,
		output: reply,
	}
}

func (f *framework) retrySendRequest(dr *dataRequest) {
	// we try again after the previous task key expires and hopefully another task
	// gets up and running.
	time.Sleep(2 * heartbeatInterval)
	dr.retry = true
	f.dataReqtoSendChan <- dr
}

// Framework http server for data request.
// Each request will be in the format: "/datareq?taskID=XXX&req=XXX".
// "taskID" indicates the requesting task. "req" is the meta data for this request.
// On success, it should respond with requested data in http body.
func (f *framework) startHTTP() {
	f.log.Printf("serving grpc on %s\n", f.ln.Addr())
	server := f.task.CreateServer()
	err := server.Serve(f.ln)
	select {
	case <-f.httpStop:
		server.Stop()
		f.log.Printf("grpc stops serving")
	default:
		if err != nil {
			f.log.Fatalf("grpc.Serve returns error: %v\n", err)
		}
	}
}

// Close listener, stop HTTP server;
// Write error message back to under-serving responses.
func (f *framework) stopHTTP() {
	close(f.httpStop)
	f.ln.Close()
}

func (f *framework) handleDataResp(ctx context.Context, resp *dataResponse) {
	f.task.DataReady(ctx, resp.taskID, resp.method, resp.output)
}
<|endoftext|>


<commit_before>// Package ns provides name server methods for selected name server(s)
package ns

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/data"
	"github.com/miekg/dns"
)

// A Host represents a name server host
type Host struct {
	ip      string
	alpha2  string
	country string
	city    string
}

// A Request represents a name server request
type Request struct {
	country string
	host    string
	hosts   []Host
}

// NewRequest creates a new dns request object
func NewRequest() *Request {
	return &Request{host: ""}
}

// Init configure dns command and fetch name servers
func (d *Request) Init() {
	if !d.cache("validate") {
		d.hosts = fetchNSHosts()
		d.cache("write")
	} else {
		d.cache("read")
	}
}

// SetCountryList init the connect contry items
func (d *Request) SetCountryList(c *cli.Readline) {
	var countries []string
	for _, host := range d.hosts {
		countries = append(countries, host.country)
	}
	countries = uniqStrSlice(countries)
	sort.Strings(countries)
	c.UpdateCompleter("connect", countries)
}

// SetArgs set requested country
func (d *Request) SetArgs(args string) bool {
	d.country = args
	return true
}

// Look up name server
func (d *Request) dnsLookup() {
	//var list []DNSHost

	c := new(dns.Client)
	m := new(dns.Msg)

	m.SetQuestion(dns.Fqdn("yahoo.com"), dns.TypeA)
	m.RecursionDesired = true
	r, _, err := c.Exchange(m, "8.8.8.8:53")
	if err != nil {
		println(err.Error())
	}
	for _, a := range r.Answer {
		fmt.Printf("%#v\n", a)
	}
}

// cache provides caching for name servers
func (d *Request) cache(r string) bool {
	switch r {
	case "read":
		b, err := ioutil.ReadFile("/tmp/mylg.ns")
		if err != nil {
			panic(err.Error())
		}
		d.hosts = d.hosts[:0]
		r := bytes.NewBuffer(b)
		s := bufio.NewScanner(r)
		for s.Scan() {
			csv := strings.Split(s.Text(), ";")
			if len(csv) != 4 {
				continue
			}
			d.hosts = append(d.hosts, Host{alpha2: csv[0], country: csv[1], city: csv[2], ip: csv[3]})
		}
	case "write":
		var data []string
		for _, h := range d.hosts {
			data = append(data, fmt.Sprintf("%s;%s;%s;%s", h.alpha2, h.country, h.city, h.ip))
		}
		err := ioutil.WriteFile("/tmp/mylg.ns", []byte(strings.Join(data, "\n")), 0644)
		if err != nil {
			panic(err.Error())
		}
		println("write done!")
	case "validate":
		f, err := os.Stat("/tmp/mylg.ns")
		if err != nil {
			return false
		}
		d := time.Since(f.ModTime())
		if d.Hours() > 48 {
			return false
		}
	}
	return true
}

// Fetch name servers from public-dns.info
func fetchNSHosts() []Host {
	var (
		hosts   []Host
		counter = make(map[string]int)
	)
	resp, err := http.Get("http://public-dns.info/nameservers.csv")
	if err != nil {
		println(err.Error())
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		csv := strings.Split(scanner.Text(), ",")
		if csv[3] != "\"\"" {
			if name, ok := data.Country[csv[2]]; ok && counter[csv[2]] < 5 {
				hosts = append(hosts, Host{ip: csv[0], alpha2: csv[2], country: name, city: csv[3]})
				counter[csv[2]]++
			}
		}
	}
	return hosts
}

// uniqStrSlice return unique slice
func uniqStrSlice(src []string) []string {
	var rst []string
	tmp := make(map[string]struct{})
	for _, s := range src {
		tmp[s] = struct{}{}
	}
	for s := range tmp {
		rst = append(rst, s)
	}
	return rst
}
<commit_msg>added set node list<commit_after>// Package ns provides name server methods for selected name server(s)
package ns

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/data"
	"github.com/miekg/dns"
)

// A Host represents a name server host
type Host struct {
	ip      string
	alpha2  string
	country string
	city    string
}

// A Request represents a name server request
type Request struct {
	country string
	host    string
	hosts   []Host
}

// NewRequest creates a new dns request object
func NewRequest() *Request {
	return &Request{host: ""}
}

// Init configure dns command and fetch name servers
func (d *Request) Init() {
	if !d.cache("validate") {
		d.hosts = fetchNSHosts()
		d.cache("write")
	} else {
		d.cache("read")
	}
}

// SetCountryList init the connect contry items
func (d *Request) SetCountryList(c *cli.Readline) {
	var countries []string
	for _, host := range d.hosts {
		countries = append(countries, host.country)
	}
	countries = uniqStrSlice(countries)
	sort.Strings(countries)
	c.UpdateCompleter("connect", countries)
}

// SetNodeList init the node city items
func (d *Request) SetNodeList(c *cli.Readline) {
	var node []string
	for _, host := range d.hosts {
		if host.country == d.country {
			node = append(node, host.city)
		}
	}
	sort.Strings(node)
	c.UpdateCompleter("node", node)
}

// ValidateCountry set requested country
func (d *Request) ValidateCountry(args string) bool {
	d.country = args
	return true
}

// Look up name server
func (d *Request) dnsLookup() {
	//var list []DNSHost

	c := new(dns.Client)
	m := new(dns.Msg)

	m.SetQuestion(dns.Fqdn("yahoo.com"), dns.TypeA)
	m.RecursionDesired = true
	r, _, err := c.Exchange(m, "8.8.8.8:53")
	if err != nil {
		println(err.Error())
	}
	for _, a := range r.Answer {
		fmt.Printf("%#v\n", a)
	}
}

// cache provides caching for name servers
func (d *Request) cache(r string) bool {
	switch r {
	case "read":
		b, err := ioutil.ReadFile("/tmp/mylg.ns")
		if err != nil {
			panic(err.Error())
		}
		d.hosts = d.hosts[:0]
		r := bytes.NewBuffer(b)
		s := bufio.NewScanner(r)
		for s.Scan() {
			csv := strings.Split(s.Text(), ";")
			if len(csv) != 4 {
				continue
			}
			d.hosts = append(d.hosts, Host{alpha2: csv[0], country: csv[1], city: csv[2], ip: csv[3]})
		}
	case "write":
		var data []string
		for _, h := range d.hosts {
			data = append(data, fmt.Sprintf("%s;%s;%s;%s", h.alpha2, h.country, h.city, h.ip))
		}
		err := ioutil.WriteFile("/tmp/mylg.ns", []byte(strings.Join(data, "\n")), 0644)
		if err != nil {
			panic(err.Error())
		}
		println("write done!")
	case "validate":
		f, err := os.Stat("/tmp/mylg.ns")
		if err != nil {
			return false
		}
		d := time.Since(f.ModTime())
		if d.Hours() > 48 {
			return false
		}
	}
	return true
}

// Fetch name servers from public-dns.info
func fetchNSHosts() []Host {
	var (
		hosts   []Host
		counter = make(map[string]int)
	)
	resp, err := http.Get("http://public-dns.info/nameservers.csv")
	if err != nil {
		println(err.Error())
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		csv := strings.Split(scanner.Text(), ",")
		if csv[3] != "\"\"" {
			if name, ok := data.Country[csv[2]]; ok && counter[csv[2]] < 5 {
				hosts = append(hosts, Host{ip: csv[0], alpha2: csv[2], country: name, city: csv[3]})
				counter[csv[2]]++
			}
		}
	}
	return hosts
}

// uniqStrSlice return unique slice
func uniqStrSlice(src []string) []string {
	var rst []string
	tmp := make(map[string]struct{})
	for _, s := range src {
		tmp[s] = struct{}{}
	}
	for s := range tmp {
		rst = append(rst, s)
	}
	return rst
}
<|endoftext|>


<commit_before>package dns

import (
	"crypto/sha1"
	"hash"
	"io"
	"strings"
)

const (
	_ = iota
	NSEC3_NXDOMAIN
	NSEC3_NODATA
)

type saltWireFmt struct {
	Salt string "size-hex"
}

// HashName hashes a string (label) according to RFC5155. It returns the hashed string.
func HashName(label string, ha uint8, iter uint16, salt string) string {
	saltwire := new(saltWireFmt)
	saltwire.Salt = salt
	wire := make([]byte, DefaultMsgSize)
	n, ok := packStruct(saltwire, wire, 0)
	if !ok {
		return ""
	}
	wire = wire[:n]
	name := make([]byte, 255)
	off, ok1 := PackDomainName(strings.ToLower(label), name, 0, nil, false)
	if !ok1 {
		return ""
	}
	name = name[:off]
	var s hash.Hash
	switch ha {
	case SHA1:
		s = sha1.New()
	default:
		return ""
	}

	// k = 0
	name = append(name, wire...)
	io.WriteString(s, string(name))
	nsec3 := s.Sum(nil)
	// k > 0
	for k := uint16(0); k < iter; k++ {
		s.Reset()
		nsec3 = append(nsec3, wire...)
		io.WriteString(s, string(nsec3))
		nsec3 = s.Sum(nil)
	}
	return unpackBase32(nsec3)
}

// HashNames hashes the ownername and the next owner name in an NSEC3 record according to RFC 5155.
// It uses the paramaters as set in the NSEC3 record. The string zone is appended to the hashed
// ownername.
func (nsec3 *RR_NSEC3) HashNames(zone string) {
	nsec3.Header().Name = strings.ToLower(HashName(nsec3.Header().Name, nsec3.Hash, nsec3.Iterations, nsec3.Salt)) + "." + zone
	nsec3.NextDomain = HashName(nsec3.NextDomain, nsec3.Hash, nsec3.Iterations, nsec3.Salt)
}

// Match checks if domain matches the first (hashed) owner name of the NSEC3 record, domain must be given
// in plain text.
func (nsec3 *RR_NSEC3) Match(domain string) bool {
	return strings.ToUpper(SplitLabels(nsec3.Header().Name)[0]) == strings.ToUpper(HashName(domain, nsec3.Hash, nsec3.Iterations, nsec3.Salt))
}

// Cover checks if domain is covered by the NSEC3 record, domain must be given in plain text.
func (nsec3 *RR_NSEC3) Cover(domain string) bool {
	hashdom := strings.ToUpper(HashName(domain, nsec3.Hash, nsec3.Iterations, nsec3.Salt))
	nextdom := strings.ToUpper(nsec3.NextDomain)
	owner := strings.ToUpper(SplitLabels(nsec3.Header().Name)[0])                                                                              // The hashed part
	apex := strings.ToUpper(HashName(strings.Join(SplitLabels(nsec3.Header().Name)[1:], "."), nsec3.Hash, nsec3.Iterations, nsec3.Salt)) + "." // The name of the zone
	// if nextdomain equals the apex, it is considered The End. So in that case hashdom is always less then nextdomain
	if hashdom > owner && nextdom == apex {
		return true
	}

	if hashdom > owner && hashdom <= nextdom {
		return true
	}

	return false
}

// NsecVerify verifies an denial of existence response with NSECs
// NsecVerify returns nil when the NSECs in the message contain
// the correct proof. This function does not validates the NSECs.
func (m *Msg) NsecVerify(q Question) error {

	return nil
}

// Nsec3Verify verifies an denial of existence response with NSEC3s.
// This function does not validate the NSEC3s.
func (m *Msg) Nsec3Verify(q Question) (int, error) {
	var (
		nsec3    []*RR_NSEC3
		ncdenied = false // next closer denied
		sodenied = false // source of synthesis denied
		ce       = ""    // closest encloser
		nc       = ""    // next closer
		so       = ""    // source of synthesis
	)
	if len(m.Answer) > 0 && len(m.Ns) > 0 {
		// Wildcard expansion
		// Closest encloser inferred from SIG in authority and qname
		// println("EXPANDED WILDCARD PROOF or DNAME CNAME")
		// println("NODATA")
		// I need to check the type bitmap
		// wildcard bit not set?
		// MM: No need to check the wildcard bit here:
		//     This response has only 1 NSEC4 and it does not match
		//     the closest encloser (it covers next closer).
	}
	if len(m.Answer) == 0 && len(m.Ns) > 0 {
		// Maybe an NXDOMAIN or NODATA, we only know when we check
		for _, n := range m.Ns {
			if n.Header().Rrtype == TypeNSEC3 {
				nsec3 = append(nsec3, n.(*RR_NSEC3))
			}
		}
		if len(nsec3) == 0 {
			return 0, ErrDenialNsec3
		}

		lastchopped := ""
		labels := SplitLabels(q.Name)

		// Find the closest encloser and create the next closer
		for _, nsec := range nsec3 {
			candidate := ""
			for i := len(labels) - 1; i >= 0; i-- {
				candidate = labels[i] + "." + candidate
				if nsec.Match(candidate) {
					ce = candidate
				}
				lastchopped = labels[i]
			}
		}
		if ce == "" { // what about root label?
			return 0, ErrDenialCe
		}
		nc = lastchopped + "." + ce
		so = "*." + ce
		// Check if the next closer is covered and thus denied
		for _, nsec := range nsec3 {
			if nsec.Cover(nc) {
				ncdenied = true
				break
			}
		}
		if !ncdenied {
			if m.MsgHdr.Rcode == RcodeNameError {
				// For NXDOMAIN this is a problem
				return 0, ErrDenialNc // add next closer name here
			}
			goto NoData
		}

		// Check if the source of synthesis is covered and thus also denied
		for _, nsec := range nsec3 {
			if nsec.Cover(so) {
				sodenied = true
				break
			}
		}
		if !sodenied {
			return 0, ErrDenialSo
		}
		// The message headers claims something different!
		if m.MsgHdr.Rcode != RcodeNameError {
			return 0, ErrDenialHdr
		}

		return NSEC3_NXDOMAIN, nil
	}
	return 0, nil
NoData:
	// For NODATA we need to to check if the matching nsec3 has to correct type bit map
	// And we need to check that the wildcard does NOT exist
	for _, nsec := range nsec3 {
		if nsec.Cover(so) {
			sodenied = true
			break
		}
	}
	if sodenied {
		// Whoa, the closest encloser is denied, but there does exist
		// a wildcard a that level. That's not good
		return 0, ErrDenialWc
	}

	// The closest encloser MUST be the query name
	for _, nsec := range nsec3 {
		if nsec.Match(nc) {
			// This nsec3 must NOT have the type bitmap set of the qtype. If it does have it, return an error
			for _, t := range nsec.TypeBitMap {
				if t == q.Qtype {
					return 0, ErrDenialBit
				}
			}
		}
	}
	if m.MsgHdr.Rcode == RcodeNameError {
		return 0, ErrDenialHdr
	}
	return NSEC3_NODATA, nil
}
<commit_msg>prototype nsec cover method<commit_after>package dns

import (
	"crypto/sha1"
	"hash"
	"io"
	"strings"
)

const (
	_ = iota
	NSEC3_NXDOMAIN
	NSEC3_NODATA
)

type saltWireFmt struct {
	Salt string "size-hex"
}

// HashName hashes a string (label) according to RFC5155. It returns the hashed string.
func HashName(label string, ha uint8, iter uint16, salt string) string {
	saltwire := new(saltWireFmt)
	saltwire.Salt = salt
	wire := make([]byte, DefaultMsgSize)
	n, ok := packStruct(saltwire, wire, 0)
	if !ok {
		return ""
	}
	wire = wire[:n]
	name := make([]byte, 255)
	off, ok1 := PackDomainName(strings.ToLower(label), name, 0, nil, false)
	if !ok1 {
		return ""
	}
	name = name[:off]
	var s hash.Hash
	switch ha {
	case SHA1:
		s = sha1.New()
	default:
		return ""
	}

	// k = 0
	name = append(name, wire...)
	io.WriteString(s, string(name))
	nsec3 := s.Sum(nil)
	// k > 0
	for k := uint16(0); k < iter; k++ {
		s.Reset()
		nsec3 = append(nsec3, wire...)
		io.WriteString(s, string(nsec3))
		nsec3 = s.Sum(nil)
	}
	return unpackBase32(nsec3)
}

// HashNames hashes the ownername and the next owner name in an NSEC3 record according to RFC 5155.
// It uses the paramaters as set in the NSEC3 record. The string zone is appended to the hashed
// ownername.
func (nsec3 *RR_NSEC3) HashNames(zone string) {
	nsec3.Header().Name = strings.ToLower(HashName(nsec3.Header().Name, nsec3.Hash, nsec3.Iterations, nsec3.Salt)) + "." + zone
	nsec3.NextDomain = HashName(nsec3.NextDomain, nsec3.Hash, nsec3.Iterations, nsec3.Salt)
}

// Match checks if domain matches the first (hashed) owner name of the NSEC3 record. Domain must be given
// in plain text.
func (nsec3 *RR_NSEC3) Match(domain string) bool {
	return strings.ToUpper(SplitLabels(nsec3.Header().Name)[0]) == strings.ToUpper(HashName(domain, nsec3.Hash, nsec3.Iterations, nsec3.Salt))
}

// Cover checks if domain is covered by the NSEC3 record. Domain must be given in plain text.
func (nsec3 *RR_NSEC3) Cover(domain string) bool {
	hashdom := strings.ToUpper(HashName(domain, nsec3.Hash, nsec3.Iterations, nsec3.Salt))
	nextdom := strings.ToUpper(nsec3.NextDomain)
	owner := strings.ToUpper(SplitLabels(nsec3.Header().Name)[0])                                                                              // The hashed part
	apex := strings.ToUpper(HashName(strings.Join(SplitLabels(nsec3.Header().Name)[1:], "."), nsec3.Hash, nsec3.Iterations, nsec3.Salt)) + "." // The name of the zone
	// if nextdomain equals the apex, it is considered The End. So in that case hashdom is always less then nextdomain
	if hashdom > owner && nextdom == apex {
		return true
	}

	if hashdom > owner && hashdom <= nextdom {
		return true
	}

	return false
}

// Cover checks if domain is covered by the NSEC record. Domain must be given in plain text.
func (nsec *RR_NSEC) Cover(domain string) bool {
	return false
}

// NsecVerify verifies an denial of existence response with NSECs
// NsecVerify returns nil when the NSECs in the message contain
// the correct proof. This function does not validates the NSECs.
func (m *Msg) NsecVerify(q Question) error {

	return nil
}

// Nsec3Verify verifies an denial of existence response with NSEC3s.
// This function does not validate the NSEC3s.
func (m *Msg) Nsec3Verify(q Question) (int, error) {
	var (
		nsec3    []*RR_NSEC3
		ncdenied = false // next closer denied
		sodenied = false // source of synthesis denied
		ce       = ""    // closest encloser
		nc       = ""    // next closer
		so       = ""    // source of synthesis
	)
	if len(m.Answer) > 0 && len(m.Ns) > 0 {
		// Wildcard expansion
		// Closest encloser inferred from SIG in authority and qname
		// println("EXPANDED WILDCARD PROOF or DNAME CNAME")
		// println("NODATA")
		// I need to check the type bitmap
		// wildcard bit not set?
		// MM: No need to check the wildcard bit here:
		//     This response has only 1 NSEC4 and it does not match
		//     the closest encloser (it covers next closer).
	}
	if len(m.Answer) == 0 && len(m.Ns) > 0 {
		// Maybe an NXDOMAIN or NODATA, we only know when we check
		for _, n := range m.Ns {
			if n.Header().Rrtype == TypeNSEC3 {
				nsec3 = append(nsec3, n.(*RR_NSEC3))
			}
		}
		if len(nsec3) == 0 {
			return 0, ErrDenialNsec3
		}

		lastchopped := ""
		labels := SplitLabels(q.Name)

		// Find the closest encloser and create the next closer
		for _, nsec := range nsec3 {
			candidate := ""
			for i := len(labels) - 1; i >= 0; i-- {
				candidate = labels[i] + "." + candidate
				if nsec.Match(candidate) {
					ce = candidate
				}
				lastchopped = labels[i]
			}
		}
		if ce == "" { // what about root label?
			return 0, ErrDenialCe
		}
		nc = lastchopped + "." + ce
		so = "*." + ce
		// Check if the next closer is covered and thus denied
		for _, nsec := range nsec3 {
			if nsec.Cover(nc) {
				ncdenied = true
				break
			}
		}
		if !ncdenied {
			if m.MsgHdr.Rcode == RcodeNameError {
				// For NXDOMAIN this is a problem
				return 0, ErrDenialNc // add next closer name here
			}
			goto NoData
		}

		// Check if the source of synthesis is covered and thus also denied
		for _, nsec := range nsec3 {
			if nsec.Cover(so) {
				sodenied = true
				break
			}
		}
		if !sodenied {
			return 0, ErrDenialSo
		}
		// The message headers claims something different!
		if m.MsgHdr.Rcode != RcodeNameError {
			return 0, ErrDenialHdr
		}

		return NSEC3_NXDOMAIN, nil
	}
	return 0, nil
NoData:
	// For NODATA we need to to check if the matching nsec3 has to correct type bit map
	// And we need to check that the wildcard does NOT exist
	for _, nsec := range nsec3 {
		if nsec.Cover(so) {
			sodenied = true
			break
		}
	}
	if sodenied {
		// Whoa, the closest encloser is denied, but there does exist
		// a wildcard a that level. That's not good
		return 0, ErrDenialWc
	}

	// The closest encloser MUST be the query name
	for _, nsec := range nsec3 {
		if nsec.Match(nc) {
			// This nsec3 must NOT have the type bitmap set of the qtype. If it does have it, return an error
			for _, t := range nsec.TypeBitMap {
				if t == q.Qtype {
					return 0, ErrDenialBit
				}
			}
		}
	}
	if m.MsgHdr.Rcode == RcodeNameError {
		return 0, ErrDenialHdr
	}
	return NSEC3_NODATA, nil
}
<|endoftext|>


<commit_before>package multiverse

import (
	"fmt"
	"runtime"
)

func (r Rarity) Ok(c *Card) (bool, error) {
	for _, printing := range c.Printings {
		if printing.Rarity == r {
			return true, nil
		}
	}
	return false, nil
}

func (m ManaColor) Ok(c *Card) (bool, error) {
	if m == ManaColors.Colorless {
		return c.Colors == 0, nil
	}
	return c.Colors&m != 0, nil
}

type Filter interface {
	Ok(*Card) (bool, error)
}

func (m Multiverse) Search(f Filter) ([]*Card, error) {
	c := m.Cards
	cores := runtime.GOMAXPROCS(-1)
	sectionLen := c.Len() / cores

	cardChan := make(chan *Card, cores*16)
	doneChan := make(chan bool)
	errChan := make(chan error)
	list := make([]*Card, 0, 1)

	for i := 0; i < cores; i++ {
		start := sectionLen * i
		end := start + sectionLen

		if i == cores-1 {
			end = c.Len()
		}

		go func(start, end int) {
			for j := range c[start:end] {
				ok, err := f.Ok(&c[j+start])
				if err != nil {
					errChan <- err
					return
				}
				if ok {
					cardChan <- &c[j+start]
				}
			}
			doneChan <- true
		}(start, end)
	}

	for cores > 0 {
		select {
		case <-doneChan:
			cores--
		case c := <-cardChan:
			appendNonDuplicateToCardList(&list, c)
		case err := <-errChan:
			return nil, err
		}
	}

	for len(cardChan) > 0 {
		c := <-cardChan
		appendNonDuplicateToCardList(&list, c)

	}

	shortList := make([]*Card, len(list))

	for i, card := range list {
		shortList[i] = card
	}

	return shortList, nil
}

func appendToCardList(list *[]*Card, c *Card) {
	for _, card := range *list {
		if card == c {
			return
		}
	}
	appendNonDuplicateToCardList(list, c)
}

func appendNonDuplicateToCardList(list *[]*Card, c *Card) {
	l := len(*list)
	if l < cap(*list) {
		*list = (*list)[:l+1]
	} else {
		newList := make([]*Card, l+1, l*2)
		copy(newList, *list)
		*list = newList
	}
	(*list)[l] = c
}

type Not struct {
	Filter
}

func (n Not) Ok(c *Card) (bool, error) {
	ok, err := n.Filter.Ok(c)
	return !ok, err
}

type And []Filter

func (a And) Ok(c *Card) (bool, error) {
	for _, f := range a {
		ok, err := f.Ok(c)
		if !ok || err != nil {
			return ok, err
		}
	}
	return true, nil
}

type Or []Filter

func (o Or) Ok(c *Card) (bool, error) {
	for _, f := range o {
		ok, err := f.Ok(c)
		if ok || err != nil {
			return ok, err
		}
	}
	return false, nil
}

type Cond map[string]interface{}

func (c Cond) Ok(card *Card) (bool, error) {
	for key, val := range c {
		switch key {
		case "color", "colors":
			same, err := handleColorSearch(card.Colors, val)
			if err != nil || !same {
				return same, err
			}
		}
	}
	return true, nil
}

func handleColorSearch(cardColor ManaColor, val interface{}) (bool, error) {
	switch val := val.(type) {
	case ManaColor:
		if cardColor&val != 0 {
			return true, nil
		}
		return false, nil
	}

	return false, fmt.Errorf("unexpected color type %T", val)
}
<commit_msg>Made sure we allow the search goroutines to exit.<commit_after>package multiverse

import (
	"fmt"
	"runtime"
)

func (r Rarity) Ok(c *Card) (bool, error) {
	for _, printing := range c.Printings {
		if printing.Rarity == r {
			return true, nil
		}
	}
	return false, nil
}

func (m ManaColor) Ok(c *Card) (bool, error) {
	if m == ManaColors.Colorless {
		return c.Colors == 0, nil
	}
	return c.Colors&m != 0, nil
}

type Filter interface {
	Ok(*Card) (bool, error)
}

func (m Multiverse) Search(f Filter) ([]*Card, error) {
	c := m.Cards
	cores := runtime.GOMAXPROCS(-1)
	sectionLen := c.Len() / cores

	cardChan := make(chan *Card, 16*cores)
	doneChan := make(chan bool)
	errChan := make(chan error)
	list := make([]*Card, 0, 1)

	for i := 0; i < cores; i++ {
		start := sectionLen * i
		end := start + sectionLen

		if i == cores-1 {
			end = c.Len()
		}

		go func(start, end int) {
			for j := range c[start:end] {
				ok, err := f.Ok(&c[j+start])
				if err != nil {
					errChan <- err
					return
				}
				if ok {
					cardChan <- &c[j+start]
				}
			}
			doneChan <- true
		}(start, end)
	}

	finishChans := func() {
		for cores > 0 {
			select {
			case <-cardChan:
			case <-errChan:
				cores--
			case <-doneChan:
				cores--
			}
		}

		close(cardChan)
		close(errChan)
		close(doneChan)
	}

	for cores > 0 {
		select {
		case <-doneChan:
			cores--
		case c := <-cardChan:
			appendNonDuplicateToCardList(&list, c)
		case err := <-errChan:
			cores--
			go finishChans()
			return nil, err
		}
	}

	close(cardChan)
	close(errChan)
	close(doneChan)

	for c := range cardChan {
		appendNonDuplicateToCardList(&list, c)

	}

	shortList := make([]*Card, len(list))

	copy(shortList, list)

	return shortList, nil
}

func appendToCardList(list *[]*Card, c *Card) {
	for _, card := range *list {
		if card == c {
			return
		}
	}
	appendNonDuplicateToCardList(list, c)
}

func appendNonDuplicateToCardList(list *[]*Card, c *Card) {
	l := len(*list)
	if l < cap(*list) {
		*list = (*list)[:l+1]
	} else {
		newList := make([]*Card, l+1, l*2)
		copy(newList, *list)
		*list = newList
	}
	(*list)[l] = c
}

type Not struct {
	Filter
}

func (n Not) Ok(c *Card) (bool, error) {
	ok, err := n.Filter.Ok(c)
	return !ok, err
}

type And []Filter

func (a And) Ok(c *Card) (bool, error) {
	for _, f := range a {
		ok, err := f.Ok(c)
		if !ok || err != nil {
			return ok, err
		}
	}
	return true, nil
}

type Or []Filter

func (o Or) Ok(c *Card) (bool, error) {
	for _, f := range o {
		ok, err := f.Ok(c)
		if ok || err != nil {
			return ok, err
		}
	}
	return false, nil
}

type Cond map[string]interface{}

func (c Cond) Ok(card *Card) (bool, error) {
	for key, val := range c {
		switch key {
		case "color", "colors":
			same, err := handleColorSearch(card.Colors, val)
			if err != nil || !same {
				return same, err
			}
		}
	}
	return true, nil
}

func handleColorSearch(cardColor ManaColor, val interface{}) (bool, error) {
	switch val := val.(type) {
	case ManaColor:
		if cardColor&val != 0 {
			return true, nil
		}
		return false, nil
	}

	return false, fmt.Errorf("unexpected color type %T", val)
}
<|endoftext|>


<commit_before>package mysql

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"

	"h12.me/csv"
)

type Cmd struct {
	Value      interface{}
	TagKey     string
	ExpandPath []string
	DB         string
	Engine     string
	Table      string
}

func (cmd Cmd) CreateDB() string {
	return fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8;\n", cmd.DB)
}

func (cmd Cmd) CreateTable() (string, error) {
	w := new(bytes.Buffer)
	fmt.Fprintf(w, "CREATE TABLE IF NOT EXISTS %s (\n", cmd.Table)
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	var pks []string
	sort.Sort(fields)
	for _, field := range fields {
		if field.Tag.Get("PK") == "yes" {
			pks = append(pks, field.Name)
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	for _, field := range fields {
		if field.Tag.Get("PK") != "yes" {
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	fmt.Fprintf(w, "\tPRIMARY KEY (%s)\n", strings.Join(pks, ","))
	fmt.Fprintf(w, ") ENGINE=%s DEFAULT CHARSET=UTF8;\n", cmd.Engine)
	return w.String(), nil
}

func (cmd Cmd) LoadData() (string, error) {
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("LOAD DATA LOCAL INFILE 'Reader::%%[1]s' REPLACE INTO TABLE %%[1]s (%s);\n", strings.Join(fields.Names(), ", ")), nil
}

func (cmd Cmd) Fields() (csv.Fields, error) {
	enc := csv.NewEncoder(ioutil.Discard).SetTagKey(cmd.TagKey).SetExpandPath(cmd.ExpandPath...)
	if err := enc.Encode(cmd.Value); err != nil {
		return nil, err
	}
	return enc.Fields(), nil
}
<commit_msg>add full table name<commit_after>package mysql

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"

	"h12.me/csv"
)

type Cmd struct {
	Value      interface{}
	TagKey     string
	ExpandPath []string
	DB         string
	Engine     string
	Table      string
}

func (cmd Cmd) CreateDB() string {
	return fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8;\n", cmd.DB)
}

func (cmd Cmd) CreateTable() (string, error) {
	w := new(bytes.Buffer)
	fmt.Fprintf(w, "CREATE TABLE IF NOT EXISTS %s (\n", cmd.Table)
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	var pks []string
	sort.Sort(fields)
	for _, field := range fields {
		if field.Tag.Get("PK") == "yes" {
			pks = append(pks, field.Name)
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	for _, field := range fields {
		if field.Tag.Get("PK") != "yes" {
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	fmt.Fprintf(w, "\tPRIMARY KEY (%s)\n", strings.Join(pks, ","))
	fmt.Fprintf(w, ") ENGINE=%s DEFAULT CHARSET=UTF8;\n", cmd.Engine)
	return w.String(), nil
}

func (cmd Cmd) LoadData() (string, error) {
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("LOAD DATA LOCAL INFILE 'Reader::%%[1]s' REPLACE INTO TABLE %%[1]s (%s);\n", strings.Join(fields.Names(), ", ")), nil
}

func (cmd Cmd) Fields() (csv.Fields, error) {
	enc := csv.NewEncoder(ioutil.Discard).SetTagKey(cmd.TagKey).SetExpandPath(cmd.ExpandPath...)
	if err := enc.Encode(cmd.Value); err != nil {
		return nil, err
	}
	return enc.Fields(), nil
}

func (cmd Cmd) FullTableName() string {
	return cmd.DB + "." + cmd.Table
}
<|endoftext|>


<commit_before>package gonameparts

import (
	"strings"
)

type NameParts struct {
	ProvidedName string   `json:"provided_name"`
	FullName     string   `json:"full_name"`
	Salutation   string   `json:"salutation"`
	FirstName    string   `json:"first_name"`
	MiddleName   string   `json:"middle_name"`
	LastName     string   `json:"last_name"`
	Suffix       string   `json:"suffix"`
	Aliases      []string `json:"aliases"`
}

type nameString struct {
	FullName string
}

func (n *nameString) cleaned() []string {
	unwanted := []string{",", "."}
	cleaned := []string{}
	for _, x := range n.split() {
		for _, y := range unwanted {
			x = strings.Replace(x, y, "", -1)
		}
		cleaned = append(cleaned, strings.Trim(x))
	}
	return cleaned
}

func (n *nameString) searchParts(parts *[]string) int {
	for i, x := range n.cleaned() {
		for _, y := range *parts {
			if strings.ToUpper(x) == strings.ToUpper(y) {
				return i
			}
		}
	}
	return -1
}

func (n *nameString) looksCorporate() bool {
	return n.searchParts(&corpEntity) > -1
}

func (n *nameString) find(part string) int {
	switch part {
	case "salutation":
		return n.searchParts(&salutations)
	case "generation":
		return n.searchParts(&generations)
	case "suffix":
		return n.searchParts(&suffixes)
	case "lnprefix":
		return n.searchParts(&lnPrefixes)
	case "nonname":
		return n.searchParts(&nonName)
	case "supplemental":
		return n.searchParts(&supplementalInfo)
	default:

	}
	return -1
}

func (n *nameString) split() []string {
	return strings.Fields(n.FullName)
}

func Parse(name string) NameParts {
	n := nameString{FullName: name}
	p := NameParts{ProvidedName: name}

	partMap = make(map[string]int)
	parts = []string{"salutation", "generation", "suffix", "lnprefix", "nonname", "supplemental"}

	for _, part := range parts {
		partMap[part] = n.find(part)
	}

	return p
}
<commit_msg>Fix undefined<commit_after>package gonameparts

import (
	"strings"
)

type NameParts struct {
	ProvidedName string   `json:"provided_name"`
	FullName     string   `json:"full_name"`
	Salutation   string   `json:"salutation"`
	FirstName    string   `json:"first_name"`
	MiddleName   string   `json:"middle_name"`
	LastName     string   `json:"last_name"`
	Suffix       string   `json:"suffix"`
	Aliases      []string `json:"aliases"`
}

type nameString struct {
	FullName string
}

func (n *nameString) cleaned() []string {
	unwanted := []string{",", "."}
	cleaned := []string{}
	for _, x := range n.split() {
		for _, y := range unwanted {
			x = strings.Replace(x, y, "", -1)
		}
		cleaned = append(cleaned, strings.Trim(x, " "))
	}
	return cleaned
}

func (n *nameString) searchParts(parts *[]string) int {
	for i, x := range n.cleaned() {
		for _, y := range *parts {
			if strings.ToUpper(x) == strings.ToUpper(y) {
				return i
			}
		}
	}
	return -1
}

func (n *nameString) looksCorporate() bool {
	return n.searchParts(&corpEntity) > -1
}

func (n *nameString) find(part string) int {
	switch part {
	case "salutation":
		return n.searchParts(&salutations)
	case "generation":
		return n.searchParts(&generations)
	case "suffix":
		return n.searchParts(&suffixes)
	case "lnprefix":
		return n.searchParts(&lnPrefixes)
	case "nonname":
		return n.searchParts(&nonName)
	case "supplemental":
		return n.searchParts(&supplementalInfo)
	default:

	}
	return -1
}

func (n *nameString) split() []string {
	return strings.Fields(n.FullName)
}

func Parse(name string) NameParts {
	n := nameString{FullName: name}
	p := NameParts{ProvidedName: name}

	partMap := make(map[string]int)
	parts := []string{"salutation", "generation", "suffix", "lnprefix", "nonname", "supplemental"}

	for _, part := range parts {
		partMap[part] = n.find(part)
	}

	return p
}
<|endoftext|>


<commit_before>package nats

////////////////////////////////////////////////////////////////////////////////
// Package scoped specific tests here..
////////////////////////////////////////////////////////////////////////////////

import (
	"errors"
	"reflect"
	"testing"
	"time"

	"github.com/nats-io/gnatsd/server"
	gnatsd "github.com/nats-io/gnatsd/test"
)

// Dumb wait program to sync on callbacks, etc... Will timeout
func Wait(ch chan bool) error {
	return WaitTime(ch, 5*time.Second)
}

func WaitTime(ch chan bool, timeout time.Duration) error {
	select {
	case <-ch:
		return nil
	case <-time.After(timeout):
	}
	return errors.New("timeout")
}

////////////////////////////////////////////////////////////////////////////////
// Reconnect tests
////////////////////////////////////////////////////////////////////////////////

var reconnectOpts = Options{
	Url:            "nats://localhost:22222",
	AllowReconnect: true,
	MaxReconnect:   10,
	ReconnectWait:  100 * time.Millisecond,
	Timeout:        DefaultTimeout,
}

func RunServerOnPort(port int) *server.Server {
	opts := gnatsd.DefaultTestOptions
	opts.Port = port
	return RunServerWithOptions(opts)
}

func RunServerWithOptions(opts server.Options) *server.Server {
	return gnatsd.RunServer(&opts)
}

func TestReconnectServerStats(t *testing.T) {
	ts := RunServerOnPort(22222)

	opts := reconnectOpts
	nc, _ := opts.Connect()
	defer nc.Close()
	nc.Flush()

	ts.Shutdown()
	// server is stopped here...

	ts = RunServerOnPort(22222)
	defer ts.Shutdown()

	if err := nc.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	// Make sure the server who is reconnected has the reconnects stats reset.
	_, cur := nc.currentServer()
	if cur.reconnects != 0 {
		t.Fatalf("Current Server's reconnects should be 0 vs %d\n", cur.reconnects)
	}
}

func TestParseStateReconnectFunctionality(t *testing.T) {
	ts := RunServerOnPort(22222)
	ch := make(chan bool)

	opts := reconnectOpts
	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}

	nc, errc := opts.Connect()
	if errc != nil {
		t.Fatalf("Failed to create a connection: %v\n", errc)
	}
	ec, errec := NewEncodedConn(nc, DEFAULT_ENCODER)
	if errec != nil {
		nc.Close()
		t.Fatalf("Failed to create an encoded connection: %v\n", errec)
	}
	defer ec.Close()

	testString := "bar"
	ec.Subscribe("foo", func(s string) {
		if s != testString {
			t.Fatal("String doesn't match")
		}
		ch <- true
	})
	ec.Flush()

	// Got a RACE condition with Travis build. The locking below does not
	// really help because the parser running in the readLoop accesses
	// nc.ps without the connection lock. Sleeping may help better since
	// it would make the memory write in parse.go (when processing the
	// pong) further away from the modification below.
	time.Sleep(1 * time.Second)

	// Simulate partialState, this needs to be cleared
	nc.mu.Lock()
	nc.ps.state = OP_PON
	nc.mu.Unlock()

	ts.Shutdown()
	// server is stopped here...

	if err := Wait(dch); err != nil {
		t.Fatal("Did not get the DisconnectedCB")
	}

	if err := ec.Publish("foo", testString); err != nil {
		t.Fatalf("Failed to publish message: %v\n", err)
	}

	ts = RunServerOnPort(22222)
	defer ts.Shutdown()

	if err := ec.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	if err := Wait(ch); err != nil {
		t.Fatal("Did not receive our message")
	}

	expectedReconnectCount := uint64(1)
	reconnectedCount := ec.Conn.Stats().Reconnects

	if reconnectedCount != expectedReconnectCount {
		t.Fatalf("Reconnect count incorrect: %d vs %d\n",
			reconnectedCount, expectedReconnectCount)
	}
}

////////////////////////////////////////////////////////////////////////////////
// ServerPool tests
////////////////////////////////////////////////////////////////////////////////

var testServers = []string{
	"nats://localhost:1222",
	"nats://localhost:1223",
	"nats://localhost:1224",
	"nats://localhost:1225",
	"nats://localhost:1226",
	"nats://localhost:1227",
	"nats://localhost:1228",
}

func TestServersRandomize(t *testing.T) {
	opts := DefaultOptions
	opts.Servers = testServers
	nc := &Conn{Opts: opts}
	if err := nc.setupServerPool(); err != nil {
		t.Fatalf("Problem setting up Server Pool: %v\n", err)
	}
	// Build []string from srvPool
	clientServers := []string{}
	for _, s := range nc.srvPool {
		clientServers = append(clientServers, s.url.String())
	}
	// In theory this could happen..
	if reflect.DeepEqual(testServers, clientServers) {
		t.Fatalf("ServerPool list not randomized\n")
	}

	// Now test that we do not randomize if proper flag is set.
	opts = DefaultOptions
	opts.Servers = testServers
	opts.NoRandomize = true
	nc = &Conn{Opts: opts}
	if err := nc.setupServerPool(); err != nil {
		t.Fatalf("Problem setting up Server Pool: %v\n", err)
	}
	// Build []string from srvPool
	clientServers = []string{}
	for _, s := range nc.srvPool {
		clientServers = append(clientServers, s.url.String())
	}
	if !reflect.DeepEqual(testServers, clientServers) {
		t.Fatalf("ServerPool list should not be randomized\n")
	}
}

func TestSelectNextServer(t *testing.T) {
	opts := DefaultOptions
	opts.Servers = testServers
	opts.NoRandomize = true
	nc := &Conn{Opts: opts}
	if err := nc.setupServerPool(); err != nil {
		t.Fatalf("Problem setting up Server Pool: %v\n", err)
	}
	if nc.url != nc.srvPool[0].url {
		t.Fatalf("Wrong default selection: %v\n", nc.url)
	}

	sel, err := nc.selectNextServer()
	if err != nil {
		t.Fatalf("Got an err: %v\n", err)
	}
	// Check that we are now looking at #2, and current is now last.
	if len(nc.srvPool) != len(testServers) {
		t.Fatalf("List is incorrect size: %d vs %d\n", len(nc.srvPool), len(testServers))
	}
	if nc.url.String() != testServers[1] {
		t.Fatalf("Selection incorrect: %v vs %v\n", nc.url, testServers[1])
	}
	if nc.srvPool[len(nc.srvPool)-1].url.String() != testServers[0] {
		t.Fatalf("Did not push old to last position\n")
	}
	if sel != nc.srvPool[0] {
		t.Fatalf("Did not return correct server: %v vs %v\n", sel.url, nc.srvPool[0].url)
	}

	// Test that we do not keep servers where we have tried to reconnect past our limit.
	nc.srvPool[0].reconnects = int(opts.MaxReconnect)
	if _, err := nc.selectNextServer(); err != nil {
		t.Fatalf("Got an err: %v\n", err)
	}
	// Check that we are now looking at #3, and current is not in the list.
	if len(nc.srvPool) != len(testServers)-1 {
		t.Fatalf("List is incorrect size: %d vs %d\n", len(nc.srvPool), len(testServers)-1)
	}
	if nc.url.String() != testServers[2] {
		t.Fatalf("Selection incorrect: %v vs %v\n", nc.url, testServers[2])
	}
	if nc.srvPool[len(nc.srvPool)-1].url.String() == testServers[1] {
		t.Fatalf("Did not throw away the last server correctly\n")
	}
}

// This will test that comma separated url strings work properly for
// the Connect() command.
func TestUrlArgument(t *testing.T) {
	check := func(url string, expected []string) {
		if !reflect.DeepEqual(processUrlString(url), expected) {
			t.Fatalf("Got wrong response processing URL: %q, RES: %#v\n", url, processUrlString(url))
		}
	}
	// This is normal case
	oneExpected := []string{"nats://localhost:1222"}

	check("nats://localhost:1222", oneExpected)
	check("nats://localhost:1222 ", oneExpected)
	check(" nats://localhost:1222", oneExpected)
	check(" nats://localhost:1222 ", oneExpected)

	var multiExpected = []string{
		"nats://localhost:1222",
		"nats://localhost:1223",
		"nats://localhost:1224",
	}

	check("nats://localhost:1222,nats://localhost:1223,nats://localhost:1224", multiExpected)
	check("nats://localhost:1222, nats://localhost:1223, nats://localhost:1224", multiExpected)
	check(" nats://localhost:1222, nats://localhost:1223, nats://localhost:1224 ", multiExpected)
	check("nats://localhost:1222,   nats://localhost:1223  ,nats://localhost:1224", multiExpected)
}
<commit_msg>Fix for race<commit_after>package nats

////////////////////////////////////////////////////////////////////////////////
// Package scoped specific tests here..
////////////////////////////////////////////////////////////////////////////////

import (
	"errors"
	"reflect"
	"testing"
	"time"

	"github.com/nats-io/gnatsd/server"
	gnatsd "github.com/nats-io/gnatsd/test"
)

// Dumb wait program to sync on callbacks, etc... Will timeout
func Wait(ch chan bool) error {
	return WaitTime(ch, 5*time.Second)
}

func WaitTime(ch chan bool, timeout time.Duration) error {
	select {
	case <-ch:
		return nil
	case <-time.After(timeout):
	}
	return errors.New("timeout")
}

////////////////////////////////////////////////////////////////////////////////
// Reconnect tests
////////////////////////////////////////////////////////////////////////////////

var reconnectOpts = Options{
	Url:            "nats://localhost:22222",
	AllowReconnect: true,
	MaxReconnect:   10,
	ReconnectWait:  100 * time.Millisecond,
	Timeout:        DefaultTimeout,
}

func RunServerOnPort(port int) *server.Server {
	opts := gnatsd.DefaultTestOptions
	opts.Port = port
	return RunServerWithOptions(opts)
}

func RunServerWithOptions(opts server.Options) *server.Server {
	return gnatsd.RunServer(&opts)
}

func TestReconnectServerStats(t *testing.T) {
	ts := RunServerOnPort(22222)

	opts := reconnectOpts
	nc, _ := opts.Connect()
	defer nc.Close()
	nc.Flush()

	ts.Shutdown()
	// server is stopped here...

	ts = RunServerOnPort(22222)
	defer ts.Shutdown()

	if err := nc.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	// Make sure the server who is reconnected has the reconnects stats reset.
	nc.mu.Lock()
	_, cur := nc.currentServer()
	nc.mu.Unlock()

	if cur.reconnects != 0 {
		t.Fatalf("Current Server's reconnects should be 0 vs %d\n", cur.reconnects)
	}
}

func TestParseStateReconnectFunctionality(t *testing.T) {
	ts := RunServerOnPort(22222)
	ch := make(chan bool)

	opts := reconnectOpts
	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}

	nc, errc := opts.Connect()
	if errc != nil {
		t.Fatalf("Failed to create a connection: %v\n", errc)
	}
	ec, errec := NewEncodedConn(nc, DEFAULT_ENCODER)
	if errec != nil {
		nc.Close()
		t.Fatalf("Failed to create an encoded connection: %v\n", errec)
	}
	defer ec.Close()

	testString := "bar"
	ec.Subscribe("foo", func(s string) {
		if s != testString {
			t.Fatal("String doesn't match")
		}
		ch <- true
	})
	ec.Flush()

	// Got a RACE condition with Travis build. The locking below does not
	// really help because the parser running in the readLoop accesses
	// nc.ps without the connection lock. Sleeping may help better since
	// it would make the memory write in parse.go (when processing the
	// pong) further away from the modification below.
	time.Sleep(1 * time.Second)

	// Simulate partialState, this needs to be cleared
	nc.mu.Lock()
	nc.ps.state = OP_PON
	nc.mu.Unlock()

	ts.Shutdown()
	// server is stopped here...

	if err := Wait(dch); err != nil {
		t.Fatal("Did not get the DisconnectedCB")
	}

	if err := ec.Publish("foo", testString); err != nil {
		t.Fatalf("Failed to publish message: %v\n", err)
	}

	ts = RunServerOnPort(22222)
	defer ts.Shutdown()

	if err := ec.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	if err := Wait(ch); err != nil {
		t.Fatal("Did not receive our message")
	}

	expectedReconnectCount := uint64(1)
	reconnectedCount := ec.Conn.Stats().Reconnects

	if reconnectedCount != expectedReconnectCount {
		t.Fatalf("Reconnect count incorrect: %d vs %d\n",
			reconnectedCount, expectedReconnectCount)
	}
}

////////////////////////////////////////////////////////////////////////////////
// ServerPool tests
////////////////////////////////////////////////////////////////////////////////

var testServers = []string{
	"nats://localhost:1222",
	"nats://localhost:1223",
	"nats://localhost:1224",
	"nats://localhost:1225",
	"nats://localhost:1226",
	"nats://localhost:1227",
	"nats://localhost:1228",
}

func TestServersRandomize(t *testing.T) {
	opts := DefaultOptions
	opts.Servers = testServers
	nc := &Conn{Opts: opts}
	if err := nc.setupServerPool(); err != nil {
		t.Fatalf("Problem setting up Server Pool: %v\n", err)
	}
	// Build []string from srvPool
	clientServers := []string{}
	for _, s := range nc.srvPool {
		clientServers = append(clientServers, s.url.String())
	}
	// In theory this could happen..
	if reflect.DeepEqual(testServers, clientServers) {
		t.Fatalf("ServerPool list not randomized\n")
	}

	// Now test that we do not randomize if proper flag is set.
	opts = DefaultOptions
	opts.Servers = testServers
	opts.NoRandomize = true
	nc = &Conn{Opts: opts}
	if err := nc.setupServerPool(); err != nil {
		t.Fatalf("Problem setting up Server Pool: %v\n", err)
	}
	// Build []string from srvPool
	clientServers = []string{}
	for _, s := range nc.srvPool {
		clientServers = append(clientServers, s.url.String())
	}
	if !reflect.DeepEqual(testServers, clientServers) {
		t.Fatalf("ServerPool list should not be randomized\n")
	}
}

func TestSelectNextServer(t *testing.T) {
	opts := DefaultOptions
	opts.Servers = testServers
	opts.NoRandomize = true
	nc := &Conn{Opts: opts}
	if err := nc.setupServerPool(); err != nil {
		t.Fatalf("Problem setting up Server Pool: %v\n", err)
	}
	if nc.url != nc.srvPool[0].url {
		t.Fatalf("Wrong default selection: %v\n", nc.url)
	}

	sel, err := nc.selectNextServer()
	if err != nil {
		t.Fatalf("Got an err: %v\n", err)
	}
	// Check that we are now looking at #2, and current is now last.
	if len(nc.srvPool) != len(testServers) {
		t.Fatalf("List is incorrect size: %d vs %d\n", len(nc.srvPool), len(testServers))
	}
	if nc.url.String() != testServers[1] {
		t.Fatalf("Selection incorrect: %v vs %v\n", nc.url, testServers[1])
	}
	if nc.srvPool[len(nc.srvPool)-1].url.String() != testServers[0] {
		t.Fatalf("Did not push old to last position\n")
	}
	if sel != nc.srvPool[0] {
		t.Fatalf("Did not return correct server: %v vs %v\n", sel.url, nc.srvPool[0].url)
	}

	// Test that we do not keep servers where we have tried to reconnect past our limit.
	nc.srvPool[0].reconnects = int(opts.MaxReconnect)
	if _, err := nc.selectNextServer(); err != nil {
		t.Fatalf("Got an err: %v\n", err)
	}
	// Check that we are now looking at #3, and current is not in the list.
	if len(nc.srvPool) != len(testServers)-1 {
		t.Fatalf("List is incorrect size: %d vs %d\n", len(nc.srvPool), len(testServers)-1)
	}
	if nc.url.String() != testServers[2] {
		t.Fatalf("Selection incorrect: %v vs %v\n", nc.url, testServers[2])
	}
	if nc.srvPool[len(nc.srvPool)-1].url.String() == testServers[1] {
		t.Fatalf("Did not throw away the last server correctly\n")
	}
}

// This will test that comma separated url strings work properly for
// the Connect() command.
func TestUrlArgument(t *testing.T) {
	check := func(url string, expected []string) {
		if !reflect.DeepEqual(processUrlString(url), expected) {
			t.Fatalf("Got wrong response processing URL: %q, RES: %#v\n", url, processUrlString(url))
		}
	}
	// This is normal case
	oneExpected := []string{"nats://localhost:1222"}

	check("nats://localhost:1222", oneExpected)
	check("nats://localhost:1222 ", oneExpected)
	check(" nats://localhost:1222", oneExpected)
	check(" nats://localhost:1222 ", oneExpected)

	var multiExpected = []string{
		"nats://localhost:1222",
		"nats://localhost:1223",
		"nats://localhost:1224",
	}

	check("nats://localhost:1222,nats://localhost:1223,nats://localhost:1224", multiExpected)
	check("nats://localhost:1222, nats://localhost:1223, nats://localhost:1224", multiExpected)
	check(" nats://localhost:1222, nats://localhost:1223, nats://localhost:1224 ", multiExpected)
	check("nats://localhost:1222,   nats://localhost:1223  ,nats://localhost:1224", multiExpected)
}
<|endoftext|>


<commit_before><commit_msg>update w/ new changes<commit_after><|endoftext|>


<commit_before>package main // FIXME omapi

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"math/rand"
	"net"
	"os"
	"sort"
	"time"
)

type Opcode int32

const (
	OpOpen Opcode = 1 + iota
	OpRefresh
	OpUpdate
	OpNotify
	OpStatus
	OpDelete
)

var Ethernet = []byte{0, 0, 0, 1}
var TokenRing = []byte{0, 0, 0, 6}
var FDDI = []byte{0, 0, 0, 8}

var True = []byte{0, 0, 0, 1}

func (opcode Opcode) String() (ret string) {
	switch opcode {
	case 1:
		ret = "open"
	case 2:
		ret = "refresh"
	case 3:
		ret = "update"
	case 4:
		ret = "notify"
	case 5:
		ret = "status"
	case 6:
		ret = "delete"
	}

	return
}

// TODO add size checks for all operations
type buffer struct {
	buffer *bytes.Buffer
}

func newBuffer() *buffer {
	return &buffer{new(bytes.Buffer)}
}

func (b *buffer) add_bytes(data []byte) {
	b.buffer.Write(data)
}

func (b *buffer) add(data interface{}) {
	err := binary.Write(b.buffer, binary.BigEndian, data)
	if err != nil {
		panic(err)
	}
}

func (b *buffer) add_map(data map[string][]byte) {
	// We need to add the map in a deterministic order for signing to
	// work, so we first sort the keys in alphabetical order, then use
	// that order to access the map entries.

	keys := make(sort.StringSlice, 0, len(data))

	for key := range data {
		keys = append(keys, key)
	}

	sort.Sort(keys)

	for _, key := range keys {
		value := data[key]

		b.add(int16(len(key)))
		b.add([]byte(key))

		b.add(int32(len(value)))
		b.add(value)
	}

	b.add([]byte("\x00\x00"))
}

func (b *buffer) bytes() []byte {
	return b.buffer.Bytes()
}

type Message struct {
	AuthID    int32
	Opcode    Opcode
	Handle    int32
	Tid       int32
	Rid       int32
	Message   map[string][]byte
	Object    map[string][]byte
	Signature []byte
}

func NewMessage() *Message {
	msg := &Message{
		Tid:     rand.Int31(),
		Message: make(map[string][]byte),
		Object:  make(map[string][]byte),
	}

	return msg
}

func NewOpenMessage(typeName string) *Message {
	message := NewMessage()
	message.Opcode = OpOpen
	message.Message["type"] = []byte(typeName)

	return message
}

func NewDeleteMessage(handle int32) *Message {
	message := NewMessage()
	message.Opcode = OpDelete
	message.Handle = handle

	return message
}

func (m *Message) Bytes(forSigning bool) []byte {
	ret := newBuffer()
	if !forSigning {
		ret.add(m.AuthID)
	}

	ret.add(int32(len(m.Signature)))
	ret.add(m.Opcode)
	ret.add(m.Handle)
	ret.add(m.Tid)
	ret.add(m.Rid)
	ret.add_map(m.Message)
	ret.add_map(m.Object)
	if !forSigning {
		ret.add(m.Signature)
	}

	return ret.buffer.Bytes()
}

func (m *Message) Sign(auth Authenticator) {
	m.AuthID = auth.AuthID()
	m.Signature = auth.Sign(m)
}

func (m *Message) Verify(auth Authenticator) bool {
	return bytes.Equal(auth.Sign(m), m.Signature)
}

func (m *Message) IsResponseTo(other *Message) bool {
	return m.Rid == other.Tid
}

type Connection struct {
	Hostname      string
	Port          int
	Username      string
	Key           string
	Authenticator Authenticator
	connection    *net.TCPConn
	inBuffer      *bytes.Buffer
}

func NewConnection(hostname string, port int, username string, key string) *Connection {
	con := &Connection{
		Hostname:      hostname,
		Port:          port,
		Username:      username,
		Key:           key,
		Authenticator: new(NullAuthenticator),
		inBuffer:      new(bytes.Buffer),
	}

	var newAuth Authenticator = new(NullAuthenticator)

	if len(username) > 0 && len(key) > 0 {
		decodedKey, err := base64.StdEncoding.DecodeString(key)
		if err != nil {
			panic(err)
		}
		newAuth = &HMACMD5Authenticator{username, decodedKey, -1}
	}

	raddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", hostname, port))
	if err != nil {
		// TODO return the error instead
		panic(err)
	}
	tcpConn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		// TODO return the error instead
		panic(err)
	}

	con.connection = tcpConn

	con.SendProtocolInitialization()
	con.ReceiveProtocolInitialization()
	con.initializeAuthenticator(newAuth)

	return con
}

func (con *Connection) initializeAuthenticator(auth Authenticator) {
	if _, ok := auth.(*NullAuthenticator); ok {
		return
	}

	message := NewOpenMessage("authenticator")
	for key, value := range auth.AuthObject() {
		message.Object[key] = value
	}

	response := con.queryServer(message)

	if response.Opcode != OpUpdate {
		panic("received non-update response for open")
	}

	if response.Handle == 0 {
		panic("received invalid authid from server")
	}

	auth.SetAuthID(response.Handle)
	con.Authenticator = auth
}

func (con *Connection) queryServer(msg *Message) *Message {
	msg.Sign(con.Authenticator)
	con.Send(msg.Bytes(false))
	response := con.parseMessage()
	if !response.IsResponseTo(msg) {
		panic("received message is not the desired response")
	}

	// TODO check authid

	return response
}

func (con *Connection) Send(data []byte) (n int, err error) {
	return con.connection.Write(data)
}

func (con *Connection) SendProtocolInitialization() {
	buf := newBuffer()
	buf.add(int32(100)) // Protocol version
	buf.add(int32(24))  // Header size
	con.Send(buf.bytes())
}

func (con *Connection) Read() {
	buf := make([]byte, 2048)
	n, err := con.connection.Read(buf)
	if err != nil {
		panic(err)
	}

	con.inBuffer.Write(buf[0:n])
}

func (con *Connection) waitForN(n int) {
	for con.inBuffer.Len() < n {
		con.Read()
	}
}

func (con *Connection) parseStartupMessage() (version, headerSize int32) {
	con.waitForN(8)

	binary.Read(con.inBuffer, binary.BigEndian, &version)
	binary.Read(con.inBuffer, binary.BigEndian, &headerSize)

	return
}

func (con *Connection) parseMap() map[string][]byte {
	dict := make(map[string][]byte)

	var (
		keyLength   int16
		valueLength int32
		key         []byte
		value       []byte
	)

	for {
		con.waitForN(2)
		binary.Read(con.inBuffer, binary.BigEndian, &keyLength)
		if keyLength == 0 {
			// end of map
			break
		}

		con.waitForN(int(keyLength))
		key = make([]byte, keyLength)
		con.inBuffer.Read(key)

		con.waitForN(4)
		binary.Read(con.inBuffer, binary.BigEndian, &valueLength)
		con.waitForN(int(valueLength))
		value = make([]byte, valueLength)
		con.inBuffer.Read(value)

		dict[string(key)] = value
	}

	return dict
}

func (con *Connection) parseMessage() *Message {
	message := new(Message)
	con.waitForN(24) // authid + authlen + opcode + handle + tid + rid

	var authlen int32

	binary.Read(con.inBuffer, binary.BigEndian, &message.AuthID)
	binary.Read(con.inBuffer, binary.BigEndian, &authlen)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Opcode)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Handle)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Tid)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Rid)

	message.Message = con.parseMap()
	message.Object = con.parseMap()

	con.waitForN(int(authlen))
	message.Signature = make([]byte, authlen)
	con.inBuffer.Read(message.Signature)

	return message
}

func (con *Connection) ReceiveProtocolInitialization() {
	version, headerSize := con.parseStartupMessage()
	if version != 100 {
		panic("version mismatch")
	}

	if headerSize != 24 {
		panic("header size mismatch")
	}
}

type Authenticator interface {
	Sign(*Message) []byte
	AuthObject() map[string][]byte
	AuthLen() int32
	AuthID() int32
	SetAuthID(int32)
}

type NullAuthenticator struct{}

func (_ *NullAuthenticator) AuthObject() map[string][]byte {
	return make(map[string][]byte)
}

func (_ *NullAuthenticator) Sign(_ *Message) []byte {
	return []byte("")
}

func (_ *NullAuthenticator) AuthLen() int32 {
	return 0
}

func (_ *NullAuthenticator) AuthID() int32 {
	return 0
}

func (_ *NullAuthenticator) SetAuthID(_ int32) {
}

type HMACMD5Authenticator struct {
	Username string
	Key      []byte
	_AuthID  int32
}

func (auth *HMACMD5Authenticator) AuthObject() map[string][]byte {
	ret := make(map[string][]byte)
	ret["name"] = []byte(auth.Username)
	ret["algorithm"] = []byte("hmac-md5.SIG-ALG.REG.INT.")

	return ret
}

func (auth *HMACMD5Authenticator) Sign(m *Message) []byte {
	hmac := hmac.New(md5.New, auth.Key)

	// The signature's length is part of the message that we are
	// signing, so initialize the signature with the correct length.
	m.Signature = bytes.Repeat([]byte("\x00"), int(auth.AuthLen()))
	hmac.Write(m.Bytes(true))

	return hmac.Sum(nil)
}

func (_ *HMACMD5Authenticator) AuthLen() int32 {
	return 16
}

func (auth *HMACMD5Authenticator) AuthID() int32 {
	return auth._AuthID
}

func (auth *HMACMD5Authenticator) SetAuthID(val int32) {
	auth._AuthID = val
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	key := os.Getenv("OMAPI_KEY")
	connection := NewConnection("192.168.1.1", 7911, "omapi_key", key)

	// message := NewOpenMessage("lease")
	// mac, _ := net.ParseMAC("bc:ae:c5:76:1d:5a")
	// message.Object["hardware-address"] = []byte(mac)
	// response := connection.queryServer(message)
	// fmt.Println(response)

	// message := NewOpenMessage("host")
	// // message.Message["create"] = Ethernet
	// mac, _ := net.ParseMAC("08:00:27:4f:72:21")
	// message.Object["hardware-address"] = []byte(mac)

	// // buf := new(bytes.Buffer)
	// // binary.Write(buf, binary.BigEndian, int32(1))

	// message.Object["hardware-type"] = Ethernet

	// response := connection.queryServer(message)
	// fmt.Println(response)
	// response = connection.queryServer(NewDeleteMessage(response.Handle))
	// fmt.Println(response)

	mac, _ := net.ParseMAC("08:00:27:4f:72:21")
	ip := net.ParseIP("192.168.1.33")
	message := NewOpenMessage("host")

	message.Message["create"] = True
	message.Message["exclusive"] = True
	message.Object["hardware-address"] = []byte(mac)
	message.Object["hardware-type"] = Ethernet
	message.Object["ip-address"] = []byte(ip[12:])
	message.Object["statements"] = []byte("ddns-hostname=\"win7.vm\";")
	message.Object["name"] = []byte("win7.vm")

	response := connection.queryServer(message)
	if response.Opcode != OpUpdate {
		fmt.Println("add failed:", string(response.Message["message"]))
	}
	// fmt.Println(response)
}
<commit_msg>more visibility changes<commit_after>package main // FIXME omapi

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"math/rand"
	"net"
	"os"
	"sort"
	"time"
)

type Opcode int32

const (
	OpOpen Opcode = 1 + iota
	OpRefresh
	OpUpdate
	OpNotify
	OpStatus
	OpDelete
)

var Ethernet = []byte{0, 0, 0, 1}
var TokenRing = []byte{0, 0, 0, 6}
var FDDI = []byte{0, 0, 0, 8}

var True = []byte{0, 0, 0, 1}

func (opcode Opcode) String() (ret string) {
	switch opcode {
	case 1:
		ret = "open"
	case 2:
		ret = "refresh"
	case 3:
		ret = "update"
	case 4:
		ret = "notify"
	case 5:
		ret = "status"
	case 6:
		ret = "delete"
	}

	return
}

// TODO add size checks for all operations
type buffer struct {
	buffer *bytes.Buffer
}

func newBuffer() *buffer {
	return &buffer{new(bytes.Buffer)}
}

func (b *buffer) add_bytes(data []byte) {
	b.buffer.Write(data)
}

func (b *buffer) add(data interface{}) {
	err := binary.Write(b.buffer, binary.BigEndian, data)
	if err != nil {
		panic(err)
	}
}

func (b *buffer) add_map(data map[string][]byte) {
	// We need to add the map in a deterministic order for signing to
	// work, so we first sort the keys in alphabetical order, then use
	// that order to access the map entries.

	keys := make(sort.StringSlice, 0, len(data))

	for key := range data {
		keys = append(keys, key)
	}

	sort.Sort(keys)

	for _, key := range keys {
		value := data[key]

		b.add(int16(len(key)))
		b.add([]byte(key))

		b.add(int32(len(value)))
		b.add(value)
	}

	b.add([]byte("\x00\x00"))
}

func (b *buffer) bytes() []byte {
	return b.buffer.Bytes()
}

type Message struct {
	AuthID    int32
	Opcode    Opcode
	Handle    int32
	Tid       int32
	Rid       int32
	Message   map[string][]byte
	Object    map[string][]byte
	Signature []byte
}

func NewMessage() *Message {
	msg := &Message{
		Tid:     rand.Int31(),
		Message: make(map[string][]byte),
		Object:  make(map[string][]byte),
	}

	return msg
}

func NewOpenMessage(typeName string) *Message {
	message := NewMessage()
	message.Opcode = OpOpen
	message.Message["type"] = []byte(typeName)

	return message
}

func NewDeleteMessage(handle int32) *Message {
	message := NewMessage()
	message.Opcode = OpDelete
	message.Handle = handle

	return message
}

func (m *Message) Bytes(forSigning bool) []byte {
	ret := newBuffer()
	if !forSigning {
		ret.add(m.AuthID)
	}

	ret.add(int32(len(m.Signature)))
	ret.add(m.Opcode)
	ret.add(m.Handle)
	ret.add(m.Tid)
	ret.add(m.Rid)
	ret.add_map(m.Message)
	ret.add_map(m.Object)
	if !forSigning {
		ret.add(m.Signature)
	}

	return ret.buffer.Bytes()
}

func (m *Message) Sign(auth Authenticator) {
	m.AuthID = auth.AuthID()
	m.Signature = auth.Sign(m)
}

func (m *Message) Verify(auth Authenticator) bool {
	return bytes.Equal(auth.Sign(m), m.Signature)
}

func (m *Message) IsResponseTo(other *Message) bool {
	return m.Rid == other.Tid
}

type Connection struct {
	Hostname      string
	Port          int
	Username      string
	Key           string
	Authenticator Authenticator
	connection    *net.TCPConn
	inBuffer      *bytes.Buffer
}

func NewConnection(hostname string, port int, username string, key string) *Connection {
	con := &Connection{
		Hostname:      hostname,
		Port:          port,
		Username:      username,
		Key:           key,
		Authenticator: new(NullAuthenticator),
		inBuffer:      new(bytes.Buffer),
	}

	var newAuth Authenticator = new(NullAuthenticator)

	if len(username) > 0 && len(key) > 0 {
		decodedKey, err := base64.StdEncoding.DecodeString(key)
		if err != nil {
			panic(err)
		}
		newAuth = &HMACMD5Authenticator{username, decodedKey, -1}
	}

	raddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", hostname, port))
	if err != nil {
		// TODO return the error instead
		panic(err)
	}
	tcpConn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		// TODO return the error instead
		panic(err)
	}

	con.connection = tcpConn

	con.sendProtocolInitialization()
	con.receiveProtocolInitialization()
	con.initializeAuthenticator(newAuth)

	return con
}

func (con *Connection) initializeAuthenticator(auth Authenticator) {
	if _, ok := auth.(*NullAuthenticator); ok {
		return
	}

	message := NewOpenMessage("authenticator")
	for key, value := range auth.AuthObject() {
		message.Object[key] = value
	}

	response := con.queryServer(message)

	if response.Opcode != OpUpdate {
		panic("received non-update response for open")
	}

	if response.Handle == 0 {
		panic("received invalid authid from server")
	}

	auth.SetAuthID(response.Handle)
	con.Authenticator = auth
}

func (con *Connection) queryServer(msg *Message) *Message {
	msg.Sign(con.Authenticator)
	con.send(msg.Bytes(false))
	response := con.parseMessage()
	if !response.IsResponseTo(msg) {
		panic("received message is not the desired response")
	}

	// TODO check authid

	return response
}

func (con *Connection) send(data []byte) (n int, err error) {
	return con.connection.Write(data)
}

func (con *Connection) sendProtocolInitialization() {
	buf := newBuffer()
	buf.add(int32(100)) // Protocol version
	buf.add(int32(24))  // Header size
	con.send(buf.bytes())
}

func (con *Connection) read() {
	buf := make([]byte, 2048)
	n, err := con.connection.Read(buf)
	if err != nil {
		panic(err)
	}

	con.inBuffer.Write(buf[0:n])
}

func (con *Connection) waitForN(n int) {
	for con.inBuffer.Len() < n {
		con.read()
	}
}

func (con *Connection) parseStartupMessage() (version, headerSize int32) {
	con.waitForN(8)

	binary.Read(con.inBuffer, binary.BigEndian, &version)
	binary.Read(con.inBuffer, binary.BigEndian, &headerSize)

	return
}

func (con *Connection) parseMap() map[string][]byte {
	dict := make(map[string][]byte)

	var (
		keyLength   int16
		valueLength int32
		key         []byte
		value       []byte
	)

	for {
		con.waitForN(2)
		binary.Read(con.inBuffer, binary.BigEndian, &keyLength)
		if keyLength == 0 {
			// end of map
			break
		}

		con.waitForN(int(keyLength))
		key = make([]byte, keyLength)
		con.inBuffer.Read(key)

		con.waitForN(4)
		binary.Read(con.inBuffer, binary.BigEndian, &valueLength)
		con.waitForN(int(valueLength))
		value = make([]byte, valueLength)
		con.inBuffer.Read(value)

		dict[string(key)] = value
	}

	return dict
}

func (con *Connection) parseMessage() *Message {
	message := new(Message)
	con.waitForN(24) // authid + authlen + opcode + handle + tid + rid

	var authlen int32

	binary.Read(con.inBuffer, binary.BigEndian, &message.AuthID)
	binary.Read(con.inBuffer, binary.BigEndian, &authlen)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Opcode)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Handle)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Tid)
	binary.Read(con.inBuffer, binary.BigEndian, &message.Rid)

	message.Message = con.parseMap()
	message.Object = con.parseMap()

	con.waitForN(int(authlen))
	message.Signature = make([]byte, authlen)
	con.inBuffer.Read(message.Signature)

	return message
}

func (con *Connection) receiveProtocolInitialization() {
	version, headerSize := con.parseStartupMessage()
	if version != 100 {
		panic("version mismatch")
	}

	if headerSize != 24 {
		panic("header size mismatch")
	}
}

type Authenticator interface {
	Sign(*Message) []byte
	AuthObject() map[string][]byte
	AuthLen() int32
	AuthID() int32
	SetAuthID(int32)
}

type NullAuthenticator struct{}

func (_ *NullAuthenticator) AuthObject() map[string][]byte {
	return make(map[string][]byte)
}

func (_ *NullAuthenticator) Sign(_ *Message) []byte {
	return []byte("")
}

func (_ *NullAuthenticator) AuthLen() int32 {
	return 0
}

func (_ *NullAuthenticator) AuthID() int32 {
	return 0
}

func (_ *NullAuthenticator) SetAuthID(_ int32) {
}

type HMACMD5Authenticator struct {
	Username string
	Key      []byte
	_AuthID  int32
}

func (auth *HMACMD5Authenticator) AuthObject() map[string][]byte {
	ret := make(map[string][]byte)
	ret["name"] = []byte(auth.Username)
	ret["algorithm"] = []byte("hmac-md5.SIG-ALG.REG.INT.")

	return ret
}

func (auth *HMACMD5Authenticator) Sign(m *Message) []byte {
	hmac := hmac.New(md5.New, auth.Key)

	// The signature's length is part of the message that we are
	// signing, so initialize the signature with the correct length.
	m.Signature = bytes.Repeat([]byte("\x00"), int(auth.AuthLen()))
	hmac.Write(m.Bytes(true))

	return hmac.Sum(nil)
}

func (_ *HMACMD5Authenticator) AuthLen() int32 {
	return 16
}

func (auth *HMACMD5Authenticator) AuthID() int32 {
	return auth._AuthID
}

func (auth *HMACMD5Authenticator) SetAuthID(val int32) {
	auth._AuthID = val
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	key := os.Getenv("OMAPI_KEY")
	connection := NewConnection("192.168.1.1", 7911, "omapi_key", key)

	// message := NewOpenMessage("lease")
	// mac, _ := net.ParseMAC("bc:ae:c5:76:1d:5a")
	// message.Object["hardware-address"] = []byte(mac)
	// response := connection.queryServer(message)
	// fmt.Println(response)

	// message := NewOpenMessage("host")
	// // message.Message["create"] = Ethernet
	// mac, _ := net.ParseMAC("08:00:27:4f:72:21")
	// message.Object["hardware-address"] = []byte(mac)

	// // buf := new(bytes.Buffer)
	// // binary.Write(buf, binary.BigEndian, int32(1))

	// message.Object["hardware-type"] = Ethernet

	// response := connection.queryServer(message)
	// fmt.Println(response)
	// response = connection.queryServer(NewDeleteMessage(response.Handle))
	// fmt.Println(response)

	mac, _ := net.ParseMAC("08:00:27:4f:72:21")
	ip := net.ParseIP("192.168.1.33")
	message := NewOpenMessage("host")

	message.Message["create"] = True
	message.Message["exclusive"] = True
	message.Object["hardware-address"] = []byte(mac)
	message.Object["hardware-type"] = Ethernet
	message.Object["ip-address"] = []byte(ip[12:])
	message.Object["statements"] = []byte("ddns-hostname=\"win7.vm\";")
	message.Object["name"] = []byte("win7.vm")

	response := connection.queryServer(message)
	if response.Opcode != OpUpdate {
		fmt.Println("add failed:", string(response.Message["message"]))
	}
	// fmt.Println(response)
}
<|endoftext|>


<commit_before>package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/fatih/set"
	zglob "github.com/mattn/go-zglob"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/mohae/deepcopy"
	"github.com/synchro-food/filelint/lib"
)

type Config struct {
	File    File     `yaml:"files"`
	Targets []Target `yaml:"targets"`
}

func NewConfig(configFile string) (*Config, error) {
	conf, err := NewDefaultConfig()
	if err != nil {
		return nil, err
	}

	userConfig := &Config{}
	src, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil, err
	}

	if err := yaml.Unmarshal(src, &userConfig); err != nil {
		return nil, err
	}

	conf.Merge(userConfig)

	return conf, nil
}

func NewDefaultConfig() (*Config, error) {
	conf := &Config{}
	src, err := configDefaultYmlBytes()
	if err != nil {
		return nil, err
	}

	if err := yaml.Unmarshal(src, &conf); err != nil {
		return nil, err
	}

	return conf, nil
}

func (src *Config) Merge(dst *Config) {
	if len(dst.File.Include) > 0 {
		src.File.Include = dst.File.Include
	}
	src.File.Exclude = append(src.File.Exclude, dst.File.Exclude...)
	src.Targets = append(src.Targets, dst.Targets...)
}

func (cfg *Config) MatchedRule(file string) RuleMap {
	rm := make(RuleMap)

	for _, t := range cfg.Targets {
		if match(file, t.Patterns) {
			rm = rm.Merge(t.Rule)
		}
	}

	return rm
}

func match(file string, patterns []string) bool {
	if len(patterns) == 0 {
		return true
	}

	for _, pattern := range patterns {
		ok, err := zglob.Match(pattern, file)
		if err != nil {
			return false
		}
		if ok {
			return true
		}
	}

	return false
}

type File struct {
	Include []string `yaml:"include"`
	Exclude []string `yaml:"exclude"`
}

func (f File) FindTargets() ([]string, error) {
	f.Include = addGlobSignIfDir(f.Include...)
	f.Exclude = addGlobSignIfDir(f.Exclude...)

	inSet, err := expandGlob(f.Include)
	if err != nil {
		return nil, err
	}

	exSet, _ := expandGlob(f.Exclude)
	if err != nil {
		return nil, err
	}

	fileSet := set.Difference(inSet, exSet)
	files := set.StringSlice(fileSet)
	files = lib.FindTextFiles(files)

	return files, nil
}

func expandGlob(files []string) (*set.Set, error) {
	fileSet := set.New()

	for _, f := range files {
		expanded, err := zglob.Glob(f)
		if err != nil {
			return nil, err
		}

		for _, ex := range expanded {
			fileSet.Add(ex)
		}
	}

	return fileSet, nil
}

func addGlobSignIfDir(files ...string) (dst []string) {
	dst = make([]string, 0, len(files))

	for _, f := range files {
		if !strings.Contains(f, "*") {
			info, err := os.Stat(f)
			if err != nil {
				continue
			}

			if info.IsDir() {
				f = filepath.Join(f, "**", "*")
			}
		}

		dst = append(dst, f)
	}

	return dst
}

type Target struct {
	Patterns []string `yaml:"patterns"`
	Rule     RuleMap  `yaml:"rules"`
}

type RuleMap map[string]map[string]interface{}

func (rm RuleMap) Merge(dst RuleMap) (ret RuleMap) {
	ret = deepcopy.Copy(rm).(RuleMap)

	for ruleName, options := range dst {
		if _, ok := ret[ruleName]; ok {
			for optionKey, value := range options {
				ret[ruleName][optionKey] = value
			}
		} else {
			ret[ruleName] = options
		}
	}

	return ret
}

var (
	fileName   = ".filelint.yml"
	searchPath = "."
)

func SearchConfigFile() (f string, ok bool, err error) {
	if f := filepath.Join(searchPath, fileName); lib.IsExist(f) {
		return f, true, nil
	}

	if gitRoot, err := lib.FindGitRootPath(searchPath); err != nil {
		if err != lib.ErrNotGitRepository {
			return "", false, err
		}
	} else {
		if f := filepath.Join(gitRoot, fileName); lib.IsExist(f) {
			return f, true, nil
		}
	}

	if home, err := homedir.Dir(); err != nil {
		return "", false, err
	} else {
		if f := filepath.Join(home, fileName); lib.IsExist(f) {
			return f, true, nil
		}
	}

	return "", false, nil
}
<commit_msg>Sort target files by name<commit_after>package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/fatih/set"
	zglob "github.com/mattn/go-zglob"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/mohae/deepcopy"
	"github.com/synchro-food/filelint/lib"
)

type Config struct {
	File    File     `yaml:"files"`
	Targets []Target `yaml:"targets"`
}

func NewConfig(configFile string) (*Config, error) {
	conf, err := NewDefaultConfig()
	if err != nil {
		return nil, err
	}

	userConfig := &Config{}
	src, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil, err
	}

	if err := yaml.Unmarshal(src, &userConfig); err != nil {
		return nil, err
	}

	conf.Merge(userConfig)

	return conf, nil
}

func NewDefaultConfig() (*Config, error) {
	conf := &Config{}
	src, err := configDefaultYmlBytes()
	if err != nil {
		return nil, err
	}

	if err := yaml.Unmarshal(src, &conf); err != nil {
		return nil, err
	}

	return conf, nil
}

func (src *Config) Merge(dst *Config) {
	if len(dst.File.Include) > 0 {
		src.File.Include = dst.File.Include
	}
	src.File.Exclude = append(src.File.Exclude, dst.File.Exclude...)
	src.Targets = append(src.Targets, dst.Targets...)
}

func (cfg *Config) MatchedRule(file string) RuleMap {
	rm := make(RuleMap)

	for _, t := range cfg.Targets {
		if match(file, t.Patterns) {
			rm = rm.Merge(t.Rule)
		}
	}

	return rm
}

func match(file string, patterns []string) bool {
	if len(patterns) == 0 {
		return true
	}

	for _, pattern := range patterns {
		ok, err := zglob.Match(pattern, file)
		if err != nil {
			return false
		}
		if ok {
			return true
		}
	}

	return false
}

type File struct {
	Include []string `yaml:"include"`
	Exclude []string `yaml:"exclude"`
}

func (f File) FindTargets() ([]string, error) {
	f.Include = addGlobSignIfDir(f.Include...)
	f.Exclude = addGlobSignIfDir(f.Exclude...)

	inSet, err := expandGlob(f.Include)
	if err != nil {
		return nil, err
	}

	exSet, _ := expandGlob(f.Exclude)
	if err != nil {
		return nil, err
	}

	fileSet := set.Difference(inSet, exSet)
	files := set.StringSlice(fileSet)
	files = lib.FindTextFiles(files)

	sort.Strings(files)

	return files, nil
}

func expandGlob(files []string) (*set.Set, error) {
	fileSet := set.New()

	for _, f := range files {
		expanded, err := zglob.Glob(f)
		if err != nil {
			return nil, err
		}

		for _, ex := range expanded {
			fileSet.Add(ex)
		}
	}

	return fileSet, nil
}

func addGlobSignIfDir(files ...string) (dst []string) {
	dst = make([]string, 0, len(files))

	for _, f := range files {
		if !strings.Contains(f, "*") {
			info, err := os.Stat(f)
			if err != nil {
				continue
			}

			if info.IsDir() {
				f = filepath.Join(f, "**", "*")
			}
		}

		dst = append(dst, f)
	}

	return dst
}

type Target struct {
	Patterns []string `yaml:"patterns"`
	Rule     RuleMap  `yaml:"rules"`
}

type RuleMap map[string]map[string]interface{}

func (rm RuleMap) Merge(dst RuleMap) (ret RuleMap) {
	ret = deepcopy.Copy(rm).(RuleMap)

	for ruleName, options := range dst {
		if _, ok := ret[ruleName]; ok {
			for optionKey, value := range options {
				ret[ruleName][optionKey] = value
			}
		} else {
			ret[ruleName] = options
		}
	}

	return ret
}

var (
	fileName   = ".filelint.yml"
	searchPath = "."
)

func SearchConfigFile() (f string, ok bool, err error) {
	if f := filepath.Join(searchPath, fileName); lib.IsExist(f) {
		return f, true, nil
	}

	if gitRoot, err := lib.FindGitRootPath(searchPath); err != nil {
		if err != lib.ErrNotGitRepository {
			return "", false, err
		}
	} else {
		if f := filepath.Join(gitRoot, fileName); lib.IsExist(f) {
			return f, true, nil
		}
	}

	if home, err := homedir.Dir(); err != nil {
		return "", false, err
	} else {
		if f := filepath.Join(home, fileName); lib.IsExist(f) {
			return f, true, nil
		}
	}

	return "", false, nil
}
<|endoftext|>