<commit_before>package http

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/guzzlerio/corcel/core"
	"github.com/guzzlerio/corcel/logger"
)

//Action ...
type HTTPAction struct {
	client  *http.Client
	URL     string
	Method  string
	Body    string
	Headers http.Header
}

func CreateAction() HTTPAction {
	tr := &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   0,
			KeepAlive: 0,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		DisableKeepAlives:   true,
		MaxIdleConnsPerHost: 10,
	}
	var instance = HTTPAction{
		client: &http.Client{Transport: tr},
	}
	return instance
}

//Execute ...
func (instance HTTPAction) Execute(ctx context.Context, executionContext core.ExecutionContext) core.ExecutionResult {

	result := core.ExecutionResult{}

	select {
	case <-ctx.Done():
		return result

	default:

		if instance.Body != "" && instance.Body[0] == '@' {
			contents, err := ioutil.ReadFile(instance.Body[1:])
			if err != nil {
				result[core.ErrorUrn.String()] = err
				return result
			}
			instance.Body = string(contents)
		}

		var requestURL = instance.URL
		var method = instance.Method
		var headers = http.Header{}
		var body = instance.Body

		for k := range instance.Headers {
			headers.Set(k, instance.Headers.Get(k))
		}
		if executionContext["$httpHeaders"] != nil {
			for hKey, hValue := range executionContext["$httpHeaders"].(map[string]interface{}) {
				headerKey := hKey
				headerValue := hValue.(string)

				if headers.Get(headerKey) == "" {
					headers.Set(headerKey, headerValue)
				}
			}
		}

		for k, v := range executionContext {
			token := k
			switch value := v.(type) {
			case string:
				for hK := range headers {
					replacement := strings.Replace(headers.Get(hK), token, value, -1)
					headers.Set(hK, replacement)
				}
				requestURL = strings.Replace(requestURL, token, value, -1)
				body = strings.Replace(body, token, value, -1)
			}
		}

		requestBody := bytes.NewBuffer([]byte(body))
		req, err := http.NewRequest(method, requestURL, requestBody)
		req = req.WithContext(ctx)
		//req.Cancel = cancellation
		//This should be a configuration item.  It allows the client to work
		//in a way similar to a server which does not support HTTP KeepAlive
		//After each request the client channel is closed.  When set to true
		//the performance overhead is large in terms of Network IO throughput

		req.Close = true

		if err != nil {
			result[core.ErrorUrn.String()] = err
			return result
		}

		req.Header = headers

		response, err := instance.client.Do(req)
		if err != nil {
			result[core.ErrorUrn.String()] = err
			logrus.Errorf("HTTP ERROR %v", err)
			return result
		}
		defer func() {
			err := response.Body.Close()
			if err != nil {
				logger.Log.Warnf("Error closing response Body %v", err)
			}
		}()

		requestBytes, _ := httputil.DumpRequest(req, true)
		responseBytes, _ := httputil.DumpResponse(response, true)

		if response.StatusCode >= 500 {
			result[core.ErrorUrn.String()] = fmt.Sprintf("Server Error %d", response.StatusCode)
		}

		result[RequestURLUrn.String()] = req.URL.String()
		result[core.BytesSentCountUrn.String()] = len(requestBytes)
		result[core.BytesReceivedCountUrn.String()] = len(responseBytes)
		//result[RequestHeadersUrn.String()] = req.Header

		for k, v := range response.Header {
			var key = RequestHeadersUrn.Name(k).String()
			result[key] = strings.Join(v, ",")
		}

		for k, v := range response.Header {
			var key = ResponseHeadersUrn.Name(k).String()
			result[key] = strings.Join(v, ",")
		}

		//TODO: We need a Response Headers key too
		result[ResponseStatusUrn.String()] = response.StatusCode

		result[core.BytesSentUrn.String()] = string(requestBytes)
		result[core.BytesReceivedUrn.String()] = string(responseBytes)

		return result
	}
}
<commit_msg>Removed the logging for now as we need for specifics checks for what to log when<commit_after>package http

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"strings"
	"time"

	"github.com/guzzlerio/corcel/core"
	"github.com/guzzlerio/corcel/logger"
)

//Action ...
type HTTPAction struct {
	client  *http.Client
	URL     string
	Method  string
	Body    string
	Headers http.Header
}

func CreateAction() HTTPAction {
	tr := &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   0,
			KeepAlive: 0,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		DisableKeepAlives:   true,
		MaxIdleConnsPerHost: 10,
	}
	var instance = HTTPAction{
		client: &http.Client{Transport: tr},
	}
	return instance
}

//Execute ...
func (instance HTTPAction) Execute(ctx context.Context, executionContext core.ExecutionContext) core.ExecutionResult {

	result := core.ExecutionResult{}

	select {
	case <-ctx.Done():
		return result

	default:

		if instance.Body != "" && instance.Body[0] == '@' {
			contents, err := ioutil.ReadFile(instance.Body[1:])
			if err != nil {
				result[core.ErrorUrn.String()] = err
				return result
			}
			instance.Body = string(contents)
		}

		var requestURL = instance.URL
		var method = instance.Method
		var headers = http.Header{}
		var body = instance.Body

		for k := range instance.Headers {
			headers.Set(k, instance.Headers.Get(k))
		}
		if executionContext["$httpHeaders"] != nil {
			for hKey, hValue := range executionContext["$httpHeaders"].(map[string]interface{}) {
				headerKey := hKey
				headerValue := hValue.(string)

				if headers.Get(headerKey) == "" {
					headers.Set(headerKey, headerValue)
				}
			}
		}

		for k, v := range executionContext {
			token := k
			switch value := v.(type) {
			case string:
				for hK := range headers {
					replacement := strings.Replace(headers.Get(hK), token, value, -1)
					headers.Set(hK, replacement)
				}
				requestURL = strings.Replace(requestURL, token, value, -1)
				body = strings.Replace(body, token, value, -1)
			}
		}

		requestBody := bytes.NewBuffer([]byte(body))
		req, err := http.NewRequest(method, requestURL, requestBody)
		req = req.WithContext(ctx)
		//req.Cancel = cancellation
		//This should be a configuration item.  It allows the client to work
		//in a way similar to a server which does not support HTTP KeepAlive
		//After each request the client channel is closed.  When set to true
		//the performance overhead is large in terms of Network IO throughput

		req.Close = true

		if err != nil {
			result[core.ErrorUrn.String()] = err
			return result
		}

		req.Header = headers

		response, err := instance.client.Do(req)
		if err != nil {
			result[core.ErrorUrn.String()] = err

			//TODO: ONLY log the error if it is not a cancellation error.
			//This is the only condition so far when NOT to log the error!
			//logrus.Errorf("HTTP ERROR %v", err)
			return result
		}
		defer func() {
			err := response.Body.Close()
			if err != nil {
				logger.Log.Warnf("Error closing response Body %v", err)
			}
		}()

		requestBytes, _ := httputil.DumpRequest(req, true)
		responseBytes, _ := httputil.DumpResponse(response, true)

		if response.StatusCode >= 500 {
			result[core.ErrorUrn.String()] = fmt.Sprintf("Server Error %d", response.StatusCode)
		}

		result[RequestURLUrn.String()] = req.URL.String()
		result[core.BytesSentCountUrn.String()] = len(requestBytes)
		result[core.BytesReceivedCountUrn.String()] = len(responseBytes)
		//result[RequestHeadersUrn.String()] = req.Header

		for k, v := range response.Header {
			var key = RequestHeadersUrn.Name(k).String()
			result[key] = strings.Join(v, ",")
		}

		for k, v := range response.Header {
			var key = ResponseHeadersUrn.Name(k).String()
			result[key] = strings.Join(v, ",")
		}

		//TODO: We need a Response Headers key too
		result[ResponseStatusUrn.String()] = response.StatusCode

		result[core.BytesSentUrn.String()] = string(requestBytes)
		result[core.BytesReceivedUrn.String()] = string(responseBytes)

		return result
	}
}
<|endoftext|>


<commit_before>package dnsr

import (
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	lru "github.com/hashicorp/golang-lru"
	"github.com/miekg/dns"
)

//go:generate sh generate.sh

var (
	Root        *Resolver
	DebugLogger io.Writer
	Timeout     = 500 * time.Millisecond
)

func init() {
	Root = New(strings.Count(root, "\n"))
	for t := range dns.ParseZone(strings.NewReader(root), "", "") {
		if t.Error == nil {
			Root.saveDNSRR(t.RR)
		}
	}
}

// Resolver implements a primitive, non-recursive, caching DNS resolver.
type Resolver struct {
	cache  *lru.Cache
	client *dns.Client
}

// New initializes a Resolver with the specified cache size. Cache size defaults to 10,000 if size <= 0.
func New(size int) *Resolver {
	if size <= 0 {
		size = 10000
	}
	cache, _ := lru.New(size)
	r := &Resolver{
		client: &dns.Client{
			DialTimeout:  Timeout,
			ReadTimeout:  Timeout,
			WriteTimeout: Timeout,
		},
		cache: cache,
	}
	return r
}

// Resolve finds DNS records of type qtype for the domain qname. It returns a channel of *RR.
// The implementation guarantees that the output channel will close, so it is safe to range over.
// For nonexistent domains (where a DNS server will return NXDOMAIN), it will simply close the output channel.
// Specify an empty string in qtype to receive any DNS records found (currently A, AAAA, NS, CNAME, and TXT).
func (r *Resolver) Resolve(qname string, qtype string) <-chan *RR {
	return r.resolve(qname, qtype, 0)
}

func (r *Resolver) resolve(qname string, qtype string, depth int) <-chan *RR {
	c := make(chan *RR, 20)
	go func() {
		logResolveStart(qname, qtype, depth)
		defer logResolveEnd(qname, qtype, depth, time.Now())
		qname = toLowerFQDN(qname)
		defer close(c)
		if rrs := r.cacheGet(qname, qtype); rrs != nil {
			inject(c, rrs...)
			return
		}
		pname, ok := qname, true
		if qtype == "NS" {
			pname, ok = parent(qname)
			if !ok {
				return
			}
		}
	outer:
		for ; ok; pname, ok = parent(pname) {
			for nrr := range r.resolve(pname, "NS", depth+1) {
				if qtype != "" {
					if rrs := r.cacheGet(qname, qtype); rrs != nil {
						inject(c, rrs...)
						return
					}
				}
				if nrr.Type != "NS" {
					continue
				}
				for arr := range r.resolve(nrr.Value, "A", depth+1) {
					if arr.Type != "A" { // FIXME: support AAAA records?
						continue
					}
					addr := arr.Value + ":53"
					dtype, ok := dns.StringToType[qtype]
					if !ok {
						dtype = dns.TypeA
					}
					qmsg := &dns.Msg{}
					qmsg.SetQuestion(qname, dtype)
					qmsg.MsgHdr.RecursionDesired = false
					// fmt.Printf(";; dig +norecurse @%s %s %s\n", a.A.String(), qname, dns.TypeToString[qtype])
					start := time.Now()
					rmsg, _, err := r.client.Exchange(qmsg, addr)
					logExchange(qname, dns.TypeToString[dtype], depth, start, arr.Value, err)
					if err != nil {
						continue // FIXME: handle errors better from flaky/failing NS servers
					}
					r.saveDNSRR(rmsg.Answer...)
					r.saveDNSRR(rmsg.Ns...)
					r.saveDNSRR(rmsg.Extra...)
					if rmsg.Rcode == dns.RcodeNameError {
						r.cacheAdd(qname, nil) // FIXME: cache NXDOMAIN responses responsibly
						return
					}
					break outer
				}
			}
		}

		if rrs := r.cacheGet(qname, ""); rrs != nil {
			if !inject(c, rrs...) {
				return
			}

			for _, crr := range rrs {
				if crr.Type != "CNAME" {
					continue
				}
				if DebugLogger != nil {
					fmt.Fprintf(DebugLogger, "%s│    CNAME: %s\n", strings.Repeat("│   ", depth), crr.String())
				}
				for rr := range r.resolve(crr.Value, qtype, depth+1) {
					r.cacheAdd(qname, rr)
					if !inject(c, rr) {
						return
					}
				}
			}
		}
	}()
	return c
}

func logResolveStart(qname string, qtype string, depth int) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s┌─── resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth), qname, qtype, depth)
}

func logResolveEnd(qname string, qtype string, depth int, start time.Time) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s└─── %dms: resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth), dur/time.Millisecond, qname, qtype, depth)
}

func logExchange(qname string, qtype string, depth int, start time.Time, host string, err error) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s│    %dms: dig @%s %s %s\n",
		strings.Repeat("│   ", depth), dur/time.Millisecond, host, qname, qtype)
	if err != nil {
		fmt.Fprintf(DebugLogger, "%s│    %dms: ERROR: %s\n",
			strings.Repeat("│   ", depth), dur/time.Millisecond, err.Error())
	}
}

// RR represents a DNS resource record.
type RR struct {
	Name  string
	Type  string
	Value string
}

// String returns a string representation of an RR in zone-file format.
func (rr *RR) String() string {
	return rr.Name + "\t      3600\tIN\t" + rr.Type + "\t" + rr.Value
}

func convertRR(drr dns.RR) *RR {
	switch t := drr.(type) {
	case *dns.NS:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.Ns}
	case *dns.CNAME:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.Target}
	case *dns.A:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.A.String()}
	case *dns.AAAA:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.AAAA.String()}
	case *dns.TXT:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], strings.Join(t.Txt, "\t")}
	default:
		// fmt.Printf("%s\n", drr.String())
	}
	return nil
}

func inject(c chan<- *RR, rrs ...*RR) bool {
	for _, rr := range rrs {
		select {
		case c <- rr:
		default:
			// return false
		}
	}
	return true
}

func parent(name string) (string, bool) {
	labels := dns.SplitDomainName(name)
	if labels == nil {
		return "", false
	}
	return toLowerFQDN(strings.Join(labels[1:], ".")), true
}

func toLowerFQDN(name string) string {
	return dns.Fqdn(strings.ToLower(name))
}

type key struct {
	Name string
	Type string
}

type entry struct {
	m   sync.RWMutex
	rrs map[RR]struct{}
}

// saveDNSRR saves 1 or more DNS records to the resolver cache.
func (r *Resolver) saveDNSRR(drrs ...dns.RR) {
	for _, drr := range drrs {
		if rr := convertRR(drr); rr != nil {
			r.cacheAdd(rr.Name, rr)
		}
	}
}

// cacheAdd adds 0 or more DNS records to the resolver cache for a specific
// domain name and record type. This ensures the cache entry exists, even
// if empty, for NXDOMAIN responses.
func (r *Resolver) cacheAdd(qname string, rr *RR) {
	qname = toLowerFQDN(qname)
	e := r.getEntry(qname)
	if e == nil {
		e = &entry{rrs: make(map[RR]struct{}, 0)}
		e.m.Lock()
		r.cache.Add(qname, e)
	} else {
		e.m.Lock()
	}
	defer e.m.Unlock()
	if rr != nil {
		e.rrs[*rr] = struct{}{}
	}
}

// cacheGet returns a randomly ordered slice of DNS records.
func (r *Resolver) cacheGet(qname string, qtype string) []*RR {
	e := r.getEntry(qname)
	if e == nil && r != Root {
		e = Root.getEntry(qname)
	}
	if e == nil {
		return nil
	}
	e.m.RLock()
	defer e.m.RUnlock()
	if len(e.rrs) == 0 {
		return []*RR{}
	}
	rrs := make([]*RR, 0, len(e.rrs))
	for rr, _ := range e.rrs {
		// fmt.Printf("%s\n", rr.String())
		if qtype == "" || rr.Type == qtype {
			rrs = append(rrs, &RR{rr.Name, rr.Type, rr.Value})
		}
	}
	if len(rrs) == 0 && (qtype != "" && qtype != "NS") {
		return nil
	}
	return rrs
}

// getEntry returns a single cache entry or nil if an entry does not exist in the cache.
func (r *Resolver) getEntry(qname string) *entry {
	c, ok := r.cache.Get(qname)
	if !ok {
		return nil
	}
	e, ok := c.(*entry)
	if !ok {
		return nil
	}
	return e
}
<commit_msg>more logging<commit_after>package dnsr

import (
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	lru "github.com/hashicorp/golang-lru"
	"github.com/miekg/dns"
)

//go:generate sh generate.sh

var (
	Root        *Resolver
	DebugLogger io.Writer
	Timeout     = 500 * time.Millisecond
)

func init() {
	Root = New(strings.Count(root, "\n"))
	for t := range dns.ParseZone(strings.NewReader(root), "", "") {
		if t.Error == nil {
			Root.saveDNSRR(t.RR)
		}
	}
}

// Resolver implements a primitive, non-recursive, caching DNS resolver.
type Resolver struct {
	cache  *lru.Cache
	client *dns.Client
}

// New initializes a Resolver with the specified cache size. Cache size defaults to 10,000 if size <= 0.
func New(size int) *Resolver {
	if size <= 0 {
		size = 10000
	}
	cache, _ := lru.New(size)
	r := &Resolver{
		client: &dns.Client{
			DialTimeout:  Timeout,
			ReadTimeout:  Timeout,
			WriteTimeout: Timeout,
		},
		cache: cache,
	}
	return r
}

// Resolve finds DNS records of type qtype for the domain qname. It returns a channel of *RR.
// The implementation guarantees that the output channel will close, so it is safe to range over.
// For nonexistent domains (where a DNS server will return NXDOMAIN), it will simply close the output channel.
// Specify an empty string in qtype to receive any DNS records found (currently A, AAAA, NS, CNAME, and TXT).
func (r *Resolver) Resolve(qname string, qtype string) <-chan *RR {
	return r.resolve(qname, qtype, 0)
}

func (r *Resolver) resolve(qname string, qtype string, depth int) <-chan *RR {
	c := make(chan *RR, 20)
	go func() {
		logResolveStart(qname, qtype, depth)
		defer logResolveEnd(qname, qtype, depth, time.Now())
		qname = toLowerFQDN(qname)
		defer close(c)
		if rrs := r.cacheGet(qname, qtype); rrs != nil {
			inject(c, rrs...)
			return
		}
		pname, ok := qname, true
		if qtype == "NS" {
			pname, ok = parent(qname)
			if !ok {
				return
			}
		}
	outer:
		for ; ok; pname, ok = parent(pname) {
			for nrr := range r.resolve(pname, "NS", depth+1) {
				if qtype != "" {
					if rrs := r.cacheGet(qname, qtype); rrs != nil {
						inject(c, rrs...)
						return
					}
				}
				if nrr.Type != "NS" {
					continue
				}
				for arr := range r.resolve(nrr.Value, "A", depth+1) {
					if arr.Type != "A" { // FIXME: support AAAA records?
						continue
					}
					addr := arr.Value + ":53"
					dtype, ok := dns.StringToType[qtype]
					if !ok {
						dtype = dns.TypeA
					}
					qmsg := &dns.Msg{}
					qmsg.SetQuestion(qname, dtype)
					qmsg.MsgHdr.RecursionDesired = false
					// fmt.Printf(";; dig +norecurse @%s %s %s\n", a.A.String(), qname, dns.TypeToString[qtype])
					start := time.Now()
					rmsg, _, err := r.client.Exchange(qmsg, addr)
					logExchange(qname, dns.TypeToString[dtype], depth, start, arr.Value, err)
					if err != nil {
						continue // FIXME: handle errors better from flaky/failing NS servers
					}
					r.saveDNSRR(rmsg.Answer...)
					r.saveDNSRR(rmsg.Ns...)
					r.saveDNSRR(rmsg.Extra...)
					if rmsg.Rcode == dns.RcodeNameError {
						r.cacheAdd(qname, nil) // FIXME: cache NXDOMAIN responses responsibly
						return
					}
					break outer
				}
			}
		}

		if rrs := r.cacheGet(qname, ""); rrs != nil {
			if !inject(c, rrs...) {
				return
			}

			for _, crr := range rrs {
				if crr.Type != "CNAME" {
					continue
				}
				logCNAME(depth, crr.String())
				for rr := range r.resolve(crr.Value, qtype, depth+1) {
					r.cacheAdd(qname, rr)
					if !inject(c, rr) {
						return
					}
				}
			}
		}
	}()
	return c
}

// RR represents a DNS resource record.
type RR struct {
	Name  string
	Type  string
	Value string
}

// String returns a string representation of an RR in zone-file format.
func (rr *RR) String() string {
	return rr.Name + "\t      3600\tIN\t" + rr.Type + "\t" + rr.Value
}

func convertRR(drr dns.RR) *RR {
	switch t := drr.(type) {
	case *dns.NS:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.Ns}
	case *dns.CNAME:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.Target}
	case *dns.A:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.A.String()}
	case *dns.AAAA:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], t.AAAA.String()}
	case *dns.TXT:
		return &RR{t.Hdr.Name, dns.TypeToString[t.Hdr.Rrtype], strings.Join(t.Txt, "\t")}
	default:
		// fmt.Printf("%s\n", drr.String())
	}
	return nil
}

func inject(c chan<- *RR, rrs ...*RR) bool {
	for _, rr := range rrs {
		select {
		case c <- rr:
		default:
			// return false
		}
	}
	return true
}

func parent(name string) (string, bool) {
	labels := dns.SplitDomainName(name)
	if labels == nil {
		return "", false
	}
	return toLowerFQDN(strings.Join(labels[1:], ".")), true
}

func toLowerFQDN(name string) string {
	return dns.Fqdn(strings.ToLower(name))
}

type key struct {
	Name string
	Type string
}

type entry struct {
	m   sync.RWMutex
	rrs map[RR]struct{}
}

// saveDNSRR saves 1 or more DNS records to the resolver cache.
func (r *Resolver) saveDNSRR(drrs ...dns.RR) {
	for _, drr := range drrs {
		if rr := convertRR(drr); rr != nil {
			r.cacheAdd(rr.Name, rr)
		}
	}
}

// cacheAdd adds 0 or more DNS records to the resolver cache for a specific
// domain name and record type. This ensures the cache entry exists, even
// if empty, for NXDOMAIN responses.
func (r *Resolver) cacheAdd(qname string, rr *RR) {
	qname = toLowerFQDN(qname)
	e := r.getEntry(qname)
	if e == nil {
		e = &entry{rrs: make(map[RR]struct{}, 0)}
		e.m.Lock()
		r.cache.Add(qname, e)
	} else {
		e.m.Lock()
	}
	defer e.m.Unlock()
	if rr != nil {
		e.rrs[*rr] = struct{}{}
	}
}

// cacheGet returns a randomly ordered slice of DNS records.
func (r *Resolver) cacheGet(qname string, qtype string) []*RR {
	e := r.getEntry(qname)
	if e == nil && r != Root {
		e = Root.getEntry(qname)
	}
	if e == nil {
		return nil
	}
	e.m.RLock()
	defer e.m.RUnlock()
	if len(e.rrs) == 0 {
		return []*RR{}
	}
	rrs := make([]*RR, 0, len(e.rrs))
	for rr, _ := range e.rrs {
		// fmt.Printf("%s\n", rr.String())
		if qtype == "" || rr.Type == qtype {
			rrs = append(rrs, &RR{rr.Name, rr.Type, rr.Value})
		}
	}
	if len(rrs) == 0 && (qtype != "" && qtype != "NS") {
		return nil
	}
	return rrs
}

// getEntry returns a single cache entry or nil if an entry does not exist in the cache.
func (r *Resolver) getEntry(qname string) *entry {
	c, ok := r.cache.Get(qname)
	if !ok {
		return nil
	}
	e, ok := c.(*entry)
	if !ok {
		return nil
	}
	return e
}

// Logging utility functions

func logResolveStart(qname string, qtype string, depth int) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s┌─── resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth), qname, qtype, depth)
}

func logResolveEnd(qname string, qtype string, depth int, start time.Time) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s└─── %dms: resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth), dur/time.Millisecond, qname, qtype, depth)
}

func logExchange(qname string, qtype string, depth int, start time.Time, host string, err error) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s│    %dms: dig @%s %s %s\n",
		strings.Repeat("│   ", depth), dur/time.Millisecond, host, qname, qtype)
	if err != nil {
		fmt.Fprintf(DebugLogger, "%s│    %dms: ERROR: %s\n",
			strings.Repeat("│   ", depth), dur/time.Millisecond, err.Error())
	}
}

func logCNAME(depth int, cname string) {
	fmt.Fprintf(DebugLogger, "%s│    CNAME: %s\n", strings.Repeat("│   ", depth), cname)
}
<|endoftext|>


<commit_before>package goxplatform

import (
	log "github.com/Sirupsen/logrus"

	fs "github.com/dvonthenen/goxplatform/fs"
	inst "github.com/dvonthenen/goxplatform/inst"
	nw "github.com/dvonthenen/goxplatform/nw"
	run "github.com/dvonthenen/goxplatform/run"
	str "github.com/dvonthenen/goxplatform/str"
	sys "github.com/dvonthenen/goxplatform/sys"
)

func init() {
	log.SetLevel(log.InfoLevel)
	log.Infoln("Initializing goxplatform...")
}

//XPlatform is a static class that provides System related functions
type XPlatform struct {
	sys  *sys.Sys
	fs   *fs.Fs
	str  *str.Str
	nw   *nw.Nw
	run  *run.Run
	inst *inst.Inst
}

//New generates a Sys object
func New() *XPlatform {
	mySys := sys.NewSys()
	myFs := fs.NewFs()
	myStr := str.NewStr()
	myNw := nw.NewNw()
	myRun := run.NewRun()
	myInst := inst.NewInst()

	myXPlatform := &XPlatform{
		sys:  mySys,
		fs:   myFs,
		str:  myStr,
		nw:   myNw,
		run:  myRun,
		inst: myInst,
	}

	return myXPlatform
}
<commit_msg>Export variables<commit_after>package goxplatform

import (
	log "github.com/Sirupsen/logrus"

	fs "github.com/dvonthenen/goxplatform/fs"
	inst "github.com/dvonthenen/goxplatform/inst"
	nw "github.com/dvonthenen/goxplatform/nw"
	run "github.com/dvonthenen/goxplatform/run"
	str "github.com/dvonthenen/goxplatform/str"
	sys "github.com/dvonthenen/goxplatform/sys"
)

func init() {
	log.SetLevel(log.InfoLevel)
	log.Infoln("Initializing goxplatform...")
}

//XPlatform is a static class that provides System related functions
type XPlatform struct {
	Sys  *sys.Sys
	Fs   *fs.Fs
	Str  *str.Str
	Nw   *nw.Nw
	Run  *run.Run
	Inst *inst.Inst
}

//New generates a Sys object
func New() *XPlatform {
	mySys := sys.NewSys()
	myFs := fs.NewFs()
	myStr := str.NewStr()
	myNw := nw.NewNw()
	myRun := run.NewRun()
	myInst := inst.NewInst()

	myXPlatform := &XPlatform{
		Sys:  mySys,
		Fs:   myFs,
		Str:  myStr,
		Nw:   myNw,
		Run:  myRun,
		Inst: myInst,
	}

	return myXPlatform
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

var opts struct {
	StateDir        string `short:"s" long:"state-dir" default:"/var/mackerel-cache/check-log" value-name:"DIR" description:"Dir to keep state files under"`
	LogFile         string `short:"f" long:"log-file" value-name:"FILE" description:"Path to log file"`
	Pattern         string `short:"q" long:"pattern" required:"true" value-name:"PAT" description:"Pattern to search for"`
	Exclude         string `short:"E" long:"exclude" default:"(?!)" value-name:"PAT" description:"Pattern to exclude from matching"`
	Warn            int64  `short:"w" long:"warn" value-name:"N" description:"Warning level if pattern has a group"`
	Crit            int64  `short:"c" long:"crit" value-name:"N" description:"Critical level if pattern has a group"`
	OnlyWarn        bool   `short:"o" long:"warn-only" description:"Warn instead of critical on match"`
	CaseInsensitive bool   `short:"i" long:"icase" description:"Run a case insensitive match"`
	FilePattern     string `short:"F" long:"filepattern" value-name:"FILE" description:"Check a pattern of files, instead of one file"`
	ReturnContent   bool   `short:"r" long:"return" description:"Return matched line"`
}

func main() {
	ckr := run(os.Args[1:])
	ckr.Name = "LOG"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	if opts.LogFile == "" && opts.FilePattern == "" {
		return checkers.Unknown("No log file specified")
	}

	excludeReg, err := regexp.Compile(opts.Exclude)
	if err != nil {
		return checkers.Unknown("exclude pattern is invalid")
	}
	excludeReg = excludeReg

	fileList := []string{}
	if opts.LogFile != "" {
		fileList = append(fileList, opts.LogFile)
	}

	if opts.FilePattern != "" {
		dirStr := filepath.Dir(opts.FilePattern)
		filePat := filepath.Base(opts.FilePattern)
		if opts.CaseInsensitive {
			filePat = strings.ToLower(filePat)
		}
		reg, err := regexp.Compile(filePat)
		if err != nil {
			return checkers.Unknown("file-pattern is invalid")
		}

		fileInfos, err := ioutil.ReadDir(dirStr)
		if err != nil {
			return checkers.Unknown("cannot read the Directory:" + err.Error())
		}

		for _, fileInfo := range fileInfos {
			if fileInfo.IsDir() {
				continue
			}
			fname := fileInfo.Name()
			if opts.CaseInsensitive {
				fname = strings.ToLower(fname)
			}
			if reg.MatchString(fname) {
				fileList = append(fileList, dirStr+string(filepath.Separator)+fileInfo.Name())
			}
		}
	}

	warnNum := 0
	critNum := 0
	errorOverall := ""

	// for _, _ = range fileList {
	//}

	checkSt := checkers.OK
	if warnNum > 0 {
		checkSt = checkers.WARNING
	}
	if critNum > 0 {
		checkSt = checkers.CRITICAL
	}
	msg := fmt.Sprintf("%d warnings, %d criticals for pattern %s. %s", warnNum, critNum, opts.Pattern, errorOverall)
	return checkers.NewChecker(checkSt, msg)
}

var stateRe = regexp.MustCompile(`^([A-Z]):[/\\]`)

func getStateFile(stateDir, f string) string {
	return filepath.Join(stateDir, stateRe.ReplaceAllString(f, `$1`+string(filepath.Separator)))
}

func getBytesToSkip(f string) (int64, error) {
	_, err := os.Stat(f)
	if err != nil {
		return 0, nil
	}
	b, err := ioutil.ReadFile(f)
	if err != nil {
		return 0, err
	}
	i, err := strconv.Atoi(strings.Trim(string(b), " \r\n"))
	if err != nil {
		return 0, err
	}
	return int64(i), nil
}

func writeBytesToSkip(f string, num int64) error {
	err := os.MkdirAll(filepath.Dir(f), 0755)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(f, []byte(fmt.Sprintf("%d", num)), 0755)
}
<commit_msg>define searchLog<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

var opts struct {
	StateDir        string `short:"s" long:"state-dir" default:"/var/mackerel-cache/check-log" value-name:"DIR" description:"Dir to keep state files under"`
	LogFile         string `short:"f" long:"log-file" value-name:"FILE" description:"Path to log file"`
	Pattern         string `short:"q" long:"pattern" required:"true" value-name:"PAT" description:"Pattern to search for"`
	Exclude         string `short:"E" long:"exclude" default:"(?!)" value-name:"PAT" description:"Pattern to exclude from matching"`
	Warn            int64  `short:"w" long:"warn" value-name:"N" description:"Warning level if pattern has a group"`
	Crit            int64  `short:"c" long:"crit" value-name:"N" description:"Critical level if pattern has a group"`
	OnlyWarn        bool   `short:"o" long:"warn-only" description:"Warn instead of critical on match"`
	CaseInsensitive bool   `short:"i" long:"icase" description:"Run a case insensitive match"`
	FilePattern     string `short:"F" long:"filepattern" value-name:"FILE" description:"Check a pattern of files, instead of one file"`
	ReturnContent   bool   `short:"r" long:"return" description:"Return matched line"`
}

func main() {
	ckr := run(os.Args[1:])
	ckr.Name = "LOG"
	ckr.Exit()
}

func regCompileWithCase(ptn string, caseInsensitive bool) (*regexp.Regexp, error) {
	if caseInsensitive {
		ptn = strings.ToLower(ptn)
	}
	return regexp.Compile(ptn)
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	if opts.LogFile == "" && opts.FilePattern == "" {
		return checkers.Unknown("No log file specified")
	}

	patternReg, err := regCompileWithCase(opts.Pattern, opts.CaseInsensitive)
	if err != nil {
		return checkers.Unknown("pattern is invalid")
	}
	excludeReg, err := regCompileWithCase(opts.Exclude, opts.CaseInsensitive)
	if err != nil {
		return checkers.Unknown("exclude pattern is invalid")
	}

	fileList := []string{}
	if opts.LogFile != "" {
		fileList = append(fileList, opts.LogFile)
	}

	if opts.FilePattern != "" {
		dirStr := filepath.Dir(opts.FilePattern)
		filePat := filepath.Base(opts.FilePattern)
		reg, err := regCompileWithCase(filePat, opts.CaseInsensitive)
		if err != nil {
			return checkers.Unknown("file-pattern is invalid")
		}

		fileInfos, err := ioutil.ReadDir(dirStr)
		if err != nil {
			return checkers.Unknown("cannot read the Directory:" + err.Error())
		}

		for _, fileInfo := range fileInfos {
			if fileInfo.IsDir() {
				continue
			}
			fname := fileInfo.Name()
			if opts.CaseInsensitive {
				fname = strings.ToLower(fname)
			}
			if reg.MatchString(fname) {
				fileList = append(fileList, dirStr+string(filepath.Separator)+fileInfo.Name())
			}
		}
	}

	warnNum := int64(0)
	critNum := int64(0)
	errorOverall := ""

	for _, f := range fileList {
		w, c, errLines, err := searchLog(f, patternReg, excludeReg)
		if err != nil {
			return checkers.Unknown(err.Error())
		}
		warnNum += w
		critNum += c
		errorOverall += errLines
	}

	checkSt := checkers.OK
	if warnNum > 0 {
		checkSt = checkers.WARNING
	}
	if critNum > 0 {
		checkSt = checkers.CRITICAL
	}
	msg := fmt.Sprintf("%d warnings, %d criticals for pattern %s. %s", warnNum, critNum, opts.Pattern, errorOverall)
	return checkers.NewChecker(checkSt, msg)
}

func searchLog(logFile string, patternReg, excludeReg *regexp.Regexp) (int64, int64, string, error) {
	stateFile := getStateFile(opts.StateDir, logFile)
	skipBytes, err := getBytesToSkip(stateFile)
	if err != nil {
		return 0, 0, "", err
	}

	f, err := os.Open(logFile)
	if err != nil {
		return 0, 0, "", err
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		return 0, 0, "", err
	}

	if skipBytes > 0 && stat.Size() > skipBytes {
		f.Seek(skipBytes, 0)
	}
	warnNum := int64(0)
	critNum := int64(0)
	errLines := ""
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		checkLine := line
		if opts.CaseInsensitive {
			checkLine = strings.ToLower(checkLine)
		}
		if patternReg.MatchString(checkLine) && !excludeReg.MatchString(checkLine) {
			warnNum++
			critNum++
			errLines += "\n" + line
		}
	}
	// writeBytesToSkip(stateFile, int64(s.Pos().Offset))
	return warnNum, critNum, errLines, nil
}

var stateRe = regexp.MustCompile(`^([A-Z]):[/\\]`)

func getStateFile(stateDir, f string) string {
	return filepath.Join(stateDir, stateRe.ReplaceAllString(f, `$1`+string(filepath.Separator)))
}

func getBytesToSkip(f string) (int64, error) {
	_, err := os.Stat(f)
	if err != nil {
		return 0, nil
	}
	b, err := ioutil.ReadFile(f)
	if err != nil {
		return 0, err
	}
	i, err := strconv.Atoi(strings.Trim(string(b), " \r\n"))
	if err != nil {
		return 0, err
	}
	return int64(i), nil
}

func writeBytesToSkip(f string, num int64) error {
	err := os.MkdirAll(filepath.Dir(f), 0755)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(f, []byte(fmt.Sprintf("%d", num)), 0755)
}
<|endoftext|>


<commit_before>package gocui

// editWrite writes a rune in edit mode.
func (v *View) editWrite(ch rune) error {
	maxX, _ := v.Size()
	v.writeRune(v.cx, v.cy, ch)
	if v.cx == maxX-1 {
		if err := v.SetOrigin(v.ox+1, v.oy); err != nil {
			return err
		}
	} else {
		if err := v.SetCursor(v.cx+1, v.cy); err != nil {
			return err
		}
	}
	return nil
}

// editDelete deletes a rune in edit mode. back determines the direction.
func (v *View) editDelete(back bool) error {
	if back {
		v.deleteRune(v.cx-1, v.cy)
		if v.cx == 0 {
			if v.ox > 0 {
				if err := v.SetOrigin(v.ox-1, v.oy); err != nil {
					return err
				}
			}
		} else {
			if err := v.SetCursor(v.cx-1, v.cy); err != nil {
				return err
			}
		}
	} else {
		v.deleteRune(v.cx, v.cy)
	}
	return nil
}

// editLine inserts a new line under the cursor in edit mode.
func (v *View) editLine() error {
	_, maxY := v.Size()
	v.addLine(v.cy + 1)
	if v.cy == maxY-1 {
		if err := v.SetOrigin(0, v.oy+1); err != nil {
			return err
		}
		if err := v.SetCursor(0, v.cy); err != nil {
			return err
		}
	} else {
		if err := v.SetCursor(0, v.cy+1); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Add LICENSE header to edit.go<commit_after>// Copyright 2014 The gocui Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocui

// editWrite writes a rune in edit mode.
func (v *View) editWrite(ch rune) error {
	maxX, _ := v.Size()
	v.writeRune(v.cx, v.cy, ch)
	if v.cx == maxX-1 {
		if err := v.SetOrigin(v.ox+1, v.oy); err != nil {
			return err
		}
	} else {
		if err := v.SetCursor(v.cx+1, v.cy); err != nil {
			return err
		}
	}
	return nil
}

// editDelete deletes a rune in edit mode. back determines the direction.
func (v *View) editDelete(back bool) error {
	if back {
		v.deleteRune(v.cx-1, v.cy)
		if v.cx == 0 {
			if v.ox > 0 {
				if err := v.SetOrigin(v.ox-1, v.oy); err != nil {
					return err
				}
			}
		} else {
			if err := v.SetCursor(v.cx-1, v.cy); err != nil {
				return err
			}
		}
	} else {
		v.deleteRune(v.cx, v.cy)
	}
	return nil
}

// editLine inserts a new line under the cursor in edit mode.
func (v *View) editLine() error {
	_, maxY := v.Size()
	v.addLine(v.cy + 1)
	if v.cy == maxY-1 {
		if err := v.SetOrigin(0, v.oy+1); err != nil {
			return err
		}
		if err := v.SetCursor(0, v.cy); err != nil {
			return err
		}
	} else {
		if err := v.SetCursor(0, v.cy+1); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


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

package shadowsocks

import (
	"bytes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
)

// payloadSizeMask is the maximum size of payload in bytes.
const payloadSizeMask = 0x3FFF // 16*1024 - 1

// Writer is an io.Writer that also implements io.ReaderFrom to
// allow for piping the data without extra allocations and copies.
type Writer interface {
	io.Writer
	io.ReaderFrom
}

type shadowsocksWriter struct {
	writer   io.Writer
	ssCipher shadowaead.Cipher
	// Wrapper for input that arrives as a slice.
	input bytes.Reader
	// These are lazily initialized:
	buf  []byte
	aead cipher.AEAD
	// Index of the next encrypted chunk to write.
	counter []byte
}

// NewShadowsocksWriter creates a Writer that encrypts the given Writer using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksWriter(writer io.Writer, ssCipher shadowaead.Cipher) Writer {
	return &shadowsocksWriter{writer: writer, ssCipher: ssCipher}
}

// init generates a random salt, sets up the AEAD object and writes
// the salt to the inner Writer.
func (sw *shadowsocksWriter) init() (err error) {
	if sw.aead == nil {
		salt := make([]byte, sw.ssCipher.SaltSize())
		if _, err := io.ReadFull(rand.Reader, salt); err != nil {
			return fmt.Errorf("failed to generate salt: %v", err)
		}
		_, err := sw.writer.Write(salt)
		if err != nil {
			return fmt.Errorf("failed to write salt: %v", err)
		}
		sw.aead, err = sw.ssCipher.Encrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sw.counter = make([]byte, sw.aead.NonceSize())
		sw.buf = make([]byte, 2+sw.aead.Overhead()+payloadSizeMask+sw.aead.Overhead())
	}
	return nil
}

// WriteBlock encrypts and writes the input buffer as one signed block.
func (sw *shadowsocksWriter) encryptBlock(ciphertext []byte, plaintext []byte) ([]byte, error) {
	out := sw.aead.Seal(ciphertext, sw.counter, plaintext, nil)
	increment(sw.counter)
	return out, nil
}

func (sw *shadowsocksWriter) Write(p []byte) (int, error) {
	sw.input.Reset(p)
	n, err := sw.ReadFrom(&sw.input)
	return int(n), err
}

func (sw *shadowsocksWriter) ReadFrom(r io.Reader) (int64, error) {
	if err := sw.init(); err != nil {
		return 0, err
	}
	var written int64
	sizeBuf := sw.buf[:2+sw.aead.Overhead()]
	payloadBuf := sw.buf[len(sizeBuf):]
	for {
		plaintextSize, err := r.Read(payloadBuf[:payloadSizeMask])
		if plaintextSize > 0 {
			// big-endian payload size
			sizeBuf[0], sizeBuf[1] = byte(plaintextSize>>8), byte(plaintextSize)
			_, err = sw.encryptBlock(sizeBuf[:0], sizeBuf[:2])
			if err != nil {
				return written, fmt.Errorf("failed to encypt payload size: %v", err)
			}
			_, err := sw.encryptBlock(payloadBuf[:0], payloadBuf[:plaintextSize])
			if err != nil {
				return written, fmt.Errorf("failed to encrypt payload: %v", err)
			}
			payloadSize := plaintextSize + sw.aead.Overhead()
			_, err = sw.writer.Write(sw.buf[:len(sizeBuf)+payloadSize])
			written += int64(plaintextSize)
		}
		if err != nil {
			if err == io.EOF { // ignore EOF as per io.ReaderFrom contract
				return written, nil
			}
			return written, fmt.Errorf("Failed to read payload: %v", err)
		}
	}
}

type shadowsocksReader struct {
	reader   io.Reader
	ssCipher shadowaead.Cipher
	// These are lazily initialized:
	aead cipher.AEAD
	// Index of the next encrypted chunk to read.
	counter  []byte
	buf      []byte
	leftover []byte
}

// Reader is an io.Reader that also implements io.WriterTo to
// allow for piping the data without extra allocations and copies.
type Reader interface {
	io.Reader
	io.WriterTo
}

// NewShadowsocksReader creates a Reader that decrypts the given Reader using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksReader(reader io.Reader, ssCipher shadowaead.Cipher) Reader {
	return &shadowsocksReader{reader: reader, ssCipher: ssCipher}
}

// init reads the salt from the inner Reader and sets up the AEAD object
func (sr *shadowsocksReader) init() (err error) {
	if sr.aead == nil {
		// For chacha20-poly1305, SaltSize is 32, NonceSize is 12 and Overhead is 16.
		salt := make([]byte, sr.ssCipher.SaltSize())
		if _, err := io.ReadFull(sr.reader, salt); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				err = fmt.Errorf("failed to read salt: %v", err)
			}
			return err
		}
		sr.aead, err = sr.ssCipher.Decrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sr.counter = make([]byte, sr.aead.NonceSize())
		sr.buf = make([]byte, payloadSizeMask+sr.aead.Overhead())
	}
	return nil
}

// ReadBlock reads and decrypts a single signed block of ciphertext.
// The block will match the given decryptedBlockSize.
// The returned slice is only valid until the next Read call.
func (sr *shadowsocksReader) readBlock(decryptedBlockSize int) ([]byte, error) {
	if err := sr.init(); err != nil {
		return nil, err
	}
	cipherBlockSize := decryptedBlockSize + sr.aead.Overhead()
	if cipherBlockSize > cap(sr.buf) {
		return nil, io.ErrShortBuffer
	}
	buf := sr.buf[:cipherBlockSize]
	_, err := io.ReadFull(sr.reader, buf)
	if err != nil {
		return nil, err
	}
	buf, err = sr.aead.Open(buf[:0], sr.counter, buf, nil)
	increment(sr.counter)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt: %v", err)
	}
	return buf, nil
}

func (sr *shadowsocksReader) Read(b []byte) (int, error) {
	n, err := sr.readLoop(b)
	return int(n), err
}

func (sr *shadowsocksReader) WriteTo(w io.Writer) (written int64, err error) {
	n, err := sr.readLoop(w)
	if err == io.EOF {
		err = nil
	}
	return n, err
}

func (sr *shadowsocksReader) readLoop(w interface{}) (written int64, err error) {
	for {
		if len(sr.leftover) == 0 {
			buf, err := sr.readBlock(2)
			if err != nil {
				if err != io.EOF && err != io.ErrUnexpectedEOF {
					err = fmt.Errorf("failed to read payload size: %v", err)
				}
				return written, err
			}
			size := (int(buf[0])<<8 + int(buf[1])) & payloadSizeMask
			payload, err := sr.readBlock(size)
			if err != nil {
				return written, fmt.Errorf("failed to read payload: %v", err)
			}
			sr.leftover = payload
		}
		switch v := w.(type) {
		case io.Writer:
			n, err := v.Write(sr.leftover)
			written += int64(n)
			sr.leftover = sr.leftover[n:]
			if err != nil {
				return written, err
			}
		case []byte:
			n := copy(v, sr.leftover)
			sr.leftover = sr.leftover[n:]
			return int64(n), nil
		}
	}
}

// increment little-endian encoded unsigned integer b. Wrap around on overflow.
func increment(b []byte) {
	for i := range b {
		b[i]++
		if b[i] != 0 {
			return
		}
	}
}
<commit_msg>Rename to "byteWrapper"<commit_after>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shadowsocks

import (
	"bytes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
)

// payloadSizeMask is the maximum size of payload in bytes.
const payloadSizeMask = 0x3FFF // 16*1024 - 1

// Writer is an io.Writer that also implements io.ReaderFrom to
// allow for piping the data without extra allocations and copies.
type Writer interface {
	io.Writer
	io.ReaderFrom
}

type shadowsocksWriter struct {
	writer   io.Writer
	ssCipher shadowaead.Cipher
	// Wrapper for input that arrives as a slice.
	byteWrapper bytes.Reader
	// These are lazily initialized:
	buf  []byte
	aead cipher.AEAD
	// Index of the next encrypted chunk to write.
	counter []byte
}

// NewShadowsocksWriter creates a Writer that encrypts the given Writer using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksWriter(writer io.Writer, ssCipher shadowaead.Cipher) Writer {
	return &shadowsocksWriter{writer: writer, ssCipher: ssCipher}
}

// init generates a random salt, sets up the AEAD object and writes
// the salt to the inner Writer.
func (sw *shadowsocksWriter) init() (err error) {
	if sw.aead == nil {
		salt := make([]byte, sw.ssCipher.SaltSize())
		if _, err := io.ReadFull(rand.Reader, salt); err != nil {
			return fmt.Errorf("failed to generate salt: %v", err)
		}
		_, err := sw.writer.Write(salt)
		if err != nil {
			return fmt.Errorf("failed to write salt: %v", err)
		}
		sw.aead, err = sw.ssCipher.Encrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sw.counter = make([]byte, sw.aead.NonceSize())
		sw.buf = make([]byte, 2+sw.aead.Overhead()+payloadSizeMask+sw.aead.Overhead())
	}
	return nil
}

// WriteBlock encrypts and writes the input buffer as one signed block.
func (sw *shadowsocksWriter) encryptBlock(ciphertext []byte, plaintext []byte) ([]byte, error) {
	out := sw.aead.Seal(ciphertext, sw.counter, plaintext, nil)
	increment(sw.counter)
	return out, nil
}

func (sw *shadowsocksWriter) Write(p []byte) (int, error) {
	sw.byteWrapper.Reset(p)
	n, err := sw.ReadFrom(&sw.byteWrapper)
	return int(n), err
}

func (sw *shadowsocksWriter) ReadFrom(r io.Reader) (int64, error) {
	if err := sw.init(); err != nil {
		return 0, err
	}
	var written int64
	sizeBuf := sw.buf[:2+sw.aead.Overhead()]
	payloadBuf := sw.buf[len(sizeBuf):]
	for {
		plaintextSize, err := r.Read(payloadBuf[:payloadSizeMask])
		if plaintextSize > 0 {
			// big-endian payload size
			sizeBuf[0], sizeBuf[1] = byte(plaintextSize>>8), byte(plaintextSize)
			_, err = sw.encryptBlock(sizeBuf[:0], sizeBuf[:2])
			if err != nil {
				return written, fmt.Errorf("failed to encypt payload size: %v", err)
			}
			_, err := sw.encryptBlock(payloadBuf[:0], payloadBuf[:plaintextSize])
			if err != nil {
				return written, fmt.Errorf("failed to encrypt payload: %v", err)
			}
			payloadSize := plaintextSize + sw.aead.Overhead()
			_, err = sw.writer.Write(sw.buf[:len(sizeBuf)+payloadSize])
			written += int64(plaintextSize)
		}
		if err != nil {
			if err == io.EOF { // ignore EOF as per io.ReaderFrom contract
				return written, nil
			}
			return written, fmt.Errorf("Failed to read payload: %v", err)
		}
	}
}

type shadowsocksReader struct {
	reader   io.Reader
	ssCipher shadowaead.Cipher
	// These are lazily initialized:
	aead cipher.AEAD
	// Index of the next encrypted chunk to read.
	counter  []byte
	buf      []byte
	leftover []byte
}

// Reader is an io.Reader that also implements io.WriterTo to
// allow for piping the data without extra allocations and copies.
type Reader interface {
	io.Reader
	io.WriterTo
}

// NewShadowsocksReader creates a Reader that decrypts the given Reader using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksReader(reader io.Reader, ssCipher shadowaead.Cipher) Reader {
	return &shadowsocksReader{reader: reader, ssCipher: ssCipher}
}

// init reads the salt from the inner Reader and sets up the AEAD object
func (sr *shadowsocksReader) init() (err error) {
	if sr.aead == nil {
		// For chacha20-poly1305, SaltSize is 32, NonceSize is 12 and Overhead is 16.
		salt := make([]byte, sr.ssCipher.SaltSize())
		if _, err := io.ReadFull(sr.reader, salt); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				err = fmt.Errorf("failed to read salt: %v", err)
			}
			return err
		}
		sr.aead, err = sr.ssCipher.Decrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sr.counter = make([]byte, sr.aead.NonceSize())
		sr.buf = make([]byte, payloadSizeMask+sr.aead.Overhead())
	}
	return nil
}

// ReadBlock reads and decrypts a single signed block of ciphertext.
// The block will match the given decryptedBlockSize.
// The returned slice is only valid until the next Read call.
func (sr *shadowsocksReader) readBlock(decryptedBlockSize int) ([]byte, error) {
	if err := sr.init(); err != nil {
		return nil, err
	}
	cipherBlockSize := decryptedBlockSize + sr.aead.Overhead()
	if cipherBlockSize > cap(sr.buf) {
		return nil, io.ErrShortBuffer
	}
	buf := sr.buf[:cipherBlockSize]
	_, err := io.ReadFull(sr.reader, buf)
	if err != nil {
		return nil, err
	}
	buf, err = sr.aead.Open(buf[:0], sr.counter, buf, nil)
	increment(sr.counter)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt: %v", err)
	}
	return buf, nil
}

func (sr *shadowsocksReader) Read(b []byte) (int, error) {
	n, err := sr.readLoop(b)
	return int(n), err
}

func (sr *shadowsocksReader) WriteTo(w io.Writer) (written int64, err error) {
	n, err := sr.readLoop(w)
	if err == io.EOF {
		err = nil
	}
	return n, err
}

func (sr *shadowsocksReader) readLoop(w interface{}) (written int64, err error) {
	for {
		if len(sr.leftover) == 0 {
			buf, err := sr.readBlock(2)
			if err != nil {
				if err != io.EOF && err != io.ErrUnexpectedEOF {
					err = fmt.Errorf("failed to read payload size: %v", err)
				}
				return written, err
			}
			size := (int(buf[0])<<8 + int(buf[1])) & payloadSizeMask
			payload, err := sr.readBlock(size)
			if err != nil {
				return written, fmt.Errorf("failed to read payload: %v", err)
			}
			sr.leftover = payload
		}
		switch v := w.(type) {
		case io.Writer:
			n, err := v.Write(sr.leftover)
			written += int64(n)
			sr.leftover = sr.leftover[n:]
			if err != nil {
				return written, err
			}
		case []byte:
			n := copy(v, sr.leftover)
			sr.leftover = sr.leftover[n:]
			return int64(n), nil
		}
	}
}

// increment little-endian encoded unsigned integer b. Wrap around on overflow.
func increment(b []byte) {
	for i := range b {
		b[i]++
		if b[i] != 0 {
			return
		}
	}
}
<|endoftext|>


<commit_before>package siri

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/jbowtie/gokogiri"
	"github.com/jbowtie/gokogiri/xml"
)

type XMLNode interface {
	NativeNode() xml.Node
}

func NewXMLNode(nativeNode xml.Node) XMLNode {
	node := &RootXMLNode{rootNode: nativeNode}

	finalizer := func(node *RootXMLNode) {
		node.Free()
	}
	runtime.SetFinalizer(node, finalizer)

	return node
}

func NewXMLNodeFromContent(content []byte) (XMLNode, error) {
	document, err := gokogiri.ParseXml(content)
	if err != nil {
		return nil, err
	}
	return NewXMLNode(document.Root().XmlNode), nil
}

type RootXMLNode struct {
	rootNode xml.Node
}

func (node *RootXMLNode) NativeNode() xml.Node {
	return node.rootNode
}

func (node *RootXMLNode) Free() {
	if node.rootNode != nil {
		node.rootNode.MyDocument().Free()
		node.rootNode = nil
	}
}

type SubXMLNode struct {
	parent     XMLNode
	nativeNode xml.Node
}

func (node *SubXMLNode) NativeNode() xml.Node {
	return node.nativeNode
}

func NewSubXMLNode(nativeNode xml.Node) *SubXMLNode {
	return &SubXMLNode{nativeNode: nativeNode}
}

type XMLStructure struct {
	node XMLNode
}

type ResponseXMLStructure struct {
	XMLStructure

	address                   string
	producerRef               string
	requestMessageRef         string
	responseMessageIdentifier string
	responseTimestamp         time.Time

	status           bool
	errorType        string
	errorNumber      int
	errorText        string
	errorDescription string
}

type RequestXMLStructure struct {
	XMLStructure

	messageIdentifier string
	requestorRef      string
	requestTimestamp  time.Time
}

func (xmlStruct *XMLStructure) findNode(localName string) xml.Node {
	xpath := fmt.Sprintf(".//*[local-name()='%s']", localName)
	nodes, err := xmlStruct.node.NativeNode().Search(xpath)
	if err != nil {
		return nil
	}
	if len(nodes) == 0 {
		return nil
	}
	return nodes[0]
}

func (xmlStruct *XMLStructure) findNodes(localName string) []XMLNode {
	xpath := fmt.Sprintf(".//*[local-name()='%s']", localName)
	nodes, err := xmlStruct.node.NativeNode().Search(xpath)
	if err != nil {
		return nil
	}
	if len(nodes) == 0 {
		return nil
	}

	xmlNodes := make([]XMLNode, 0)
	for _, node := range nodes {
		subNode := NewSubXMLNode(node)
		subNode.parent = xmlStruct.node
		xmlNodes = append(xmlNodes, subNode)
	}

	return xmlNodes
}

// TODO: See how to handle errors
func (xmlStruct *XMLStructure) findStringChildContent(localName string) string {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return ""
	}
	return strings.TrimSpace(node.Content())
}

func (xmlStruct *XMLStructure) findTimeChildContent(localName string) time.Time {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return time.Time{}
	}
	t, err := time.Parse("2006-01-02T15:04:05Z07:00", strings.TrimSpace(node.Content()))
	// t, err := time.Parse(time.RFC3339, strings.TrimSpace(node.Content()))
	if err != nil {
		return time.Time{}
	}
	return t
}

func (xmlStruct *XMLStructure) findBoolChildContent(localName string) bool {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return false
	}
	s, err := strconv.ParseBool(strings.TrimSpace(node.Content()))
	if err != nil {
		return false
	}
	return s
}

func (xmlStruct *XMLStructure) findIntChildContent(localName string) int {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return 0
	}
	s, err := strconv.Atoi(strings.TrimSpace(node.Content()))
	if err != nil {
		return 0
	}
	return s
}

func (xmlStruct *XMLStructure) RawXML() string {
	return xmlStruct.node.NativeNode().String()
}

func (request *RequestXMLStructure) MessageIdentifier() string {
	if request.messageIdentifier == "" {
		request.messageIdentifier = request.findStringChildContent("MessageIdentifier")
	}
	return request.messageIdentifier
}

func (request *RequestXMLStructure) RequestorRef() string {
	if request.requestorRef == "" {
		request.requestorRef = request.findStringChildContent("RequestorRef")
	}
	return request.requestorRef
}

func (request *RequestXMLStructure) RequestTimestamp() time.Time {
	if request.requestTimestamp.IsZero() {
		request.requestTimestamp = request.findTimeChildContent("RequestTimestamp")
	}
	return request.requestTimestamp
}

func (response *ResponseXMLStructure) Address() string {
	if response.address == "" {
		response.address = response.findStringChildContent("Address")
	}
	return response.address
}

func (response *ResponseXMLStructure) ProducerRef() string {
	if response.producerRef == "" {
		response.producerRef = response.findStringChildContent("ProducerRef")
	}
	return response.producerRef
}

func (response *ResponseXMLStructure) RequestMessageRef() string {
	if response.requestMessageRef == "" {
		response.requestMessageRef = response.findStringChildContent("RequestMessageRef")
	}
	return response.requestMessageRef
}

func (response *ResponseXMLStructure) ResponseMessageIdentifier() string {
	if response.responseMessageIdentifier == "" {
		response.responseMessageIdentifier = response.findStringChildContent("ResponseMessageIdentifier")
	}
	return response.responseMessageIdentifier
}

func (response *ResponseXMLStructure) ResponseTimestamp() time.Time {
	if response.responseTimestamp.IsZero() {
		response.responseTimestamp = response.findTimeChildContent("ResponseTimestamp")
	}
	return response.responseTimestamp
}

func (response *ResponseXMLStructure) Status() bool {
	if !response.status {
		response.status = response.findBoolChildContent("Status")
	}
	return response.status
}

func (response *ResponseXMLStructure) ErrorType() string {
	if !response.Status() && response.errorType == "" {
		node := response.findNode("ErrorText")
		response.errorType = node.Parent().Name()

		// Find errorText and errorNumber to avoir too much parsing
		response.errorText = strings.TrimSpace(node.Content())
		if response.errorType == "OtherError" {
			n, err := strconv.Atoi(node.Parent().Attr("number"))
			if err != nil {
				return ""
			}
			response.errorNumber = n
		}
	}
	return response.errorType
}

func (response *ResponseXMLStructure) ErrorNumber() int {
	if !response.Status() && response.ErrorType() == "OtherError" && response.errorNumber == 0 {
		node := response.findNode("ErrorText")
		n, err := strconv.Atoi(node.Parent().Attr("number"))
		if err != nil {
			return -1
		}
		response.errorNumber = n
	}
	return response.errorNumber
}

func (response *ResponseXMLStructure) ErrorText() string {
	if !response.Status() && response.errorText == "" {
		response.errorText = response.findStringChildContent("ErrorText")
	}
	return response.errorText
}

func (response *ResponseXMLStructure) ErrorDescription() string {
	if !response.Status() && response.errorDescription == "" {
		response.errorDescription = response.findStringChildContent("Description")
	}
	return response.errorDescription
}
<commit_msg>Prevent error when node ErrorText isn't found. Fixes #2964<commit_after>package siri

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/jbowtie/gokogiri"
	"github.com/jbowtie/gokogiri/xml"
)

type XMLNode interface {
	NativeNode() xml.Node
}

func NewXMLNode(nativeNode xml.Node) XMLNode {
	node := &RootXMLNode{rootNode: nativeNode}

	finalizer := func(node *RootXMLNode) {
		node.Free()
	}
	runtime.SetFinalizer(node, finalizer)

	return node
}

func NewXMLNodeFromContent(content []byte) (XMLNode, error) {
	document, err := gokogiri.ParseXml(content)
	if err != nil {
		return nil, err
	}
	return NewXMLNode(document.Root().XmlNode), nil
}

type RootXMLNode struct {
	rootNode xml.Node
}

func (node *RootXMLNode) NativeNode() xml.Node {
	return node.rootNode
}

func (node *RootXMLNode) Free() {
	if node.rootNode != nil {
		node.rootNode.MyDocument().Free()
		node.rootNode = nil
	}
}

type SubXMLNode struct {
	parent     XMLNode
	nativeNode xml.Node
}

func (node *SubXMLNode) NativeNode() xml.Node {
	return node.nativeNode
}

func NewSubXMLNode(nativeNode xml.Node) *SubXMLNode {
	return &SubXMLNode{nativeNode: nativeNode}
}

type XMLStructure struct {
	node XMLNode
}

type ResponseXMLStructure struct {
	XMLStructure

	address                   string
	producerRef               string
	requestMessageRef         string
	responseMessageIdentifier string
	responseTimestamp         time.Time

	status           bool
	errorType        string
	errorNumber      int
	errorText        string
	errorDescription string
}

type RequestXMLStructure struct {
	XMLStructure

	messageIdentifier string
	requestorRef      string
	requestTimestamp  time.Time
}

func (xmlStruct *XMLStructure) findNode(localName string) xml.Node {
	xpath := fmt.Sprintf(".//*[local-name()='%s']", localName)
	nodes, err := xmlStruct.node.NativeNode().Search(xpath)
	if err != nil {
		return nil
	}
	if len(nodes) == 0 {
		return nil
	}
	return nodes[0]
}

func (xmlStruct *XMLStructure) findNodes(localName string) []XMLNode {
	xpath := fmt.Sprintf(".//*[local-name()='%s']", localName)
	nodes, err := xmlStruct.node.NativeNode().Search(xpath)
	if err != nil {
		return nil
	}
	if len(nodes) == 0 {
		return nil
	}

	xmlNodes := make([]XMLNode, 0)
	for _, node := range nodes {
		subNode := NewSubXMLNode(node)
		subNode.parent = xmlStruct.node
		xmlNodes = append(xmlNodes, subNode)
	}

	return xmlNodes
}

// TODO: See how to handle errors
func (xmlStruct *XMLStructure) findStringChildContent(localName string) string {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return ""
	}
	return strings.TrimSpace(node.Content())
}

func (xmlStruct *XMLStructure) findTimeChildContent(localName string) time.Time {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return time.Time{}
	}
	t, err := time.Parse("2006-01-02T15:04:05Z07:00", strings.TrimSpace(node.Content()))
	// t, err := time.Parse(time.RFC3339, strings.TrimSpace(node.Content()))
	if err != nil {
		return time.Time{}
	}
	return t
}

func (xmlStruct *XMLStructure) findBoolChildContent(localName string) bool {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return false
	}
	s, err := strconv.ParseBool(strings.TrimSpace(node.Content()))
	if err != nil {
		return false
	}
	return s
}

func (xmlStruct *XMLStructure) findIntChildContent(localName string) int {
	node := xmlStruct.findNode(localName)
	if node == nil {
		return 0
	}
	s, err := strconv.Atoi(strings.TrimSpace(node.Content()))
	if err != nil {
		return 0
	}
	return s
}

func (xmlStruct *XMLStructure) RawXML() string {
	return xmlStruct.node.NativeNode().String()
}

func (request *RequestXMLStructure) MessageIdentifier() string {
	if request.messageIdentifier == "" {
		request.messageIdentifier = request.findStringChildContent("MessageIdentifier")
	}
	return request.messageIdentifier
}

func (request *RequestXMLStructure) RequestorRef() string {
	if request.requestorRef == "" {
		request.requestorRef = request.findStringChildContent("RequestorRef")
	}
	return request.requestorRef
}

func (request *RequestXMLStructure) RequestTimestamp() time.Time {
	if request.requestTimestamp.IsZero() {
		request.requestTimestamp = request.findTimeChildContent("RequestTimestamp")
	}
	return request.requestTimestamp
}

func (response *ResponseXMLStructure) Address() string {
	if response.address == "" {
		response.address = response.findStringChildContent("Address")
	}
	return response.address
}

func (response *ResponseXMLStructure) ProducerRef() string {
	if response.producerRef == "" {
		response.producerRef = response.findStringChildContent("ProducerRef")
	}
	return response.producerRef
}

func (response *ResponseXMLStructure) RequestMessageRef() string {
	if response.requestMessageRef == "" {
		response.requestMessageRef = response.findStringChildContent("RequestMessageRef")
	}
	return response.requestMessageRef
}

func (response *ResponseXMLStructure) ResponseMessageIdentifier() string {
	if response.responseMessageIdentifier == "" {
		response.responseMessageIdentifier = response.findStringChildContent("ResponseMessageIdentifier")
	}
	return response.responseMessageIdentifier
}

func (response *ResponseXMLStructure) ResponseTimestamp() time.Time {
	if response.responseTimestamp.IsZero() {
		response.responseTimestamp = response.findTimeChildContent("ResponseTimestamp")
	}
	return response.responseTimestamp
}

func (response *ResponseXMLStructure) Status() bool {
	if !response.status {
		response.status = response.findBoolChildContent("Status")
	}
	return response.status
}

func (response *ResponseXMLStructure) ErrorType() string {
	if !response.Status() && response.errorType == "" {
		node := response.findNode("ErrorText")
		if node != nil {
			response.errorType = node.Parent().Name()

			// Find errorText and errorNumber to avoir too much parsing
			response.errorText = strings.TrimSpace(node.Content())
			if response.errorType == "OtherError" {
				n, err := strconv.Atoi(node.Parent().Attr("number"))
				if err != nil {
					return ""
				}
				response.errorNumber = n
			}
		}
	}
	return response.errorType
}

func (response *ResponseXMLStructure) ErrorNumber() int {
	if !response.Status() && response.ErrorType() == "OtherError" && response.errorNumber == 0 {
		node := response.findNode("ErrorText")
		n, err := strconv.Atoi(node.Parent().Attr("number"))
		if err != nil {
			return -1
		}
		response.errorNumber = n
	}
	return response.errorNumber
}

func (response *ResponseXMLStructure) ErrorText() string {
	if !response.Status() && response.errorText == "" {
		response.errorText = response.findStringChildContent("ErrorText")
	}
	return response.errorText
}

func (response *ResponseXMLStructure) ErrorDescription() string {
	if !response.Status() && response.errorDescription == "" {
		response.errorDescription = response.findStringChildContent("Description")
	}
	return response.errorDescription
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"testing"

	bserver "github.com/keybase/kbfs/bserver"
)

// Return a new initialized RootMetadata object for testing.
func newRootMetadataForTest(d *DirHandle, id DirID) *RootMetadata {
	rmd := NewRootMetadata(d, id)
	var keyGen KeyGen
	if id.IsPublic() {
		keyGen = PublicKeyGen
	} else {
		keyGen = 1
	}
	rmd.data.Dir = DirEntry{
		BlockPointer: BlockPointer{
			KeyGen:  keyGen,
			DataVer: 1,
		},
	}
	// make up the MD ID
	rmd.mdID = MdID{id[0]}
	return rmd
}

// MakeTestConfigOrBust creates and returns a config suitable for
// unit-testing with the given list of users.
func MakeTestConfigOrBust(t *testing.T, blockServerRemote bool, users ...string) *ConfigLocal {
	config := NewConfigLocal()

	localUsers := MakeLocalUsers(users)
	loggedInUser := localUsers[0]

	kbpki := NewKBPKILocal(loggedInUser.UID, localUsers)

	// TODO: Consider using fake BlockOps and MDOps instead.
	config.SetKBPKI(kbpki)

	signingKey := MakeLocalUserSigningKeyOrBust(loggedInUser.Name)
	cryptPrivateKey := MakeLocalUserCryptPrivateKeyOrBust(loggedInUser.Name)
	crypto := NewCryptoLocal(config.Codec(), signingKey, cryptPrivateKey)
	config.SetCrypto(crypto)

	if blockServerRemote {
		blockServer := NewBlockServerRemote(config, bserver.Config.BServerAddr)
		config.SetBlockServer(blockServer)
	} else {
		blockServer, err := NewBlockServerMemory(config)
		if err != nil {
			t.Fatal(err)
		}
		config.SetBlockServer(blockServer)
	}

	mdServer, err := NewMDServerMemory(config)
	if err != nil {
		t.Fatal(err)
	}
	config.SetMDServer(mdServer)

	keyOps, err := NewKeyServerMemory(config.Codec())
	if err != nil {
		t.Fatal(err)
	}
	config.SetKeyOps(keyOps)

	return config
}

// ConfigAsUser clones a test configuration, setting another user as
// the logged in user
func ConfigAsUser(config *ConfigLocal, loggedInUser string) *ConfigLocal {
	c := NewConfigLocal()

	pki := config.KBPKI().(*KBPKILocal)
	loggedInUID, ok := pki.Asserts[loggedInUser]
	if !ok {
		panic("bad test: unknown user: " + loggedInUser)
	}

	var localUsers []LocalUser
	for _, u := range pki.Users {
		localUsers = append(localUsers, u)
	}
	newPKI := NewKBPKILocal(loggedInUID, localUsers)
	c.SetKBPKI(newPKI)

	signingKey := MakeLocalUserSigningKeyOrBust(loggedInUser)
	cryptPrivateKey := MakeLocalUserCryptPrivateKeyOrBust(loggedInUser)
	crypto := NewCryptoLocal(config.Codec(), signingKey, cryptPrivateKey)
	c.SetCrypto(crypto)

	c.SetBlockServer(config.BlockServer())
	c.SetMDServer(config.MDServer())

	c.SetKeyOps(config.KeyOps())

	return c
}
<commit_msg>Fix compile<commit_after>package libkbfs

import (
	"testing"

	bserver "github.com/keybase/kbfs/bserver"
)

// Return a new initialized RootMetadata object for testing.
func newRootMetadataForTest(d *DirHandle, id DirID) *RootMetadata {
	rmd := NewRootMetadata(d, id)
	var keyGen KeyGen
	if id.IsPublic() {
		keyGen = PublicKeyGen
	} else {
		keyGen = 1
	}
	rmd.data.Dir = DirEntry{
		BlockInfo: BlockInfo{
			BlockPointer: BlockPointer{
				KeyGen:  keyGen,
				DataVer: 1,
			},
		},
	}
	// make up the MD ID
	rmd.mdID = MdID{id[0]}
	return rmd
}

// MakeTestConfigOrBust creates and returns a config suitable for
// unit-testing with the given list of users.
func MakeTestConfigOrBust(t *testing.T, blockServerRemote bool, users ...string) *ConfigLocal {
	config := NewConfigLocal()

	localUsers := MakeLocalUsers(users)
	loggedInUser := localUsers[0]

	kbpki := NewKBPKILocal(loggedInUser.UID, localUsers)

	// TODO: Consider using fake BlockOps and MDOps instead.
	config.SetKBPKI(kbpki)

	signingKey := MakeLocalUserSigningKeyOrBust(loggedInUser.Name)
	cryptPrivateKey := MakeLocalUserCryptPrivateKeyOrBust(loggedInUser.Name)
	crypto := NewCryptoLocal(config.Codec(), signingKey, cryptPrivateKey)
	config.SetCrypto(crypto)

	if blockServerRemote {
		blockServer := NewBlockServerRemote(config, bserver.Config.BServerAddr)
		config.SetBlockServer(blockServer)
	} else {
		blockServer, err := NewBlockServerMemory(config)
		if err != nil {
			t.Fatal(err)
		}
		config.SetBlockServer(blockServer)
	}

	mdServer, err := NewMDServerMemory(config)
	if err != nil {
		t.Fatal(err)
	}
	config.SetMDServer(mdServer)

	keyOps, err := NewKeyServerMemory(config.Codec())
	if err != nil {
		t.Fatal(err)
	}
	config.SetKeyOps(keyOps)

	return config
}

// ConfigAsUser clones a test configuration, setting another user as
// the logged in user
func ConfigAsUser(config *ConfigLocal, loggedInUser string) *ConfigLocal {
	c := NewConfigLocal()

	pki := config.KBPKI().(*KBPKILocal)
	loggedInUID, ok := pki.Asserts[loggedInUser]
	if !ok {
		panic("bad test: unknown user: " + loggedInUser)
	}

	var localUsers []LocalUser
	for _, u := range pki.Users {
		localUsers = append(localUsers, u)
	}
	newPKI := NewKBPKILocal(loggedInUID, localUsers)
	c.SetKBPKI(newPKI)

	signingKey := MakeLocalUserSigningKeyOrBust(loggedInUser)
	cryptPrivateKey := MakeLocalUserCryptPrivateKeyOrBust(loggedInUser)
	crypto := NewCryptoLocal(config.Codec(), signingKey, cryptPrivateKey)
	c.SetCrypto(crypto)

	c.SetBlockServer(config.BlockServer())
	c.SetMDServer(config.MDServer())

	c.SetKeyOps(config.KeyOps())

	return c
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"context"
	"encoding/json"
	"html/template"
	"net/http"
	"net/http/pprof"
	"os"
	"time"

	"github.com/elazarl/go-bindata-assetfs"
	"github.com/gorilla/websocket"
	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/patrickalin/bloomsky-client-go/assembly-assetfs"
)

type httpServer struct {
	bloomskyMessageToHTTP chan bloomsky.Bloomsky
	httpServ              *http.Server
	conn                  *websocket.Conn
	msgJSON               []byte
	templates             map[string]*template.Template
	store                 store
}

type meas struct {
	Timestamp time.Time
	Value     float64
}

type pageHome struct {
	Websockerurl string
}

type pageLog struct {
	LogTxt string
}

type pageHistory struct {
	Websockerurl string
	Store        template.JS
}

type logStru struct {
	Time  string `json:"time"`
	Msg   string `json:"msg"`
	Level string `json:"level"`
	Param string `json:"param"`
	Fct   string `json:"fct"`
}

//listen
func (httpServ *httpServer) listen(context context.Context) {
	go func() {
		for {
			mybloomsky := <-httpServ.bloomskyMessageToHTTP
			var err error

			httpServ.msgJSON, err = json.Marshal(mybloomsky.GetBloomskyStruct())
			checkErr(err, funcName(), "Marshal json Error", "")

			if httpServ.msgJSON == nil {
				logFatal(err, funcName(), "JSON Empty", "")
			}

			if httpServ.conn != nil {
				err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON)
				checkErr(err, funcName(), "Impossible to write to websocket", "")
			}

			logDebug(funcName(), "Listen", string(httpServ.msgJSON))
		}
	}()
}

// Websocket handler to send data
func (httpServ *httpServer) refreshdata(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh data Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	if err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON); err != nil {
		logFatal(err, funcName(), "Impossible to write to websocket", "")
	}
}

func getWs(r *http.Request) string {
	if r.TLS == nil {
		return "ws://"
	}
	return "wss://"
}

// Home bloomsky handler
func (httpServ *httpServer) home(w http.ResponseWriter, r *http.Request) {

	logDebug(funcName(), "Home Http handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshdata"}
	if err := httpServ.templates["home"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template home", "")
	}
}

// Home bloomsky handler
func (httpServ *httpServer) history(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home History handle", "")

	//fmt.Println(httpServ.store.String("temp"))

	var prim template.JS
	prim = "[[new Date(1416013200000), 22],[new Date(2014, 10, 15, 0, 30), 23],[new Date(2014, 10, 15, 0, 00), 22],[new Date(2014, 10, 14, 23, 30), 21],[new Date(2014, 10, 14, 23, 00), 22],[new Date(2014, 10, 14, 22, 30), 18],]"

	p := pageHistory{Websockerurl: getWs(r) + r.Host + "/refreshdata", Store: prim}
	if err := httpServ.templates["history"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template history", "")
	}
}

// Log handler
func (httpServ *httpServer) log(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Log Http handle", "")

	p := map[string]interface{}{"logRange": createArrayLog()}

	err := httpServ.templates["log"].Execute(w, p)
	checkErr(err, funcName(), "Compile template log", "")
}

func getFileServer(dev bool) http.FileSystem {
	if dev {
		return http.Dir("static")
	}
	return &assetfs.AssetFS{Asset: assemblyAssetfs.Asset, AssetDir: assemblyAssetfs.AssetDir, AssetInfo: assemblyAssetfs.AssetInfo, Prefix: "static"}
}

//createWebServer create web server
func createWebServer(in chan bloomsky.Bloomsky, HTTPPort string, HTTPSPort string, translate i18n.TranslateFunc, devel bool, store store) (*httpServer, error) {

	t := make(map[string]*template.Template)
	t["home"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/bloomsky/script.html", "tmpl/bloomsky/body.html", "tmpl/bloomsky/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["history"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/history/script.html", "tmpl/history/body.html", "tmpl/history/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["log"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/log/script.html", "tmpl/log/body.html", "tmpl/log/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)

	server := &httpServer{bloomskyMessageToHTTP: in,
		templates: t,
		store:     store}

	fs := http.FileServer(getFileServer(devel))

	s := http.NewServeMux()

	s.Handle("/static/", http.StripPrefix("/static/", fs))
	s.Handle("/favicon.ico", fs)
	s.HandleFunc("/", server.home)
	s.HandleFunc("/refreshdata", server.refreshdata)
	s.HandleFunc("/log", server.log)
	s.HandleFunc("/history", server.history)
	s.HandleFunc("/debug/pprof/", pprof.Index)
	s.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.HandleFunc("/debug/pprof/trace", pprof.Trace)

	h := &http.Server{Addr: HTTPPort, Handler: s}
	go func() {
		err := h.ListenAndServe()
		checkErr(err, funcName(), "Error when I create the server HTTP (don't forget ':')", "")
	}()

	hs := &http.Server{Addr: HTTPSPort, Handler: s}
	go func() {
		err := hs.ListenAndServeTLS("server.crt", "server.key")
		checkErr(err, funcName(), "Error when I create the server HTTPS (don't forget ':')", "")
	}()

	logInfo(funcName(), "Server HTTP listen on port", HTTPPort)
	logInfo(funcName(), "Server HTTPS listen on port", HTTPSPort)

	server.httpServ = h
	return server, nil
}

func createArrayLog() (logRange []logStru) {
	file, err := os.Open("bloomsky.log")
	checkErr(err, funcName(), "Imposible to open file", "bloomsky.log")

	defer file.Close()
	scanner := bufio.NewScanner(file)

	var tt logStru
	for scanner.Scan() {
		json.Unmarshal([]byte(scanner.Text()), &tt)
		checkErr(err, funcName(), "Impossible to unmarshall log", scanner.Text())

		logRange = append(logRange, tt)
	}

	scanner.Err()
	checkErr(err, funcName(), "Scanner Err", "")

	return logRange
}
<commit_msg>send data to javascript<commit_after>package main

import (
	"bufio"
	"context"
	"encoding/json"
	"html/template"
	"net/http"
	"net/http/pprof"
	"os"
	"time"

	"github.com/elazarl/go-bindata-assetfs"
	"github.com/gorilla/websocket"
	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/patrickalin/bloomsky-client-go/assembly-assetfs"
)

type httpServer struct {
	bloomskyMessageToHTTP chan bloomsky.Bloomsky
	httpServ              *http.Server
	conn                  *websocket.Conn
	msgJSON               []byte
	templates             map[string]*template.Template
	store                 store
}

type meas struct {
	Timestamp time.Time
	Value     float64
}

type pageHome struct {
	Websockerurl string
}

type pageLog struct {
	LogTxt string
}

type pageHistory struct {
	Websockerurl string
	Store        template.JS
}

type logStru struct {
	Time  string `json:"time"`
	Msg   string `json:"msg"`
	Level string `json:"level"`
	Param string `json:"param"`
	Fct   string `json:"fct"`
}

//listen
func (httpServ *httpServer) listen(context context.Context) {
	go func() {
		for {
			mybloomsky := <-httpServ.bloomskyMessageToHTTP
			var err error

			httpServ.msgJSON, err = json.Marshal(mybloomsky.GetBloomskyStruct())
			checkErr(err, funcName(), "Marshal json Error", "")

			if httpServ.msgJSON == nil {
				logFatal(err, funcName(), "JSON Empty", "")
			}

			if httpServ.conn != nil {
				err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON)
				checkErr(err, funcName(), "Impossible to write to websocket", "")
			}

			logDebug(funcName(), "Listen", string(httpServ.msgJSON))
		}
	}()
}

// Websocket handler to send data
func (httpServ *httpServer) refreshdata(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh data Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	if err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON); err != nil {
		logFatal(err, funcName(), "Impossible to write to websocket", "")
	}
}

func getWs(r *http.Request) string {
	if r.TLS == nil {
		return "ws://"
	}
	return "wss://"
}

// Home bloomsky handler
func (httpServ *httpServer) home(w http.ResponseWriter, r *http.Request) {

	logDebug(funcName(), "Home Http handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshdata"}
	if err := httpServ.templates["home"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template home", "")
	}
}

// Home bloomsky handler
func (httpServ *httpServer) history(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home History handle", "")

	p := pageHistory{Websockerurl: getWs(r) + r.Host + "/refreshdata", Store: template.JS(httpServ.store.String("temp"))}
	if err := httpServ.templates["history"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template history", "")
	}
}

// Log handler
func (httpServ *httpServer) log(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Log Http handle", "")

	p := map[string]interface{}{"logRange": createArrayLog()}

	err := httpServ.templates["log"].Execute(w, p)
	checkErr(err, funcName(), "Compile template log", "")
}

func getFileServer(dev bool) http.FileSystem {
	if dev {
		return http.Dir("static")
	}
	return &assetfs.AssetFS{Asset: assemblyAssetfs.Asset, AssetDir: assemblyAssetfs.AssetDir, AssetInfo: assemblyAssetfs.AssetInfo, Prefix: "static"}
}

//createWebServer create web server
func createWebServer(in chan bloomsky.Bloomsky, HTTPPort string, HTTPSPort string, translate i18n.TranslateFunc, devel bool, store store) (*httpServer, error) {

	t := make(map[string]*template.Template)
	t["home"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/bloomsky/script.html", "tmpl/bloomsky/body.html", "tmpl/bloomsky/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["history"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/history/script.html", "tmpl/history/body.html", "tmpl/history/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["log"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/log/script.html", "tmpl/log/body.html", "tmpl/log/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)

	server := &httpServer{bloomskyMessageToHTTP: in,
		templates: t,
		store:     store}

	fs := http.FileServer(getFileServer(devel))

	s := http.NewServeMux()

	s.Handle("/static/", http.StripPrefix("/static/", fs))
	s.Handle("/favicon.ico", fs)
	s.HandleFunc("/", server.home)
	s.HandleFunc("/refreshdata", server.refreshdata)
	s.HandleFunc("/log", server.log)
	s.HandleFunc("/history", server.history)
	s.HandleFunc("/debug/pprof/", pprof.Index)
	s.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.HandleFunc("/debug/pprof/trace", pprof.Trace)

	h := &http.Server{Addr: HTTPPort, Handler: s}
	go func() {
		err := h.ListenAndServe()
		checkErr(err, funcName(), "Error when I create the server HTTP (don't forget ':')", "")
	}()

	hs := &http.Server{Addr: HTTPSPort, Handler: s}
	go func() {
		err := hs.ListenAndServeTLS("server.crt", "server.key")
		checkErr(err, funcName(), "Error when I create the server HTTPS (don't forget ':')", "")
	}()

	logInfo(funcName(), "Server HTTP listen on port", HTTPPort)
	logInfo(funcName(), "Server HTTPS listen on port", HTTPSPort)

	server.httpServ = h
	return server, nil
}

func createArrayLog() (logRange []logStru) {
	file, err := os.Open("bloomsky.log")
	checkErr(err, funcName(), "Imposible to open file", "bloomsky.log")

	defer file.Close()
	scanner := bufio.NewScanner(file)

	var tt logStru
	for scanner.Scan() {
		json.Unmarshal([]byte(scanner.Text()), &tt)
		checkErr(err, funcName(), "Impossible to unmarshall log", scanner.Text())

		logRange = append(logRange, tt)
	}

	scanner.Err()
	checkErr(err, funcName(), "Scanner Err", "")

	return logRange
}
<|endoftext|>


<commit_before>package domain

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"sync"

	"github.com/toorop/govh"
)

// Client is an OVH API client
type Client struct {
	*govh.OVHClient
}

// New return a new Client
func New(client *govh.OVHClient) (*Client, error) {
	if client == nil {
		return nil, errors.New("client is nil")
	}
	return &Client{client}, nil
}

// List return a list of domains
func (c *Client) List(whoisOwner ...string) (domains []string, err error) {
	uri := "domain"
	if len(whoisOwner) != 0 {
		uri += "?whoisOwner=" + url.QueryEscape(strings.Join(whoisOwner, ""))
	}
	r, err := c.GET(uri)
	if err != nil {
		return
	}
	err = json.Unmarshal(r.Body, &domains)
	return
}

// GetRecordsOptions options for Client.GetRecordIDs
type GetRecordsOptions struct {
	FieldType string
	SubDomain string
}

// GetRecordIDs return record ID for the zone zone
func (c *Client) GetRecordIDs(zone string, options GetRecordsOptions) (IDs []int, err error) {
	uri := "domain/zone/" + url.QueryEscape(strings.ToLower(zone)) + "/record"
	v := url.Values{}
	if options.FieldType != "" {
		options.FieldType = strings.ToUpper(options.FieldType)
		if !IsValidFieldType(options.FieldType) {
			return IDs, fmt.Errorf("%s is not a valid type", options.FieldType)
		}
		v.Add("fieldType", options.FieldType)
	}
	if options.SubDomain != "" {
		v.Add("subDomain", strings.ToLower(options.SubDomain))
	}
	params := v.Encode()
	if params != "" {
		uri += "?" + params
	}
	r, err := c.GET(uri)
	if err != nil {
		return
	}
	if string(r.Body) != "" {
		err = json.Unmarshal(r.Body, &IDs)
	}
	return
}

// GetRecordByID return a ZoneRecord by its ID
func (c *Client) GetRecordByID(zone string, ID int) (record ZoneRecord, err error) {
	record = ZoneRecord{}
	r, err := c.GET("domain/zone/" + url.QueryEscape(strings.ToLower(zone)) + "/record/" + fmt.Sprintf("%d", ID))
	if err != nil {
		return
	}
	err = json.Unmarshal(r.Body, &record)
	return
}

// GetRecords returns record(s) for zone filtered by filedType
func (c *Client) GetRecords(zone string, options GetRecordsOptions) (records []ZoneRecord, err error) {
	IDs, err := c.GetRecordIDs(zone, options)
	if err != nil {
		return
	}
	var wg sync.WaitGroup
	errChan := make(chan error, 1)
	doneChan := make(chan int)

	for _, ID := range IDs {
		//log.Println("range", ID)
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			record, err := c.GetRecordByID(zone, id)
			if err != nil {
				errChan <- err
				return
			}
			records = append(records, record)
		}(ID)
	}

	go func() {
		wg.Wait()
		doneChan <- 1
	}()

	select {
	case err = <-errChan:
		return []ZoneRecord{}, err

	case <-doneChan:
		break
	}
	return
}
<commit_msg>zone newrecord && delrecord<commit_after>package domain

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"sync"

	"github.com/toorop/govh"
)

// Client is an OVH API client
type Client struct {
	*govh.OVHClient
}

// New return a new Client
func New(client *govh.OVHClient) (*Client, error) {
	if client == nil {
		return nil, errors.New("client is nil")
	}
	return &Client{client}, nil
}

// List return a list of domains
func (c *Client) List(whoisOwner ...string) (domains []string, err error) {
	uri := "domain"
	if len(whoisOwner) != 0 {
		uri += "?whoisOwner=" + url.QueryEscape(strings.Join(whoisOwner, ""))
	}
	r, err := c.GET(uri)
	if err != nil {
		return
	}
	err = json.Unmarshal(r.Body, &domains)
	return
}

// GetRecordsOptions options for Client.GetRecordIDs
type GetRecordsOptions struct {
	FieldType string
	SubDomain string
}

// GetRecordIDs return record ID for the zone zone
func (c *Client) GetRecordIDs(zone string, options GetRecordsOptions) (IDs []int, err error) {
	uri := "domain/zone/" + url.QueryEscape(strings.ToLower(zone)) + "/record"
	v := url.Values{}
	if options.FieldType != "" {
		options.FieldType = strings.ToUpper(options.FieldType)
		if !IsValidFieldType(options.FieldType) {
			return IDs, fmt.Errorf("%s is not a valid type", options.FieldType)
		}
		v.Add("fieldType", options.FieldType)
	}
	if options.SubDomain != "" {
		v.Add("subDomain", strings.ToLower(options.SubDomain))
	}
	params := v.Encode()
	if params != "" {
		uri += "?" + params
	}
	r, err := c.GET(uri)
	if err != nil {
		return
	}
	if string(r.Body) != "" {
		err = json.Unmarshal(r.Body, &IDs)
	}
	return
}

// GetRecordByID return a ZoneRecord by its ID
func (c *Client) GetRecordByID(zone string, ID int) (record ZoneRecord, err error) {
	record = ZoneRecord{}
	r, err := c.GET("domain/zone/" + url.QueryEscape(strings.ToLower(zone)) + "/record/" + fmt.Sprintf("%d", ID))
	if err != nil {
		return
	}
	err = json.Unmarshal(r.Body, &record)
	return
}

// GetRecords returns record(s) for zone filtered by filedType
func (c *Client) GetRecords(zone string, options GetRecordsOptions) (records []ZoneRecord, err error) {
	IDs, err := c.GetRecordIDs(zone, options)
	if err != nil {
		return
	}
	var wg sync.WaitGroup
	errChan := make(chan error, 1)
	doneChan := make(chan int)

	for _, ID := range IDs {
		//log.Println("range", ID)
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			record, err := c.GetRecordByID(zone, id)
			if err != nil {
				errChan <- err
				return
			}
			records = append(records, record)
		}(ID)
	}

	go func() {
		wg.Wait()
		doneChan <- 1
	}()

	select {
	case err = <-errChan:
		return []ZoneRecord{}, err

	case <-doneChan:
		break
	}
	return
}

// NewRecord creates a new record for zone
func (c *Client) NewRecord(zr ZoneRecord) (record ZoneRecord, err error) {
	payloadRaw := struct {
		TTL       int    `json:"ttl"`
		Target    string `json:"target"`
		FieldType string `json:"fieldType"`
		SubDomain string `json:"subDomain"`
	}{
		TTL:       zr.TTL,
		Target:    zr.Target,
		FieldType: zr.FieldType,
		SubDomain: zr.SubDomain,
	}

	payload, err := json.Marshal(payloadRaw)
	if err != nil {
		return
	}

	r, err := c.POST("domain/zone/"+url.QueryEscape(zr.Zone)+"/record", string(payload))
	if err != nil {
		return
	}

	err = json.Unmarshal(r.Body, &record)
	return

}

// DeleteRecord deletes a record
func (c *Client) DeleteRecord(zone string, ID int) error {
	_, err := c.DELETE("domain/zone/" + url.QueryEscape(zone) + "/record/" + fmt.Sprintf("%d", ID))
	return err
}

// ActivateZone activate zone zone
func (c *Client) ActivateZone(zone string) error {
	return nil
}
<|endoftext|>