<commit_before>package main

import (
	"errors"
	"github.com/briandowns/spinner"
	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"net"
	"regexp"
	"strings"
	"time"
)

type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() map[string]string
	ChangeNode(node string)
	Ping() (string, error)
}

var providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3)}

func validateProvider(p string) (string, error) {
	match, _ := regexp.MatchString("(telia|level3)", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	} else {
		return "", errors.New("provider not support")
	}
}

func main() {
	var (
		err    error
		cPName string = "local"
	)

	rep := make(chan string, 1)
	cmd := make(chan string, 1)
	nxt := make(chan struct{}, 1)

	c := cli.Init("local")
	go c.Run(cmd, nxt)

	r, _ := regexp.Compile(`(ping|connect|node|local) (.*)`)
	s := spinner.New(spinner.CharSets[26], 220*time.Millisecond)

	for {
		select {
		case req := <-cmd:
			subReq := r.FindStringSubmatch(req)
			if len(subReq) == 0 {
				println("syntax error")
				nxt <- struct{}{}
				continue
			}
			cmd := strings.TrimSpace(subReq[1])
			args := strings.TrimSpace(subReq[2])
			switch {
			case cmd == "ping" && cPName == "local":
				p := icmp.NewPing()
				ra, err := net.ResolveIPAddr("ip", args)
				if err != nil {
					println("cannot resolve", args, ": Unknown host")
					nxt <- struct{}{}
					continue
				}
				p.IP(ra.String())
				for n := 0; n < 4; n++ {
					p.Ping(rep)
					println(<-rep)
				}
				nxt <- struct{}{}
			case cmd == "ping":
				s.Prefix = "please wait "
				s.Start()
				providers[cPName].Set(args, "ipv4")
				m, _ := providers[cPName].Ping()
				s.Stop()
				println(m)
				nxt <- struct{}{}
			case cmd == "node":
				providers[cPName].ChangeNode(args)
				c.SetPrompt(cPName + "/" + args)
				nxt <- struct{}{}
			case cmd == "local":
				cPName = "local"
				c.SetPrompt(cPName)
				nxt <- struct{}{}
			case cmd == "connect":
				var pName string
				if pName, err = validateProvider(args); err != nil {
					println("provider not available")
					nxt <- struct{}{}
					continue
				}
				cPName = pName
				c.SetPrompt(cPName + "/" + providers[cPName].GetDefaultNode())
				go func() {
					c.UpdateCompleter("node", providers[cPName].GetNodes())
				}()
				nxt <- struct{}{}
			}
		}
	}
}
<commit_msg>added comment<commit_after>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"github.com/briandowns/spinner"
	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"net"
	"regexp"
	"strings"
	"time"
)

type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() map[string]string
	ChangeNode(node string)
	Ping() (string, error)
}

var providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3)}

func validateProvider(p string) (string, error) {
	match, _ := regexp.MatchString("(telia|level3)", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	} else {
		return "", errors.New("provider not support")
	}
}

func main() {
	var (
		err    error
		cPName string = "local"
	)

	rep := make(chan string, 1)
	cmd := make(chan string, 1)
	nxt := make(chan struct{}, 1)

	c := cli.Init("local")
	go c.Run(cmd, nxt)

	r, _ := regexp.Compile(`(ping|connect|node|local) (.*)`)
	s := spinner.New(spinner.CharSets[26], 220*time.Millisecond)

	for {
		select {
		case req := <-cmd:
			subReq := r.FindStringSubmatch(req)
			if len(subReq) == 0 {
				println("syntax error")
				nxt <- struct{}{}
				continue
			}
			cmd := strings.TrimSpace(subReq[1])
			args := strings.TrimSpace(subReq[2])
			switch {
			case cmd == "ping" && cPName == "local":
				p := icmp.NewPing()
				ra, err := net.ResolveIPAddr("ip", args)
				if err != nil {
					println("cannot resolve", args, ": Unknown host")
					nxt <- struct{}{}
					continue
				}
				p.IP(ra.String())
				for n := 0; n < 4; n++ {
					p.Ping(rep)
					println(<-rep)
				}
				nxt <- struct{}{}
			case cmd == "ping":
				s.Prefix = "please wait "
				s.Start()
				providers[cPName].Set(args, "ipv4")
				m, _ := providers[cPName].Ping()
				s.Stop()
				println(m)
				nxt <- struct{}{}
			case cmd == "node":
				providers[cPName].ChangeNode(args)
				c.SetPrompt(cPName + "/" + args)
				nxt <- struct{}{}
			case cmd == "local":
				cPName = "local"
				c.SetPrompt(cPName)
				nxt <- struct{}{}
			case cmd == "connect":
				var pName string
				if pName, err = validateProvider(args); err != nil {
					println("provider not available")
					nxt <- struct{}{}
					continue
				}
				cPName = pName
				c.SetPrompt(cPName + "/" + providers[cPName].GetDefaultNode())
				go func() {
					c.UpdateCompleter("node", providers[cPName].GetNodes())
				}()
				nxt <- struct{}{}
			}
		}
	}
}
<|endoftext|>


<commit_before>package artnet

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/jsimonetti/go-artnet/packet"
	"github.com/jsimonetti/go-artnet/packet/code"
)

// Node is the information known about a node
type Node struct {
	// Config holds the configuration of this node
	Config NodeConfig

	// conn is the UDP connection this node will listen on
	conn   net.PacketConn
	bconn  net.Conn
	sendCh chan netPayload
	recvCh chan netPayload

	// shutdownCh will be closed on shutdown of the node
	shutdownCh   chan struct{}
	shutdown     bool
	shutdownErr  error
	shutdownLock sync.Mutex

	// pollCh will receive ArtPoll packets
	pollCh chan packet.ArtPollPacket
	// pollCh will send ArtPollReply packets
	pollReplyCh chan packet.ArtPollReplyPacket

	log Logger
}

// netPayload contains bytes read from the network and/or an error
type netPayload struct {
	address net.UDPAddr
	err     error
	data    []byte
}

// NewNode return a Node
func NewNode(name string, style code.StyleCode, ip net.IP, log Logger) *Node {
	n := &Node{
		Config: NodeConfig{
			Name: name,
			Type: style,
		},
		conn:     nil,
		shutdown: true,
		log:      log.With(Fields{"type": "Node"}),
	}
	if len(ip) > 0 {
		n.Config.IP = ip
	}
	//n.Config.IP = GenerateIP()
	return n
}

// Stop will stop all running routines and close the network connection
func (n *Node) Stop() {
	n.shutdownLock.Lock()
	n.shutdown = true
	n.shutdownLock.Unlock()
	close(n.shutdownCh)
	if n.conn != nil {
		n.conn.Close()
	}
	if n.bconn != nil {
		n.bconn.Close()
	}
}

// Start will start the controller
func (n *Node) Start() error {
	n.log.With(Fields{"ip": n.Config.IP.String(), "type": n.Config.Type.String()}).Debug("node started")

	n.sendCh = make(chan netPayload, 10)
	n.recvCh = make(chan netPayload, 10)
	n.pollCh = make(chan packet.ArtPollPacket, 10)
	n.pollReplyCh = make(chan packet.ArtPollReplyPacket, 10)
	n.shutdownCh = make(chan struct{})
	n.shutdown = false

	var err error

	n.bconn, err = net.Dial("udp4", "2.255.255.255:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	n.conn, err = net.ListenPacket("udp4", "0.0.0.0:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	go n.recvLoop()
	go n.sendLoop()

	return nil
}

// pollReplyLoop loops to reply to ArtPoll packets
// when a controller asks for continuous updates, we do that using a ticker
func (n *Node) pollReplyLoop() {
	var timer time.Ticker

	// loop untill shutdown
	for {
		select {
		case <-timer.C:
			// if we should regularly send replies (can be requested by the controller)
			// we send it here

		case poll := <-n.pollCh:
			// reply with pollReply
			n.log.With(Fields{"poll": poll}).Debugf("poll received, now send a reply")

			// if we are asked to send changes regularyl, set the Ticker here

		case <-n.shutdownCh:
			return
		}
	}
}

// sendLoop is used to send packets to the network
func (n *Node) sendLoop() {
	// loop untill shutdown
	for {
		select {
		case payload := <-n.sendCh:
			n.shutdownLock.Lock()
			if !n.shutdown {
				var num int
				var err error
				if payload.address.IP.Equal(broadcastAddr.IP) {
					num, err = n.bconn.Write(payload.data)
				} else {
					num, err = n.conn.WriteTo(payload.data, &payload.address)
				}
				if err != nil {
					n.log.With(Fields{"error": err}).Debugf("error writing packet")
					continue
				}
				n.log.With(Fields{"dst": payload.address.String(), "bytes": num}).Debugf("packet sent")
			}
			n.shutdownLock.Unlock()
		case <-n.shutdownCh:
			return
		}
	}
}

// AddrToUDPAddr will turn a net.Addr into a net.UDPAddr
func AddrToUDPAddr(addr net.Addr) net.UDPAddr {
	udp := addr.(*net.UDPAddr)
	return *udp
}

// recvLoop is used to receive packets from the network
// it starts a goroutine for dumping the msgs onto a channel,
// the payload from that channel is then fed into a handler
// due to the nature of broadcasting, we see our own sent
// packets to, but we ignore them
func (n *Node) recvLoop() {
	// start a routine that will read data from n.conn
	// and (if not shutdown), send to the recvCh
	go func() {
		b := make([]byte, 4096)
		for {
			num, src, err := n.conn.ReadFrom(b)
			n.shutdownLock.Lock()
			if !n.shutdown {
				n.shutdownLock.Unlock()
				from := AddrToUDPAddr(src)
				if n.Config.IP.Equal(from.IP) {
					// this was sent by me, so we ignore it
					//n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("ignoring received packet from self")
					continue
				}

				n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("received packet")
				if err != nil && err != io.EOF {
					n.recvCh <- netPayload{
						address: from,
						data:    b[:num],
						err:     err,
					}
					continue
				}
				n.recvCh <- netPayload{
					address: from,
					data:    b[:num],
					err:     err,
				}
				continue
			}
			n.shutdownLock.Unlock()
			return
		}
	}()

	// loop untill shutdown
	for {
		select {
		case payload := <-n.recvCh:
			p, err := packet.Unmarshal(payload.data)
			if err != nil {
				n.log.Printf("failed to parse packet: %v", err)
				continue
			}
			go n.handlePacket(p)

		case <-n.shutdownCh:
			return
		}
	}
}

// handlePacket contains the logic for dealing with incoming packets
func (n *Node) handlePacket(p packet.ArtNetPacket) {
	switch p := p.(type) {
	case *packet.ArtPollReplyPacket:
		// only handle these packets if we are a controller
		if n.Config.Type == code.StController {
			n.pollReplyCh <- *p
		}

	default:
		n.log.With(Fields{"packet": p}).Debugf("unknown packet type")
	}

}
<commit_msg>Only listen for UDP packets on the node IP<commit_after>package artnet

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/jsimonetti/go-artnet/packet"
	"github.com/jsimonetti/go-artnet/packet/code"
)

// Node is the information known about a node
type Node struct {
	// Config holds the configuration of this node
	Config NodeConfig

	// conn is the UDP connection this node will listen on
	conn   net.PacketConn
	bconn  net.Conn
	sendCh chan netPayload
	recvCh chan netPayload

	// shutdownCh will be closed on shutdown of the node
	shutdownCh   chan struct{}
	shutdown     bool
	shutdownErr  error
	shutdownLock sync.Mutex

	// pollCh will receive ArtPoll packets
	pollCh chan packet.ArtPollPacket
	// pollCh will send ArtPollReply packets
	pollReplyCh chan packet.ArtPollReplyPacket

	log Logger
}

// netPayload contains bytes read from the network and/or an error
type netPayload struct {
	address net.UDPAddr
	err     error
	data    []byte
}

// NewNode return a Node
func NewNode(name string, style code.StyleCode, ip net.IP, log Logger) *Node {
	n := &Node{
		Config: NodeConfig{
			Name: name,
			Type: style,
		},
		conn:     nil,
		shutdown: true,
		log:      log.With(Fields{"type": "Node"}),
	}
	if len(ip) > 0 {
		n.Config.IP = ip
	}
	//n.Config.IP = GenerateIP()
	return n
}

// Stop will stop all running routines and close the network connection
func (n *Node) Stop() {
	n.shutdownLock.Lock()
	n.shutdown = true
	n.shutdownLock.Unlock()
	close(n.shutdownCh)
	if n.conn != nil {
		n.conn.Close()
	}
	if n.bconn != nil {
		n.bconn.Close()
	}
}

// Start will start the controller
func (n *Node) Start() error {
	n.log.With(Fields{"ip": n.Config.IP.String(), "type": n.Config.Type.String()}).Debug("node started")

	n.sendCh = make(chan netPayload, 10)
	n.recvCh = make(chan netPayload, 10)
	n.pollCh = make(chan packet.ArtPollPacket, 10)
	n.pollReplyCh = make(chan packet.ArtPollReplyPacket, 10)
	n.shutdownCh = make(chan struct{})
	n.shutdown = false

	var err error

	n.bconn, err = net.Dial("udp4", "2.255.255.255:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	rcvListenAddress := "0.0.0.0:6454"
	if len(n.Config.IP) > 0 {
		rcvListenAddress = fmt.Sprintf("%v:6454", n.Config.IP.String())
	}

	n.conn, err = net.ListenPacket("udp4", rcvListenAddress)
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	go n.recvLoop()
	go n.sendLoop()

	return nil
}

// pollReplyLoop loops to reply to ArtPoll packets
// when a controller asks for continuous updates, we do that using a ticker
func (n *Node) pollReplyLoop() {
	var timer time.Ticker

	// loop untill shutdown
	for {
		select {
		case <-timer.C:
			// if we should regularly send replies (can be requested by the controller)
			// we send it here

		case poll := <-n.pollCh:
			// reply with pollReply
			n.log.With(Fields{"poll": poll}).Debugf("poll received, now send a reply")

			// if we are asked to send changes regularyl, set the Ticker here

		case <-n.shutdownCh:
			return
		}
	}
}

// sendLoop is used to send packets to the network
func (n *Node) sendLoop() {
	// loop untill shutdown
	for {
		select {
		case payload := <-n.sendCh:
			n.shutdownLock.Lock()
			if !n.shutdown {
				var num int
				var err error
				if payload.address.IP.Equal(broadcastAddr.IP) {
					num, err = n.bconn.Write(payload.data)
				} else {
					num, err = n.conn.WriteTo(payload.data, &payload.address)
				}
				if err != nil {
					n.log.With(Fields{"error": err}).Debugf("error writing packet")
					continue
				}
				n.log.With(Fields{"dst": payload.address.String(), "bytes": num}).Debugf("packet sent")
			}
			n.shutdownLock.Unlock()
		case <-n.shutdownCh:
			return
		}
	}
}

// AddrToUDPAddr will turn a net.Addr into a net.UDPAddr
func AddrToUDPAddr(addr net.Addr) net.UDPAddr {
	udp := addr.(*net.UDPAddr)
	return *udp
}

// recvLoop is used to receive packets from the network
// it starts a goroutine for dumping the msgs onto a channel,
// the payload from that channel is then fed into a handler
// due to the nature of broadcasting, we see our own sent
// packets to, but we ignore them
func (n *Node) recvLoop() {
	// start a routine that will read data from n.conn
	// and (if not shutdown), send to the recvCh
	go func() {
		b := make([]byte, 4096)
		for {
			num, src, err := n.conn.ReadFrom(b)
			n.shutdownLock.Lock()
			if !n.shutdown {
				n.shutdownLock.Unlock()
				from := AddrToUDPAddr(src)
				if n.Config.IP.Equal(from.IP) {
					// this was sent by me, so we ignore it
					//n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("ignoring received packet from self")
					continue
				}

				n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("received packet")
				if err != nil && err != io.EOF {
					n.recvCh <- netPayload{
						address: from,
						data:    b[:num],
						err:     err,
					}
					continue
				}
				n.recvCh <- netPayload{
					address: from,
					data:    b[:num],
					err:     err,
				}
				continue
			}
			n.shutdownLock.Unlock()
			return
		}
	}()

	// loop untill shutdown
	for {
		select {
		case payload := <-n.recvCh:
			p, err := packet.Unmarshal(payload.data)
			if err != nil {
				n.log.Printf("failed to parse packet from %s: %v", payload.address.String(), err)
				continue
			}
			go n.handlePacket(p)

		case <-n.shutdownCh:
			return
		}
	}
}

// handlePacket contains the logic for dealing with incoming packets
func (n *Node) handlePacket(p packet.ArtNetPacket) {
	switch p := p.(type) {
	case *packet.ArtPollReplyPacket:
		// only handle these packets if we are a controller
		if n.Config.Type == code.StController {
			n.pollReplyCh <- *p
		}

	default:
		n.log.With(Fields{"packet": p}).Debugf("unknown packet type")
	}

}
<|endoftext|>


<commit_before>package node

// Consider for data structures: http://arslan.io/thread-safe-set-data-structure-in-go

import (
	"fmt"
	"os"
	"sync"
	"time"
)

type Node interface {
	Accept(Peer) error
	Listen(*Session)
	Close() error
	GetLocalPeer(URI, map[string]interface{}) (Peer, error)
}

type node struct {
	closing   bool
	closeLock sync.Mutex
	Authen
	Broker
	Dealer
	Agent
	agent    *Client
	sessions map[string]Session
	stats    *NodeStats
	PermMode string
	Config   *NodeConfig
}

// NewDefaultNode creates a very basic WAMP Node.
func NewNode(config *NodeConfig) Node {
	node := &node{
		sessions: make(map[string]Session, 0),
		Broker:   NewDefaultBroker(),
		Dealer:   NewDefaultDealer(),
		Agent:    NewAgent(),
		stats:    NewNodeStats(),
		PermMode: os.Getenv("EXIS_PERMISSIONS"),
		Config:   config,
	}

	node.agent = node.localClient(config.Agent)
	node.Authen = NewAuthen(node)

	node.RegisterGetUsage()

	return node
}

func (node *node) Close() error {
	node.closeLock.Lock()

	if node.closing {
		node.closeLock.Unlock()
		return fmt.Errorf("already closed")
	}

	node.closing = true
	node.closeLock.Unlock()

	// Tell all sessions wer're going down
	for _, s := range node.sessions {
		s.kill <- ErrSystemShutdown
	}

	// Clear the map (might not be needed)
	node.sessions = make(map[string]Session, 0)

	return nil
}

func (node *node) Accept(client Peer) error {
	sess, ok := node.Handshake(client)

	node.stats.LogEvent("SessionAccept")

	if ok != nil {
		return ok
	}

	// Start listening on the session
	// This will eventually move to the session
	go node.Listen(&sess)

	return nil
}

// Spin on a session, wait for messages to arrive. Method does not return
func (node *node) Listen(sess *Session) {
	c := sess.Receive()

	limit := node.Config.GetRequestLimit(sess.authid)
	limiter := NewBasicLimiter(limit)
	out.Debug("Request rate limit for %s: %d/s", sess, limit)

	for {
		var open bool
		var msg Message

		limiter.Acquire()

		select {
		case msg, open = <-c:
			if !open {
				//log.Println("lost session:", sess)

				node.SessionClose(sess)
				return
			}

		case reason := <-sess.kill:
			logErr(sess.Send(&Goodbye{Reason: reason, Details: make(map[string]interface{})}))
			//log.Printf("kill session %s: %v", sess, reason)

			//NEW: Exit the session!
			node.SessionClose(sess)
			return
		}

		node.Handle(&msg, sess)
	}
}

// Handle a new Peer, creating and returning a session
func (n *node) Handshake(client Peer) (Session, error) {
	sess := Session{Peer: client, kill: make(chan URI, 1)}

	// Dont accept new sessions if the node is going down
	if n.closing {
		logErr(client.Send(&Abort{Reason: ErrSystemShutdown}))
		logErr(client.Close())
		return sess, fmt.Errorf("Node is closing, no new connections are allowed")
	}

	msg, err := GetMessageTimeout(client, 5*time.Second)
	if err != nil {
		return sess, err
	}

	hello, msgOk := msg.(*Hello)

	// Ensure the message is valid and well constructed
	if !msgOk {
		logErr(client.Send(&Abort{Reason: URI("wamp.error.protocol_violation")}))
		logErr(client.Close())

		return sess, fmt.Errorf("protocol violation: expected HELLO, received %s", msg.MessageType())
	}

	sess.pdid = hello.Realm
	sess.authid = string(hello.Realm)

	// Old implementation: the authentication must occur before fetching the realm
	welcome, err := n.Authen.handleAuth(&sess, hello)

	if err != nil {
		abort := &Abort{
			Reason:  ErrAuthorizationFailed, // TODO: should this be AuthenticationFailed?
			Details: map[string]interface{}{"error": err.Error()},
		}

		logErr(client.Send(abort))
		logErr(client.Close())
		return sess, AuthenticationError(err.Error())
	}

	welcome.Id = NewID()

	if welcome.Details == nil {
		welcome.Details = make(map[string]interface{})
	}

	// add default details to welcome message
	for k, v := range defaultWelcomeDetails {
		if _, ok := welcome.Details[k]; !ok {
			welcome.Details[k] = v
		}
	}

	if err := client.Send(welcome); err != nil {
		return sess, err
	}

	out.Notice("Session open: %s", string(hello.Realm))
	sess.Id = welcome.Id
	n.sessions[string(hello.Realm)] = sess

	return sess, nil
}

// Called when a session is closed or closes itself
func (n *node) SessionClose(sess *Session) {
	sess.Close()
	out.Notice("Session close: %s", sess)

	n.Dealer.lostSession(sess)
	n.Broker.lostSession(sess)

	n.stats.LogEvent("SessionClose")

	delete(n.sessions, string(sess.pdid))
}

func (n *node) LogMessage(msg *Message, sess *Session) {
	// Extract the target domain from the message
	target, err := destination(msg)

	// Make errors nice and pretty. These are riffle error messages, not node errors
	m := *msg
	if m.MessageType() == ERROR {
		out.Warning("%s from %s", m.MessageType(), *sess)
	} else if err == nil {
		out.Debug("%s %s from %s", m.MessageType(), string(target), *sess)
	} else {
		out.Debug("%s from %s", m.MessageType(), *sess)
	}

	n.stats.CountMessage(msg)
}

// Handle a new message
func (n *node) Handle(msg *Message, sess *Session) {
	// NOTE: there is a serious shortcoming here: How do we deal with WAMP messages with an
	// implicit destination? Many of them refer to sessions, but do we want to store the session
	// IDs with the ultimate PDID target, or just change the protocol?

	n.LogMessage(msg, sess)

	// Extract the target domain from the message
	target, err := destination(msg)
	if err == nil {
		// Ensure the construction of the message is valid, extract the endpoint, domain, and action
		_, _, err := breakdownEndpoint(string(target))

		// Return a WAMP error to the user indicating a poorly constructed endpoint
		if err != nil {
			out.Error("Misconstructed endpoint: %s", msg)
			m := *msg

			err := &Error{
				Type:    m.MessageType(),
				Request: requestID(msg),
				Details: map[string]interface{}{"Invalid Endpoint": "Poorly constructed endpoint."},
				Error:   ErrInvalidUri,
			}

			sess.Peer.Send(err)
			return
		}

		// Downward domain action? That is, endpoint is a subdomain of the current agent?
		if !n.Permitted(target, sess) {
			out.Warning("Action not allowed: %s:%s", sess.pdid, target)

			m := *msg
			err := &Error{
				Type:    m.MessageType(),
				Request: requestID(msg),
				Details: map[string]interface{}{"Not Permitted": "Action not permitted."},
				Error:   ErrNotAuthorized,
			}

			sess.Peer.Send(err)
			return
		}
	}

	switch msg := (*msg).(type) {
	case *Goodbye:
		logErr(sess.Send(&Goodbye{Reason: ErrGoodbyeAndOut, Details: make(map[string]interface{})}))
		// log.Printf("[%s] leaving: %v", sess, msg.Reason)
		return

	// Broker messages
	case *Publish:
		n.Broker.Publish(sess, msg)
	case *Subscribe:
		n.Broker.Subscribe(sess, msg)
	case *Unsubscribe:
		n.Broker.Unsubscribe(sess, msg)

	// Dealer messages
	case *Register:
		n.Dealer.Register(sess, msg)
	case *Unregister:
		n.Dealer.Unregister(sess, msg)
	case *Call:
		n.Dealer.Call(sess, msg)
	case *Yield:
		n.Dealer.Yield(sess, msg)

	// Error messages
	case *Error:
		if msg.Type == INVOCATION {
			// the only type of ERROR message the Node should receive
			n.Dealer.Error(sess, msg)
		} else {
			out.Critical("invalid ERROR message received: %v", msg)
		}

	default:
		out.Critical("Unhandled message:", msg.MessageType())
	}
}

// Return true or false based on the message and the session which sent the message
func (n *node) Permitted(endpoint URI, sess *Session) bool {
	// Permissions checking is turned off---only for testing, please!
	if n.PermMode == "off" {
		return true
	}

	// The node is always permitted to perform any action
	if sess.isLocal() {
		return true
	}

	// Always allow downward actions.
	if subdomain(string(sess.authid), string(endpoint)) {
		return true
	}

	// Look up auth level of receiver.  The action will not be permitted if the
	// receiver is more strongly authenticated than the caller.
	//
	// This code is only for testing interaction with authenticated agents
	// without breaking unauthenticated agents.
	//
	// TODO: Remove this code when all agents are authenticated.
	targetDomain, _ := extractDomain(string(endpoint))
	targetSession, ok := n.sessions[targetDomain]
	if ok && targetSession.authLevel > sess.authLevel {
		out.Warning("Communication with authenticated agent %s not permitted", targetSession.pdid)
		return false
	}

	// TODO Check permissions cache: if found, allow

	// Check with bouncer(s) on permissions check.
	// At least one bouncer needs to approve a non-downward action.
	for _, bouncer := range ancestorDomains(targetDomain, "bouncer") {
		checkPerm := bouncer + "/checkPerm"

		bouncerActive := n.Dealer.hasRegistration(checkPerm)
		if !bouncerActive {
			continue
		}

		args := []interface{}{string(sess.authid), string(endpoint)}
		ret, err := n.agent.Call(checkPerm, args, nil)
		if err != nil {
			out.Critical("Error, returning false: %s", err)
			continue
		}

		if permitted, ok := ret.Arguments[0].(bool); ok {
			if !ok {
				out.Critical("Could not extract permission from return val. Bouncer called and returnd: %s", ret.Arguments)
				continue
			}

			if ok && permitted {
				return true
			}

			// TODO: save a permitted action in some flavor of cache
		}
	}

	// No bouncer approved it.
	return false
}

// returns the pdid of the next hop on the path for the given message
func (n *node) Route(msg *Message) string {
	// Is target a tenant?
	// Is target in forwarding tables?
	// Ask map for next hop

	return ""
}

// GetLocalPeer returns an internal peer connected to the specified realm.
func (r *node) GetLocalPeer(realmURI URI, details map[string]interface{}) (Peer, error) {
	peerA, peerB := localPipe()
	sess := Session{Peer: peerA, Id: NewID(), kill: make(chan URI, 1)}
	out.Notice("Established internal session:", sess.Id)

	if details == nil {
		details = make(map[string]interface{})
	}

	go r.Listen(&sess)
	return peerB, nil
}

func (r *node) getTestPeer() Peer {
	peerA, peerB := localPipe()
	go r.Accept(peerA)
	return peerB
}

var defaultWelcomeDetails = map[string]interface{}{
	"roles": map[string]struct{}{
		"broker": {},
		"dealer": {},
	},
}

////////////////////////////////////////
// Misc and old
////////////////////////////////////////

func (n *node) localClient(s string) *Client {
	p := n.getTestPeer()

	client := NewClient(p)
	client.ReceiveTimeout = 1000 * time.Millisecond
	if _, err := client.JoinRealm(s, nil); err != nil {
		out.Error("Error when creating new client: ", err)
	}

	client.pdid = URI(s)

	return client
}
<commit_msg>Fixing race condition around sessions map access in the node<commit_after>package node

// Consider for data structures: http://arslan.io/thread-safe-set-data-structure-in-go

import (
	"fmt"
	"os"
	"sync"
	"time"
)

type Node interface {
	Accept(Peer) error
	Listen(*Session)
	Close() error
	GetLocalPeer(URI, map[string]interface{}) (Peer, error)
}

type node struct {
	closing   bool
	closeLock sync.Mutex
	Authen
	Broker
	Dealer
	Agent
	agent       *Client
	sessions    map[string]Session
	sessionLock sync.RWMutex
	stats       *NodeStats
	PermMode    string
	Config      *NodeConfig
}

// NewDefaultNode creates a very basic WAMP Node.
func NewNode(config *NodeConfig) Node {
	node := &node{
		sessions: make(map[string]Session, 0),
		Broker:   NewDefaultBroker(),
		Dealer:   NewDefaultDealer(),
		Agent:    NewAgent(),
		stats:    NewNodeStats(),
		PermMode: os.Getenv("EXIS_PERMISSIONS"),
		Config:   config,
	}

	node.agent = node.localClient(config.Agent)
	node.Authen = NewAuthen(node)

	node.RegisterGetUsage()

	return node
}

func (node *node) Close() error {
	node.closeLock.Lock()

	if node.closing {
		node.closeLock.Unlock()
		return fmt.Errorf("already closed")
	}

	node.closing = true
	node.closeLock.Unlock()

	// Tell all sessions wer're going down
	// sessions must be locked before access, read is ok here
	node.sessionLock.RLock()
	for _, s := range node.sessions {
		s.kill <- ErrSystemShutdown
	}
	node.sessionLock.RUnlock()

	// Clear the map (might not be needed)
	node.sessionLock.Lock()
	node.sessions = make(map[string]Session, 0)
	node.sessionLock.Unlock()

	return nil
}

func (node *node) Accept(client Peer) error {
	sess, ok := node.Handshake(client)

	node.stats.LogEvent("SessionAccept")

	if ok != nil {
		return ok
	}

	// Start listening on the session
	// This will eventually move to the session
	go node.Listen(&sess)

	return nil
}

// Spin on a session, wait for messages to arrive. Method does not return
func (node *node) Listen(sess *Session) {
	c := sess.Receive()

	limit := node.Config.GetRequestLimit(sess.authid)
	limiter := NewBasicLimiter(limit)
	out.Debug("Request rate limit for %s: %d/s", sess, limit)

	for {
		var open bool
		var msg Message

		limiter.Acquire()

		select {
		case msg, open = <-c:
			if !open {
				//log.Println("lost session:", sess)

				node.SessionClose(sess)
				return
			}

		case reason := <-sess.kill:
			logErr(sess.Send(&Goodbye{Reason: reason, Details: make(map[string]interface{})}))
			//log.Printf("kill session %s: %v", sess, reason)

			//NEW: Exit the session!
			node.SessionClose(sess)
			return
		}

		node.Handle(&msg, sess)
	}
}

// Handle a new Peer, creating and returning a session
func (n *node) Handshake(client Peer) (Session, error) {
	sess := Session{Peer: client, kill: make(chan URI, 1)}

	// Dont accept new sessions if the node is going down
	if n.closing {
		logErr(client.Send(&Abort{Reason: ErrSystemShutdown}))
		logErr(client.Close())
		return sess, fmt.Errorf("Node is closing, no new connections are allowed")
	}

	msg, err := GetMessageTimeout(client, 5*time.Second)
	if err != nil {
		return sess, err
	}

	hello, msgOk := msg.(*Hello)

	// Ensure the message is valid and well constructed
	if !msgOk {
		logErr(client.Send(&Abort{Reason: URI("wamp.error.protocol_violation")}))
		logErr(client.Close())

		return sess, fmt.Errorf("protocol violation: expected HELLO, received %s", msg.MessageType())
	}

	sess.pdid = hello.Realm
	sess.authid = string(hello.Realm)

	// Old implementation: the authentication must occur before fetching the realm
	welcome, err := n.Authen.handleAuth(&sess, hello)

	if err != nil {
		abort := &Abort{
			Reason:  ErrAuthorizationFailed, // TODO: should this be AuthenticationFailed?
			Details: map[string]interface{}{"error": err.Error()},
		}

		logErr(client.Send(abort))
		logErr(client.Close())
		return sess, AuthenticationError(err.Error())
	}

	welcome.Id = NewID()

	if welcome.Details == nil {
		welcome.Details = make(map[string]interface{})
	}

	// add default details to welcome message
	for k, v := range defaultWelcomeDetails {
		if _, ok := welcome.Details[k]; !ok {
			welcome.Details[k] = v
		}
	}

	if err := client.Send(welcome); err != nil {
		return sess, err
	}

	out.Notice("Session open: %s", string(hello.Realm))
	sess.Id = welcome.Id
	n.sessionLock.Lock()
	n.sessions[string(hello.Realm)] = sess
	n.sessionLock.Unlock()

	return sess, nil
}

// Called when a session is closed or closes itself
func (n *node) SessionClose(sess *Session) {
	sess.Close()
	out.Notice("Session close: %s", sess)

	n.Dealer.lostSession(sess)
	n.Broker.lostSession(sess)

	n.stats.LogEvent("SessionClose")

	n.sessionLock.Lock()
	delete(n.sessions, string(sess.pdid))
	n.sessionLock.Unlock()
}

func (n *node) LogMessage(msg *Message, sess *Session) {
	// Extract the target domain from the message
	target, err := destination(msg)

	// Make errors nice and pretty. These are riffle error messages, not node errors
	m := *msg
	if m.MessageType() == ERROR {
		out.Warning("%s from %s", m.MessageType(), *sess)
	} else if err == nil {
		out.Debug("%s %s from %s", m.MessageType(), string(target), *sess)
	} else {
		out.Debug("%s from %s", m.MessageType(), *sess)
	}

	n.stats.CountMessage(msg)
}

// Handle a new message
func (n *node) Handle(msg *Message, sess *Session) {
	// NOTE: there is a serious shortcoming here: How do we deal with WAMP messages with an
	// implicit destination? Many of them refer to sessions, but do we want to store the session
	// IDs with the ultimate PDID target, or just change the protocol?

	n.LogMessage(msg, sess)

	// Extract the target domain from the message
	target, err := destination(msg)
	if err == nil {
		// Ensure the construction of the message is valid, extract the endpoint, domain, and action
		_, _, err := breakdownEndpoint(string(target))

		// Return a WAMP error to the user indicating a poorly constructed endpoint
		if err != nil {
			out.Error("Misconstructed endpoint: %s", msg)
			m := *msg

			err := &Error{
				Type:    m.MessageType(),
				Request: requestID(msg),
				Details: map[string]interface{}{"Invalid Endpoint": "Poorly constructed endpoint."},
				Error:   ErrInvalidUri,
			}

			sess.Peer.Send(err)
			return
		}

		// Downward domain action? That is, endpoint is a subdomain of the current agent?
		if !n.Permitted(target, sess) {
			out.Warning("Action not allowed: %s:%s", sess.pdid, target)

			m := *msg
			err := &Error{
				Type:    m.MessageType(),
				Request: requestID(msg),
				Details: map[string]interface{}{"Not Permitted": "Action not permitted."},
				Error:   ErrNotAuthorized,
			}

			sess.Peer.Send(err)
			return
		}
	}

	switch msg := (*msg).(type) {
	case *Goodbye:
		logErr(sess.Send(&Goodbye{Reason: ErrGoodbyeAndOut, Details: make(map[string]interface{})}))
		// log.Printf("[%s] leaving: %v", sess, msg.Reason)
		return

	// Broker messages
	case *Publish:
		n.Broker.Publish(sess, msg)
	case *Subscribe:
		n.Broker.Subscribe(sess, msg)
	case *Unsubscribe:
		n.Broker.Unsubscribe(sess, msg)

	// Dealer messages
	case *Register:
		n.Dealer.Register(sess, msg)
	case *Unregister:
		n.Dealer.Unregister(sess, msg)
	case *Call:
		n.Dealer.Call(sess, msg)
	case *Yield:
		n.Dealer.Yield(sess, msg)

	// Error messages
	case *Error:
		if msg.Type == INVOCATION {
			// the only type of ERROR message the Node should receive
			n.Dealer.Error(sess, msg)
		} else {
			out.Critical("invalid ERROR message received: %v", msg)
		}

	default:
		out.Critical("Unhandled message:", msg.MessageType())
	}
}

// Return true or false based on the message and the session which sent the message
func (n *node) Permitted(endpoint URI, sess *Session) bool {
	// Permissions checking is turned off---only for testing, please!
	if n.PermMode == "off" {
		return true
	}

	// The node is always permitted to perform any action
	if sess.isLocal() {
		return true
	}

	// Always allow downward actions.
	if subdomain(string(sess.authid), string(endpoint)) {
		return true
	}

	// Look up auth level of receiver.  The action will not be permitted if the
	// receiver is more strongly authenticated than the caller.
	//
	// This code is only for testing interaction with authenticated agents
	// without breaking unauthenticated agents.
	//
	// TODO: Remove this code when all agents are authenticated.
	targetDomain, _ := extractDomain(string(endpoint))
	n.sessionLock.RLock()
	targetSession, ok := n.sessions[targetDomain]
	n.sessionLock.RUnlock()
	if ok && targetSession.authLevel > sess.authLevel {
		out.Warning("Communication with authenticated agent %s not permitted", targetSession.pdid)
		return false
	}

	// TODO Check permissions cache: if found, allow

	// Check with bouncer(s) on permissions check.
	// At least one bouncer needs to approve a non-downward action.
	for _, bouncer := range ancestorDomains(targetDomain, "bouncer") {
		checkPerm := bouncer + "/checkPerm"

		bouncerActive := n.Dealer.hasRegistration(checkPerm)
		if !bouncerActive {
			continue
		}

		args := []interface{}{string(sess.authid), string(endpoint)}
		ret, err := n.agent.Call(checkPerm, args, nil)
		if err != nil {
			out.Critical("Error, returning false: %s", err)
			continue
		}

		if permitted, ok := ret.Arguments[0].(bool); ok {
			if !ok {
				out.Critical("Could not extract permission from return val. Bouncer called and returnd: %s", ret.Arguments)
				continue
			}

			if ok && permitted {
				return true
			}

			// TODO: save a permitted action in some flavor of cache
		}
	}

	// No bouncer approved it.
	return false
}

// returns the pdid of the next hop on the path for the given message
func (n *node) Route(msg *Message) string {
	// Is target a tenant?
	// Is target in forwarding tables?
	// Ask map for next hop

	return ""
}

// GetLocalPeer returns an internal peer connected to the specified realm.
func (r *node) GetLocalPeer(realmURI URI, details map[string]interface{}) (Peer, error) {
	peerA, peerB := localPipe()
	sess := Session{Peer: peerA, Id: NewID(), kill: make(chan URI, 1)}
	out.Notice("Established internal session:", sess.Id)

	if details == nil {
		details = make(map[string]interface{})
	}

	go r.Listen(&sess)
	return peerB, nil
}

func (r *node) getTestPeer() Peer {
	peerA, peerB := localPipe()
	go r.Accept(peerA)
	return peerB
}

var defaultWelcomeDetails = map[string]interface{}{
	"roles": map[string]struct{}{
		"broker": {},
		"dealer": {},
	},
}

////////////////////////////////////////
// Misc and old
////////////////////////////////////////

func (n *node) localClient(s string) *Client {
	p := n.getTestPeer()

	client := NewClient(p)
	client.ReceiveTimeout = 1000 * time.Millisecond
	if _, err := client.JoinRealm(s, nil); err != nil {
		out.Error("Error when creating new client: ", err)
	}

	client.pdid = URI(s)

	return client
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"net"
	"sort"
	"strings"
)

type NSCheck struct {
	NS      []NSData
	NSCheck []NSCheckData
	Report
}

type NSCheckData struct {
	Name      string
	IP        string
	NS        []dns.RR
	CNAME     []dns.RR
	Error     string
	Auth      bool
	Recursive bool
}

func (c *NSCheck) Scan(domain string) {
	for _, ns := range c.NS {
		for _, nsip := range ns.IP {
			data := NSCheckData{Name: ns.Name, IP: nsip.String()}
			res, err := query(domain, dns.TypeNS, nsip.String(), true)
			if err != nil {
				data.Error = fmt.Sprintf("NS check failed on %s: %s", nsip.String(), err)
				c.NSCheck = append(c.NSCheck, data)
				break
			}
			nsrr := extractRR(res.Msg.Answer, dns.TypeNS)
			if len(nsrr) == 0 {
				data.Error = fmt.Sprintf("NSCheck check failed on %s: %s", nsip.String(), "no records found")
				data.NS = nsrr
				data.Auth = res.Msg.Authoritative
				c.NSCheck = append(c.NSCheck, data)
				break
			}
			data.NS = nsrr
			data.Auth = res.Msg.Authoritative
			data.Recursive = res.Msg.RecursionAvailable
			c.NSCheck = append(c.NSCheck, data)
		}
	}
}

func (c *NSCheck) CheckCNAME() []ReportResult {
	rep := []ReportResult{}
	m := make(map[string]bool)
	for _, ns := range c.NSCheck {
		// skip lookup if already done
		if _, ok := m[ns.Name]; ok {
			break
		}
		// asking recursor for now
		res, err := query(dns.Fqdn(ns.Name), dns.TypeA, resolver, true)
		if err != nil {
			break
		}
		cname := extractRR(res.Msg.Answer, dns.TypeCNAME)
		if len(cname) > 0 {
			rep = append(rep, ReportResult{Result: fmt.Sprintf("FAIL: Your nameserver (%s) is a CNAME.", ns.Name),
				Status: false})
		}
		res, err = query(dns.Fqdn(ns.Name), dns.TypeAAAA, resolver, true)
		if err != nil {
			break
		}
		cname = extractRR(res.Msg.Answer, dns.TypeCNAME)
		if len(cname) > 0 {
			rep = append(rep, ReportResult{Result: fmt.Sprintf("FAIL: Your nameserver (%s) is a CNAME.", ns.Name),
				Status: false})
		}
		m[ns.Name] = true
	}
	if len(rep) == 0 {
		rep = append(rep, ReportResult{Result: "OK  : No CNAMEs found for your NS records",
			Status: true})
	}
	return rep
}

func (c *NSCheck) CheckParent(domain string) ReportResult {
	nsdata, err := findNS(getParentDomain(domain))
	if err != nil {
		return ReportResult{}
	}
	var rrset []dns.RR
loop:
	for _, ns := range nsdata {
		for _, nsip := range ns.IP {
			res, err := query(dns.Fqdn(domain), dns.TypeNS, nsip.String(), true)
			if err != nil {
				break
			}
			rrset = extractRR(res.Msg.Ns, dns.TypeNS)
			if len(rrset) > 0 {
				break loop
			}
		}
	}
	m := make(map[string]bool)
	for _, rr := range rrset {
		m[dns.Fqdn(rr.(*dns.NS).Ns)] = true
	}
	missing := []string{}
	for _, ns := range c.NS {
		if _, ok := m[ns.Name]; !ok {
			missing = append(missing, ns.Name)
		}
	}
	if len(missing) > 0 {
		return ReportResult{Result: fmt.Sprintf("FAIL: The following nameservers are not listed at the parent nameservers: %s", missing),
			Status: false}
	} else {
		return ReportResult{Result: "OK  : Your nameservers are also listed at the parent nameservers",
			Status: true}
	}
}

func (c *NSCheck) Identical() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.NSCheck {
		nsstr := []string{}
		if ns.NS != nil && ns.Error == "" {
			for _, nsc := range ns.NS {
				nsstr = append(nsstr, nsc.String()+"\n\t ")
			}
			sort.Strings(nsstr)
			m[strings.Join(nsstr, "")] = append(m[strings.Join(nsstr, "")], ns.IP)
		}
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = fmt.Sprintf("FAIL: NS not identical\n")
		res.Status = false
		for k, v := range m {
			res.Result += fmt.Sprintf("\t %s\n\t %s\n", v, k)
		}
	} else {
		res.Result = "OK  : NS of all nameservers are identical"
		res.Status = true
	}
	return res
}

func (c *NSCheck) ASN() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.NSCheck {
		ip := net.ParseIP(ns.IP)
		info, _ := ipinfo(ip)
		m[info.ASN.String()] = append(m[info.ASN.String()], ns.IP)
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = "OK  : Nameservers are spread over multiple AS"
		res.Status = true
	} else {
		as := ""
		for k := range m {
			as = k
			break
		}
		res.Result = fmt.Sprintf("WARN: Nameservers are all on the same AS (%s). This is a single point of failure.", as)
		res.Status = false
	}
	return res
}

func (c *NSCheck) IPCheck() []ReportResult {
	m := make(map[string]int)
	for _, ns := range c.NSCheck {
		if strings.Contains(ns.IP, ":") {
			m["ipv6"]++
		} else {
			m["ipv4"]++
		}
	}

	res := []ReportResult{}
	if m["ipv6"] == 0 {
		res = append(res, ReportResult{Result: "WARN: No IPv6 nameservers found. IPv6-only users will have problems.",
			Status: false})
	}

	// I wonder when this will ever happen :)
	if m["ipv4"] == 0 {
		res = append(res, ReportResult{Result: "WARN: No IPv4 nameservers found. IPv4-only users will have problems.",
			Status: false})
	}
	if (m["ipv4"] > 0) && (m["ipv6"] > 0) {
		res = append(res, ReportResult{Result: "OK  : IPv4 and IPv6 nameservers found.",
			Status: true})
	}
	return res
}

func (c *NSCheck) Auth() []ReportResult {
	res := []ReportResult{}
	ok := true
	for _, ns := range c.NSCheck {
		if len(ns.NS) > 0 && !ns.Auth {
			res = append(res, ReportResult{Result: fmt.Sprintf("FAIL: %s (%s) is not authoritative.", ns.Name, ns.IP),
				Status: false})
			ok = false
		}
	}
	if ok {
		res = append(res, ReportResult{Result: "OK  : All nameservers are authoritative.",
			Status: true})
	}
	return res
}

func (c *NSCheck) Recursive() []ReportResult {
	res := []ReportResult{}
	ok := true
	for _, ns := range c.NSCheck {
		if len(ns.NS) > 0 && ns.Recursive {
			res = append(res, ReportResult{Result: fmt.Sprintf("WARN: %s (%s) allows recursive queries.", ns.Name, ns.IP),
				Status: false})
			ok = false
		}
	}
	if ok {
		res = append(res, ReportResult{Result: "OK  : All nameservers report they are not allowing recursive queries.",
			Status: true})
	}
	return res
}

func (c *NSCheck) Values() []ReportResult {
	var results []ReportResult
	var rrset []dns.RR
	for _, ns := range c.NSCheck {
		if ns.NS != nil {
			rrset = ns.NS
			break
		}
	}
	if len(rrset) > 1 {
		records := []string{}
		for _, rr := range rrset {
			records = append(records, rr.String())
		}
		results = append(results, ReportResult{Result: "OK  : Multiple nameservers found",
			Status: true, Records: records})
	} else {
		results = append(results, ReportResult{Result: fmt.Sprintf("WARN: Only %v nameserver found. Extra nameservers increases reliability", len(rrset)),
			Status: false})
	}

	for _, ns := range c.NSCheck {
		if ns.NS != nil {
			if len(ns.CNAME) > 1 {
				results = append(results, ReportResult{Result: fmt.Sprintf("FAIL: NS %s is a CNAME for %s", ns.Name, ns.CNAME[0].(*dns.CNAME).Target),
					Status: false})
			}
		}
	}

	return results

	//TODO
	// check if NS actually response
	// stealth records

}

func (c *NSCheck) CreateReport(domain string) {
	c.Scan(domain)
	c.Report.Type = "NS"
	c.Report.Result = append(c.Report.Result, c.Identical())
	c.Report.Result = append(c.Report.Result, c.Values()...)
	c.Report.Result = append(c.Report.Result, c.ASN())
	c.Report.Result = append(c.Report.Result, c.IPCheck()...)
	c.Report.Result = append(c.Report.Result, c.Auth()...)
	c.Report.Result = append(c.Report.Result, c.Recursive()...)
	c.Report.Result = append(c.Report.Result, c.CheckParent(domain))
	c.Report.Result = append(c.Report.Result, c.CheckCNAME()...)
}
<commit_msg>Fix parent/child NS check (NScheck)<commit_after>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"net"
	"sort"
	"strings"
)

type NSCheck struct {
	NS      []NSData
	NSCheck []NSCheckData
	Report
}

type NSCheckData struct {
	Name      string
	IP        string
	NS        []dns.RR
	CNAME     []dns.RR
	Error     string
	Auth      bool
	Recursive bool
}

func (c *NSCheck) Scan(domain string) {
	for _, ns := range c.NS {
		for _, nsip := range ns.IP {
			data := NSCheckData{Name: ns.Name, IP: nsip.String()}
			res, err := query(domain, dns.TypeNS, nsip.String(), true)
			if err != nil {
				data.Error = fmt.Sprintf("NS check failed on %s: %s", nsip.String(), err)
				c.NSCheck = append(c.NSCheck, data)
				break
			}
			nsrr := extractRR(res.Msg.Answer, dns.TypeNS)
			if len(nsrr) == 0 {
				data.Error = fmt.Sprintf("NSCheck check failed on %s: %s", nsip.String(), "no records found")
				data.NS = nsrr
				data.Auth = res.Msg.Authoritative
				c.NSCheck = append(c.NSCheck, data)
				break
			}
			data.NS = nsrr
			data.Auth = res.Msg.Authoritative
			data.Recursive = res.Msg.RecursionAvailable
			c.NSCheck = append(c.NSCheck, data)
		}
	}
}

func (c *NSCheck) CheckCNAME() []ReportResult {
	rep := []ReportResult{}
	m := make(map[string]bool)
	for _, ns := range c.NSCheck {
		// skip lookup if already done
		if _, ok := m[ns.Name]; ok {
			break
		}
		// asking recursor for now
		res, err := query(dns.Fqdn(ns.Name), dns.TypeA, resolver, true)
		if err != nil {
			break
		}
		cname := extractRR(res.Msg.Answer, dns.TypeCNAME)
		if len(cname) > 0 {
			rep = append(rep, ReportResult{Result: fmt.Sprintf("FAIL: Your nameserver (%s) is a CNAME.", ns.Name),
				Status: false})
		}
		res, err = query(dns.Fqdn(ns.Name), dns.TypeAAAA, resolver, true)
		if err != nil {
			break
		}
		cname = extractRR(res.Msg.Answer, dns.TypeCNAME)
		if len(cname) > 0 {
			rep = append(rep, ReportResult{Result: fmt.Sprintf("FAIL: Your nameserver (%s) is a CNAME.", ns.Name),
				Status: false})
		}
		m[ns.Name] = true
	}
	if len(rep) == 0 {
		rep = append(rep, ReportResult{Result: "OK  : No CNAMEs found for your NS records",
			Status: true})
	}
	return rep
}

func (c *NSCheck) CheckParent(domain string) []ReportResult {
	var rep []ReportResult
	nsdata, err := findNS(getParentDomain(domain))
	if err != nil {
		return []ReportResult{}
	}
	var rrset []dns.RR
loop:
	for _, ns := range nsdata {
		for _, nsip := range ns.IP {
			res, err := query(dns.Fqdn(domain), dns.TypeNS, nsip.String(), true)
			if err != nil {
				break
			}
			rrset = extractRR(res.Msg.Ns, dns.TypeNS)
			if len(rrset) > 0 {
				break loop
			}
		}
	}
	m := make(map[string]bool)
	for _, rr := range rrset {
		m[dns.Fqdn(rr.(*dns.NS).Ns)] = true
	}
	missing := []string{}
	for _, ns := range c.NS {
		if _, ok := m[ns.Name]; !ok {
			missing = append(missing, ns.Name)
		} else {
			m[ns.Name] = false
		}

	}
	if len(missing) > 0 {
		rep = append(rep, ReportResult{Result: fmt.Sprintf("FAIL: The following nameservers are not listed as NS at the parent nameservers: %s", missing),
			Status: false})
	} else {
		rep = append(rep, ReportResult{Result: "OK  : Your nameservers are also listed as NS at the parent nameservers",
			Status: true})
	}

	// find the records that are sent by parent NS but arent in the domain NS
	missing = []string{}
	for k, v := range m {
		if v == true {
			missing = append(missing, k)
		}
	}
	if len(missing) > 0 {
		rep = append(rep, ReportResult{Result: fmt.Sprintf("FAIL: The following nameservers are listed at the parent but not as NS at your nameservers: %s", missing),
			Status: false})
	} else {
		rep = append(rep, ReportResult{Result: "OK  : Your parent nameservers are also listed as NS at your nameservers",
			Status: true})
	}
	return rep
}

func (c *NSCheck) Identical() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.NSCheck {
		nsstr := []string{}
		if ns.NS != nil && ns.Error == "" {
			for _, nsc := range ns.NS {
				nsstr = append(nsstr, nsc.String()+"\n\t ")
			}
			sort.Strings(nsstr)
			m[strings.Join(nsstr, "")] = append(m[strings.Join(nsstr, "")], ns.IP)
		}
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = fmt.Sprintf("FAIL: NS not identical\n")
		res.Status = false
		for k, v := range m {
			res.Result += fmt.Sprintf("\t %s\n\t %s\n", v, k)
		}
	} else {
		res.Result = "OK  : NS of all nameservers are identical"
		res.Status = true
	}
	return res
}

func (c *NSCheck) ASN() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.NSCheck {
		ip := net.ParseIP(ns.IP)
		info, _ := ipinfo(ip)
		m[info.ASN.String()] = append(m[info.ASN.String()], ns.IP)
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = "OK  : Nameservers are spread over multiple AS"
		res.Status = true
	} else {
		as := ""
		for k := range m {
			as = k
			break
		}
		res.Result = fmt.Sprintf("WARN: Nameservers are all on the same AS (%s). This is a single point of failure.", as)
		res.Status = false
	}
	return res
}

func (c *NSCheck) IPCheck() []ReportResult {
	m := make(map[string]int)
	for _, ns := range c.NSCheck {
		if strings.Contains(ns.IP, ":") {
			m["ipv6"]++
		} else {
			m["ipv4"]++
		}
	}

	res := []ReportResult{}
	if m["ipv6"] == 0 {
		res = append(res, ReportResult{Result: "WARN: No IPv6 nameservers found. IPv6-only users will have problems.",
			Status: false})
	}

	// I wonder when this will ever happen :)
	if m["ipv4"] == 0 {
		res = append(res, ReportResult{Result: "WARN: No IPv4 nameservers found. IPv4-only users will have problems.",
			Status: false})
	}
	if (m["ipv4"] > 0) && (m["ipv6"] > 0) {
		res = append(res, ReportResult{Result: "OK  : IPv4 and IPv6 nameservers found.",
			Status: true})
	}
	return res
}

func (c *NSCheck) Auth() []ReportResult {
	res := []ReportResult{}
	ok := true
	for _, ns := range c.NSCheck {
		if len(ns.NS) > 0 && !ns.Auth {
			res = append(res, ReportResult{Result: fmt.Sprintf("FAIL: %s (%s) is not authoritative.", ns.Name, ns.IP),
				Status: false})
			ok = false
		}
	}
	if ok {
		res = append(res, ReportResult{Result: "OK  : All nameservers are authoritative.",
			Status: true})
	}
	return res
}

func (c *NSCheck) Recursive() []ReportResult {
	res := []ReportResult{}
	ok := true
	for _, ns := range c.NSCheck {
		if len(ns.NS) > 0 && ns.Recursive {
			res = append(res, ReportResult{Result: fmt.Sprintf("WARN: %s (%s) allows recursive queries.", ns.Name, ns.IP),
				Status: false})
			ok = false
		}
	}
	if ok {
		res = append(res, ReportResult{Result: "OK  : All nameservers report they are not allowing recursive queries.",
			Status: true})
	}
	return res
}

func (c *NSCheck) Values() []ReportResult {
	var results []ReportResult
	var rrset []dns.RR
	for _, ns := range c.NSCheck {
		if ns.NS != nil {
			rrset = ns.NS
			break
		}
	}
	if len(rrset) > 1 {
		records := []string{}
		for _, rr := range rrset {
			records = append(records, rr.String())
		}
		results = append(results, ReportResult{Result: "OK  : Multiple nameservers found",
			Status: true, Records: records})
	} else {
		results = append(results, ReportResult{Result: fmt.Sprintf("WARN: Only %v nameserver found. Extra nameservers increases reliability", len(rrset)),
			Status: false})
	}

	for _, ns := range c.NSCheck {
		if ns.NS != nil {
			if len(ns.CNAME) > 1 {
				results = append(results, ReportResult{Result: fmt.Sprintf("FAIL: NS %s is a CNAME for %s", ns.Name, ns.CNAME[0].(*dns.CNAME).Target),
					Status: false})
			}
		}
	}

	return results

	//TODO
	// check if NS actually response
	// stealth records

}

func (c *NSCheck) CreateReport(domain string) {
	c.Scan(domain)
	c.Report.Type = "NS"
	c.Report.Result = append(c.Report.Result, c.Identical())
	c.Report.Result = append(c.Report.Result, c.Values()...)
	c.Report.Result = append(c.Report.Result, c.ASN())
	c.Report.Result = append(c.Report.Result, c.IPCheck()...)
	c.Report.Result = append(c.Report.Result, c.Auth()...)
	c.Report.Result = append(c.Report.Result, c.Recursive()...)
	c.Report.Result = append(c.Report.Result, c.CheckParent(domain)...)
	c.Report.Result = append(c.Report.Result, c.CheckCNAME()...)
}
<|endoftext|>


<commit_before>/*

OpenColorIO bindings - http://opencolorio.org/developers/api/OpenColorIO.html

*/
package ocio

/*
#cgo CPPFLAGS: -I./cpp
#cgo LDFLAGS: -lstdc++
#cgo pkg-config: OpenColorIO

#include "stdlib.h"

#include "cpp/ocio.h"

*/
import "C"

import (
	"errors"
)

type (
	LoggingLevelType int
	EnvironmentMode  int
	InterpType       int
)

const (
	LOGGING_LEVEL_NONE    LoggingLevelType = C.LOGGING_LEVEL_NONE
	LOGGING_LEVEL_WARNING LoggingLevelType = C.LOGGING_LEVEL_WARNING
	LOGGING_LEVEL_INFO    LoggingLevelType = C.LOGGING_LEVEL_INFO
	LOGGING_LEVEL_DEBUG   LoggingLevelType = C.LOGGING_LEVEL_DEBUG
	LOGGING_LEVEL_UNKNOWN LoggingLevelType = C.LOGGING_LEVEL_UNKNOWN
)

const (
	ENVIRONMENT_UNKNOWN         EnvironmentMode = C.ENV_ENVIRONMENT_UNKNOWN
	ENVIRONMENT_LOAD_PREDEFINED EnvironmentMode = C.ENV_ENVIRONMENT_LOAD_PREDEFINED
	ENVIRONMENT_LOAD_ALL        EnvironmentMode = C.ENV_ENVIRONMENT_LOAD_ALL
)

const (
	INTERP_UNKNOWN     InterpType = C.INTERP_UNKNOWN
	INTERP_NEAREST     InterpType = C.INTERP_NEAREST
	INTERP_LINEAR      InterpType = C.INTERP_LINEAR
	INTERP_TETRAHEDRAL InterpType = C.INTERP_TETRAHEDRAL
	INTERP_BEST        InterpType = C.INTERP_BEST
)

var (
	ROLE_DEFAULT         = C.GoString(C.ROLE_DEFAULT)
	ROLE_REFERENCE       = C.GoString(C.ROLE_REFERENCE)
	ROLE_DATA            = C.GoString(C.ROLE_DATA)
	ROLE_COLOR_PICKING   = C.GoString(C.ROLE_COLOR_PICKING)
	ROLE_SCENE_LINEAR    = C.GoString(C.ROLE_SCENE_LINEAR)
	ROLE_COMPOSITING_LOG = C.GoString(C.ROLE_COMPOSITING_LOG)
	ROLE_COLOR_TIMING    = C.GoString(C.ROLE_COLOR_TIMING)
	ROLE_TEXTURE_PAINT   = C.GoString(C.ROLE_TEXTURE_PAINT)
	ROLE_MATTE_PAINT     = C.GoString(C.ROLE_MATTE_PAINT)
)

/*
Errors
*/

func getLastError(ptr *C._Context) error {
	return errors.New(C.GoString(ptr.last_error))
}

// An exception class for errors detected at runtime,
// thrown when OCIO cannot find a file that is expected to exist.
// This is provided as a custom type to distinguish cases where
// one wants to continue looking for missing files, but wants to
// properly fail for other error conditions.
type ErrMissingFile struct{ what string }

func (e ErrMissingFile) Error() string { return e.what }

/*
Global
*/

/*
OpenColorIO, during normal usage, tends to cache certain information
(such as the contents of LUTs on disk, intermediate results, etc.).
Calling this function will flush all such information.
Under normal usage, this is not necessary, but it can be helpful in
particular instances, such as designing OCIO profiles, and wanting
to re-read luts without restarting.
*/
func ClearAllCaches() {
	C.ClearAllCaches()
}

// Get the version number for the library, as a dot-delimited string (e.g., “1.0.0”).
// This is also available at compile time as OCIO_VERSION.
func Version() string {
	return C.GoString(C.GetVersion())
}

// Get the version number for the library, as a single 4-byte hex number
// (e.g., 0x01050200 for “1.5.2”), to be used for numeric comparisons.
// This is also available at compile time as OCIO_VERSION_HEX.
func VersionHex() int {
	return int(C.GetVersionHex())
}

// Get the global logging level. You can override this at runtime using the
// OCIO_LOGGING_LEVEL environment variable. The client application that sets
// this should use SetLoggingLevel(), and not the environment variable.
// The default value is INFO.
//
// Returns on of the LOGGING_LEVEL_* const values
func LoggingLevel() LoggingLevelType {
	return LoggingLevelType(C.GetLoggingLevel())
}

// Set the global logging level.
func SetLoggingLevel(level LoggingLevelType) {
	C.SetLoggingLevel(C.LoggingLevel(level))
}
<commit_msg>Suppress return type compiler warning caused by macros<commit_after>/*

OpenColorIO bindings - http://opencolorio.org/developers/api/OpenColorIO.html

*/
package ocio

/*
#cgo CPPFLAGS: -I./cpp -Wno-return-type
#cgo LDFLAGS: -lstdc++
#cgo pkg-config: OpenColorIO

#include "stdlib.h"

#include "cpp/ocio.h"

*/
import "C"

import (
	"errors"
)

type (
	LoggingLevelType int
	EnvironmentMode  int
	InterpType       int
)

const (
	LOGGING_LEVEL_NONE    LoggingLevelType = C.LOGGING_LEVEL_NONE
	LOGGING_LEVEL_WARNING LoggingLevelType = C.LOGGING_LEVEL_WARNING
	LOGGING_LEVEL_INFO    LoggingLevelType = C.LOGGING_LEVEL_INFO
	LOGGING_LEVEL_DEBUG   LoggingLevelType = C.LOGGING_LEVEL_DEBUG
	LOGGING_LEVEL_UNKNOWN LoggingLevelType = C.LOGGING_LEVEL_UNKNOWN
)

const (
	ENVIRONMENT_UNKNOWN         EnvironmentMode = C.ENV_ENVIRONMENT_UNKNOWN
	ENVIRONMENT_LOAD_PREDEFINED EnvironmentMode = C.ENV_ENVIRONMENT_LOAD_PREDEFINED
	ENVIRONMENT_LOAD_ALL        EnvironmentMode = C.ENV_ENVIRONMENT_LOAD_ALL
)

const (
	INTERP_UNKNOWN     InterpType = C.INTERP_UNKNOWN
	INTERP_NEAREST     InterpType = C.INTERP_NEAREST
	INTERP_LINEAR      InterpType = C.INTERP_LINEAR
	INTERP_TETRAHEDRAL InterpType = C.INTERP_TETRAHEDRAL
	INTERP_BEST        InterpType = C.INTERP_BEST
)

var (
	ROLE_DEFAULT         = C.GoString(C.ROLE_DEFAULT)
	ROLE_REFERENCE       = C.GoString(C.ROLE_REFERENCE)
	ROLE_DATA            = C.GoString(C.ROLE_DATA)
	ROLE_COLOR_PICKING   = C.GoString(C.ROLE_COLOR_PICKING)
	ROLE_SCENE_LINEAR    = C.GoString(C.ROLE_SCENE_LINEAR)
	ROLE_COMPOSITING_LOG = C.GoString(C.ROLE_COMPOSITING_LOG)
	ROLE_COLOR_TIMING    = C.GoString(C.ROLE_COLOR_TIMING)
	ROLE_TEXTURE_PAINT   = C.GoString(C.ROLE_TEXTURE_PAINT)
	ROLE_MATTE_PAINT     = C.GoString(C.ROLE_MATTE_PAINT)
)

/*
Errors
*/

func getLastError(ptr *C._Context) error {
	return errors.New(C.GoString(ptr.last_error))
}

// An exception class for errors detected at runtime,
// thrown when OCIO cannot find a file that is expected to exist.
// This is provided as a custom type to distinguish cases where
// one wants to continue looking for missing files, but wants to
// properly fail for other error conditions.
type ErrMissingFile struct{ what string }

func (e ErrMissingFile) Error() string { return e.what }

/*
Global
*/

/*
OpenColorIO, during normal usage, tends to cache certain information
(such as the contents of LUTs on disk, intermediate results, etc.).
Calling this function will flush all such information.
Under normal usage, this is not necessary, but it can be helpful in
particular instances, such as designing OCIO profiles, and wanting
to re-read luts without restarting.
*/
func ClearAllCaches() {
	C.ClearAllCaches()
}

// Get the version number for the library, as a dot-delimited string (e.g., “1.0.0”).
// This is also available at compile time as OCIO_VERSION.
func Version() string {
	return C.GoString(C.GetVersion())
}

// Get the version number for the library, as a single 4-byte hex number
// (e.g., 0x01050200 for “1.5.2”), to be used for numeric comparisons.
// This is also available at compile time as OCIO_VERSION_HEX.
func VersionHex() int {
	return int(C.GetVersionHex())
}

// Get the global logging level. You can override this at runtime using the
// OCIO_LOGGING_LEVEL environment variable. The client application that sets
// this should use SetLoggingLevel(), and not the environment variable.
// The default value is INFO.
//
// Returns on of the LOGGING_LEVEL_* const values
func LoggingLevel() LoggingLevelType {
	return LoggingLevelType(C.GetLoggingLevel())
}

// Set the global logging level.
func SetLoggingLevel(level LoggingLevelType) {
	C.SetLoggingLevel(C.LoggingLevel(level))
}
<|endoftext|>


<commit_before>/**
 * ok.go - A small library to make assertion like testing statements friendly to the standard testing
 * package available with Go.
 * @author R. S. Doiel, <rsdoiel@gmail.com>
 * copyright (c) 2015 all rights reserved.
 * Released under the Simplified BSD License.
 * See:
 */

// Package ok provides a functions similar to those in NodeJS's assert module without catastrophic side effects.
package ok

import (
	"testing"
)

func Ok(t *testing.T, expression bool, message string) bool {
	if expression == true {
		return true
	}
	t.Errorf("Failed (expected true): [%s]\n", message)
	return false
}

func NotOk(t *testing.T, expression bool, message string) bool {
	if expression == false {
		return true
	}
	t.Errorf("Failed (expected false): [%s]\n", message)
	return false
}
<commit_msg>passes golint now.<commit_after>/**
 * ok.go - A small library to make assertion like testing statements friendly to the standard testing
 * package available with Go.
 * @author R. S. Doiel, <rsdoiel@gmail.com>
 * copyright (c) 2015 all rights reserved.
 * Released under the Simplified BSD License.
 * See:
 */

// Package ok provides a functions similar to those in NodeJS's assert module without catastrophic side effects.
package ok

import (
	"testing"
)

// Ok is similar to assert true, calls testing.T.Errorf if expression is false
func Ok(t *testing.T, expression bool, message string) bool {
	if expression == true {
		return true
	}
	t.Errorf("Failed (expected true): [%s]\n", message)
	return false
}

// NotOk is similar to assert false, calls testing.T.Errorf if expression is true
func NotOk(t *testing.T, expression bool, message string) bool {
	if expression == false {
		return true
	}
	t.Errorf("Failed (expected false): [%s]\n", message)
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"

	"github.com/go-ini/ini"
	. "github.com/reujab/bronze/types"
)

func osSegment(segment *Segment) {
	switch runtime.GOOS {
	case "darwin":
		segment.Value = icons["apple"]
	case "linux":
		file, err := ini.Load("/etc/os-release")
		die(err)
		switch file.Section("").Key("ID").Value() {
		case "arch":
			segment.Value = icons["arch"]
		case "centos":
			segment.Value = icons["centOS"]
		case "debian":
			segment.Value = icons["debian"]
		case "fedora":
			segment.Value = icons["fedora"]
		case "linuxmint":
			segment.Value = icons["mint"]
		case "suse", "opensuse":
			segment.Value = icons["SUSE"]
		case "ubuntu":
			segment.Value = icons["ubuntu"]
		case "elementary":
			segment.Value = icons["elementary"]
		}
		if segment.Value == "" {
			segment.Value = icons["linux"]
		}
	case "freebsd", "netbsd", "openbsd":
		segment.Value = icons["bsd"]
	}
}
<commit_msg>fixed os not recognizing unsupported linux distros<commit_after>package main

import (
	"runtime"

	"github.com/go-ini/ini"
	. "github.com/reujab/bronze/types"
)

func osSegment(segment *Segment) {
	switch runtime.GOOS {
	case "darwin":
		segment.Value = icons["apple"]
	case "linux":
		var distro string
		file, err := ini.Load("/etc/os-release")
		if err == nil {
			distro = file.Section("").Key("ID").Value()
		}

		switch distro {
		case "arch":
			segment.Value = icons["arch"]
		case "centos":
			segment.Value = icons["centOS"]
		case "debian":
			segment.Value = icons["debian"]
		case "fedora":
			segment.Value = icons["fedora"]
		case "linuxmint":
			segment.Value = icons["mint"]
		case "suse", "opensuse":
			segment.Value = icons["SUSE"]
		case "ubuntu":
			segment.Value = icons["ubuntu"]
		case "elementary":
			segment.Value = icons["elementary"]
		default:
			segment.Value = icons["linux"]
		}
	case "freebsd", "netbsd", "openbsd":
		segment.Value = icons["bsd"]
	}
}
<|endoftext|>


<commit_before>//Package qb is simple library to construct SQL Query.
//Supported filter of are :
//	"=" 	equal
//	">=" 	greater than
//	"<="	less than
package qb

import (
	"fmt"
	"strings"
)

//Tabler is an interface that represent table for sql.
type Tabler interface {
	TableName() string
	//Fields return a fields for the Table.
	Fields() []string
	PrimaryKeys() []string
}

type filter struct {
	field string
	op    string
	value interface{}
}

//Builder is
type Builder struct {
	explicit bool
	t        Tabler
	driver   string
	fields   []string
	orderBy  []string
	filters  []filter
}

//NewPQ create a builder for PostgreSQL database.
//If explicit, when fields is not specified the builder will return query with the fields,
//instead of *.
func NewPQ(t Tabler, explicit bool) *Builder {
	return &Builder{explicit: explicit, t: t, driver: "pq"}
}

//SetFields set the fields to retrieve from table.
func (b *Builder) SetFields(fieldName ...string) *Builder {
	for _, field := range fieldName {
		b.fields = append(b.fields, strings.ToLower(field))
	}
	return b
}

//SetFilter set the where clause for the query, filter will be AND with other filter.
func (b *Builder) SetFilter(fieldName string, op string, value interface{}) *Builder {
	f := filter{
		field: strings.ToLower(fieldName),
		op:    op,
		value: value,
	}
	b.filters = append(b.filters, f)
	return b
}

//SetRange is a convenient wrapper for setfilter.
//SetRange is equivalent with 2 SetFilter call:
//	SetFilter(field,">=",starting)
//	SetFilter(field,"<=",ending)
func (b *Builder) SetRange(fieldName string, starting, ending interface{}) *Builder {
	b.SetFilter(fieldName, ">=", starting)
	b.SetFilter(fieldName, "<=", ending)
	return b
}

//OrderBy set the order by for the query.
//If OrderBy is not called the query will orderBy primaryKey fields.
func (b *Builder) OrderBy(fieldName ...string) *Builder {
	b.orderBy = fieldName
	return b
}

//SelectAll return a query to select all data from sql.
func (b *Builder) SelectAll() string {
	query := b.initialQuery() + " " + b.orderByQuery()
	return query
}

//SelectByPK return a query with the where clause from PrimaryKey.
func (b *Builder) SelectByPK() string {
	query := b.initialQuery() + " " + b.pkWhereQuery()
	return query
}

func (b *Builder) initialQuery() string {
	if len(b.fields) == 0 {
		if !b.explicit {
			return "SELECT * FROM " + b.t.TableName()
		}
		b.fields = b.t.Fields()
	}
	query := "SELECT " + strings.Join(b.fields, ",") + " FROM " + b.t.TableName()
	return query
}

func (b *Builder) orderByQuery() string {
	b.orderBy = append(b.orderBy, b.t.PrimaryKeys()...)
	orderBy := "ORDER BY " + strings.Join(b.orderBy, ",")
	return orderBy
}

func (b *Builder) pkWhereQuery() string {
	if b.driver == "pq" {
		return pqWhere(b.t.PrimaryKeys())
	}
	return "pkWhereQuery: should not happen"
}

func (b *Builder) filterQuery() (where string, args []interface{}) {
	if b.driver == "pq" {
		return pqFilter(b.filters)
	}
	return "filterQuery: should not happen", nil
}

//Error check the query
func (b *Builder) Error() error {
	if err := b.fieldError(); err != nil {
		return err
	}
	if err := b.orderByError(); err != nil {
		return err
	}
	if err := b.filterError(); err != nil {
		return err
	}
	return nil
}

//Query return a query without checking the error.
func (b *Builder) Query() (query string, args []interface{}) {
	where, args := b.filterQuery()
	query = b.initialQuery() + " " + where
	return query, args
}

//Reset zeroes the fields,filters and orderBy so the builder can be reuse to construct new query
//without include the old fields,filters and orderBy.
func (b *Builder) Reset() *Builder {
	b.fields = []string{}
	b.filters = []filter{}
	b.orderBy = []string{}
	return b
}

func (b *Builder) fieldError() error {
	for _, field := range b.fields {
		if !b.fieldExist(field) {
			return fmt.Errorf("field %s doesn't exist", field)
		}
	}
	return nil
}

func (b *Builder) orderByError() error {
	for _, field := range b.orderBy {
		if !b.fieldExist(field) {
			return fmt.Errorf("orderBy field %s doesn't exist", field)
		}
	}
	return nil
}

func (b *Builder) filterError() error {
	for _, filter := range b.filters {
		if !b.fieldExist(filter.field) {
			return fmt.Errorf("field %s doesn't exist", filter.field)
		}
	}
	return nil
}

func (b *Builder) fieldExist(field string) bool {
	for _, val := range b.t.Fields() {
		if val == field {
			return true
		}
	}
	return false
}

//QueryMust will panic when builder return an error.
func (b *Builder) QueryMust() (query string, args []interface{}) {
	if err := b.Error(); err != nil {
		panic(err)
	}
	return b.Query()
}
<commit_msg>filter operator checking<commit_after>//Package qb is simple library to construct SQL Query.
//Supported filter of are :
//	"=" 	equal
//	">"		greater than
//	"<"		less than
//	">=" 	greater than and equal
//	"<="	less than and equal
package qb

import (
	"fmt"
	"strings"
)

//Tabler is an interface that represent table for sql.
type Tabler interface {
	TableName() string
	//Fields return a fields for the Table.
	Fields() []string
	PrimaryKeys() []string
}

type filter struct {
	field string
	op    string
	value interface{}
}

//Builder is
type Builder struct {
	explicit bool
	t        Tabler
	driver   string
	fields   []string
	orderBy  []string
	filters  []filter
}

//NewPQ create a builder for PostgreSQL database.
//If explicit, when fields is not specified the builder will return query with the fields,
//instead of *.
func NewPQ(t Tabler, explicit bool) *Builder {
	return &Builder{explicit: explicit, t: t, driver: "pq"}
}

//SetFields set the fields to retrieve from table.
func (b *Builder) SetFields(fieldName ...string) *Builder {
	for _, field := range fieldName {
		b.fields = append(b.fields, strings.ToLower(field))
	}
	return b
}

//SetFilter set the where clause for the query, filter will be AND with other filter.
func (b *Builder) SetFilter(fieldName string, op string, value interface{}) *Builder {
	f := filter{
		field: strings.ToLower(fieldName),
		op:    op,
		value: value,
	}
	b.filters = append(b.filters, f)
	return b
}

//SetRange is a convenient wrapper for setfilter.
//SetRange is equivalent with 2 SetFilter call:
//	SetFilter(field,">=",starting)
//	SetFilter(field,"<=",ending)
func (b *Builder) SetRange(fieldName string, starting, ending interface{}) *Builder {
	b.SetFilter(fieldName, ">=", starting)
	b.SetFilter(fieldName, "<=", ending)
	return b
}

//OrderBy set the order by for the query.
//If OrderBy is not called the query will orderBy primaryKey fields.
func (b *Builder) OrderBy(fieldName ...string) *Builder {
	b.orderBy = fieldName
	return b
}

//SelectAll return a query to select all data from sql.
func (b *Builder) SelectAll() string {
	query := b.initialQuery() + " " + b.orderByQuery()
	return query
}

//SelectByPK return a query with the where clause from PrimaryKey.
func (b *Builder) SelectByPK() string {
	query := b.initialQuery() + " " + b.pkWhereQuery()
	return query
}

func (b *Builder) initialQuery() string {
	if len(b.fields) == 0 {
		if !b.explicit {
			return "SELECT * FROM " + b.t.TableName()
		}
		b.fields = b.t.Fields()
	}
	query := "SELECT " + strings.Join(b.fields, ",") + " FROM " + b.t.TableName()
	return query
}

func (b *Builder) orderByQuery() string {
	b.orderBy = append(b.orderBy, b.t.PrimaryKeys()...)
	orderBy := "ORDER BY " + strings.Join(b.orderBy, ",")
	return orderBy
}

func (b *Builder) pkWhereQuery() string {
	if b.driver == "pq" {
		return pqWhere(b.t.PrimaryKeys())
	}
	return "pkWhereQuery: should not happen"
}

func (b *Builder) filterQuery() (where string, args []interface{}) {
	if b.driver == "pq" {
		return pqFilter(b.filters)
	}
	return "filterQuery: should not happen", nil
}

//Error check the query
func (b *Builder) Error() error {
	if err := b.fieldError(); err != nil {
		return err
	}
	if err := b.orderByError(); err != nil {
		return err
	}
	if err := b.filterError(); err != nil {
		return err
	}
	return nil
}

//Query return a query without checking the error.
func (b *Builder) Query() (query string, args []interface{}) {
	where, args := b.filterQuery()
	query = b.initialQuery() + " " + where
	return query, args
}

//Reset zeroes the fields,filters and orderBy so the builder can be reuse to construct new query
//without include the old fields,filters and orderBy.
func (b *Builder) Reset() *Builder {
	b.fields = []string{}
	b.filters = []filter{}
	b.orderBy = []string{}
	return b
}

func (b *Builder) fieldError() error {
	for _, field := range b.fields {
		if !b.fieldExist(field) {
			return fmt.Errorf("field %s doesn't exist", field)
		}
	}
	return nil
}

func (b *Builder) orderByError() error {
	for _, field := range b.orderBy {
		if !b.fieldExist(field) {
			return fmt.Errorf("orderBy field %s doesn't exist", field)
		}
	}
	return nil
}

func (b *Builder) filterError() error {
	for _, filter := range b.filters {
		if !b.fieldExist(filter.field) {
			return fmt.Errorf("field %s doesn't exist", filter.field)
		}
		if err := b.isValidOp(filter.op); err != nil {
			return err
		}
	}
	return nil
}

func (b *Builder) fieldExist(field string) bool {
	for _, val := range b.t.Fields() {
		if val == field {
			return true
		}
	}
	return false
}

func (b *Builder) isValidOp(op string) error {
	if op == "=" || op == "<" || op == ">" || op == ">=" || op == "<=" {
		return nil
	}
	return fmt.Errorf("filter op %s is not supported", op)
}

//QueryMust will panic when builder return an error.
func (b *Builder) QueryMust() (query string, args []interface{}) {
	if err := b.Error(); err != nil {
		panic(err)
	}
	return b.Query()
}
<|endoftext|>


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

// Package s3Manager  wraps some common s3 actions.
// Credenttials need to be stored in the environment
// See ../example for a simple command line thing .
package s3Manager

import (
	"fmt"
	"io/ioutil"
	"matilde/serialize"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
)

// hold wg and counters
var (
	wg          sync.WaitGroup
	counter     uint64
	donecounter uint64
)

// Connection wraps in one struct the required parameters
// to connect to aws, region and AWS
type Connection struct {
	Region aws.Region
	Auth   aws.Auth
}

//Connect starts the connection to aws, and returns
// any error, default strategy is to connect 5 times, wait 4 seconds and with delay of 200 ms
// make sure to source the credential file on the server
// Region is harcoded to EUWest
// Auth is read from environment.
func (c *Connection) Connect() error {
	auth, err := aws.EnvAuth()
	if err != nil {
		return err
	}
	c.Auth = auth
	c.Region = aws.EUWest
	return nil
}

// Getbucket connects to bucket name, with connection c, and returns
// a bucket type and any connection error
func Getbucket(name string, c *Connection) (*s3.Bucket, error) {
	s3Connector := s3.New(c.Auth, c.Region)
	bucket := s3Connector.Bucket(name)
	_, err := bucket.List("", "", "", 1)
	if err != nil {
		// probably wrong name of bucket
		log.Errorf("connection to s3", name, err)
	} else {
		fmt.Printf("Connected to s3 bucket: %v\n", name)
	}
	return bucket, err
}

//TODO get rid of this dependecy

// Put puts a serialize.Msgpack object into bucket b.
// by default uploads checksum of the files
func Put(b *s3.Bucket, data serialize.Msgpack) error {
	// data is a log of msgpack file kind
	// aws api wants []byte
	fileAsByteArray := data.Bytes
	options := s3.Options{}
	options.ContentMD5 = data.Checksum.Md5
	err := b.Put(data.Path, fileAsByteArray, "", s3.Private, options)
	if err != nil {
		return err
	}
	return nil
}

// Get gets the s3 object at the specified path and saves to disk
// with its filename and a root prependeded
func Get(b *s3.Bucket, path string, root string) error {
	var conn Connection
	err := conn.Connect()
	if err != nil {
		return err
	}
	data, err := b.Get(path)
	if err != nil {
		panic(fmt.Sprintf("can't download %v \n ", err))
	}
	localpath := fmt.Sprintf("%v/%v", root, path)
	stripped := strings.Split(localpath, "/")
	pathonly := strings.Join(stripped[:len(stripped)-1], "/")
	err = os.MkdirAll(pathonly, 0777)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(localpath, data, 0666)
	if err != nil {
		return err
	}
	fmt.Printf("Downloaded: %v \n", localpath)
	return nil
}

// KeyBucket holds pointers to a bucket one wants to operate with
// and the key of interest inside that bucket
type KeyBucket struct {
	B   *s3.Bucket
	Key s3.Key
}

// Action is the the action on the key/bucket speficied in KeyBucket.
type Action func(KeyBucket)

//ApplyToMultiList applies the action a to all the keys that match prefix
//To select ALL contents of the bucket use prefix, delim = ""
func ApplyToMultiList(b *s3.Bucket, prefix, delim string, a Action) {
	//prints stats
	go func() {
		for {
			select {
			case <-time.After(3 * time.Second):
				r := atomic.LoadUint64(&donecounter)
				fmt.Printf("read %v keys\n", r)
				r = atomic.LoadUint64(&counter)
				fmt.Printf("processed %v keys\n", r)
			}
		}
	}()
	resp, err := b.List(prefix, delim, "", 1000)
	if err != nil {
		panic(err)
	}
	if len(resp.Contents) < 1 {
		log.Infof("got no Contents")
		return
	}
	lastSeen := resp.Contents[len(resp.Contents)-1]
	for _, obj := range resp.Contents {
		atomic.AddUint64(&counter, 1)
		go a(KeyBucket{b, obj})
	}
	for {
		if resp.IsTruncated {
			resp, err = b.List(prefix, delim, lastSeen.Key, 1000)
			if err != nil {
				panic(err)
			}
			isTheSame := "no"
			lastSeen = resp.Contents[len(resp.Contents)-1]
			fmt.Printf("------ \n %v \n-----", lastSeen.Key)
			// TODO allow setting a max number of workers
			for _, obj := range resp.Contents {
				atomic.AddUint64(&counter, 1)
				go func() {
					wg.Add(1)
					a(KeyBucket{b, obj})
					wg.Done()
				}()
			}
			isTheSame = lastSeen.Key
		} else {
			break
		}
	}
	wg.Wait()
}

//Del deletes the key in the bucket
func Del(kb KeyBucket) {
	err := kb.B.Del(kb.Key.Key)
	if err != nil {
		log.Warnf("%v\n", err)
	}
	atomic.AddUint64(&donecounter, 1)
}
<commit_msg>Remove old dependecy.<commit_after>// Copyright 2015 Giulio Ungaretti. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package s3Manager  wraps some common s3 actions.
// Credenttials need to be stored in the environment
// See ../example for a simple command line thing .
package s3Manager

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
)

// hold wg and counters
var (
	wg          sync.WaitGroup
	counter     uint64
	donecounter uint64
)

// Connection wraps in one struct the required parameters
// to connect to aws, region and AWS
type Connection struct {
	Region aws.Region
	Auth   aws.Auth
}

//Connect starts the connection to aws, and returns
// any error, default strategy is to connect 5 times, wait 4 seconds and with delay of 200 ms
// make sure to source the credential file on the server
// Region is harcoded to EUWest
// Auth is read from environment.
func (c *Connection) Connect() error {
	auth, err := aws.EnvAuth()
	if err != nil {
		return err
	}
	c.Auth = auth
	c.Region = aws.EUWest
	return nil
}

// Getbucket connects to bucket name, with connection c, and returns
// a bucket type and any connection error
func Getbucket(name string, c *Connection) (*s3.Bucket, error) {
	s3Connector := s3.New(c.Auth, c.Region)
	bucket := s3Connector.Bucket(name)
	_, err := bucket.List("", "", "", 1)
	if err != nil {
		// probably wrong name of bucket
		log.Errorf("connection to s3", name, err)
	} else {
		fmt.Printf("Connected to s3 bucket: %v\n", name)
	}
	return bucket, err
}

// Put puts a serialize.Msgpack object into bucket b.
// by default uploads checksum of the files
func Put(b *s3.Bucket, data []byte, path string, checksum string) error {
	// data is a log of msgpack file kind
	// aws api wants []byte
	options := s3.Options{}
	options.ContentMD5 = checksum
	err := b.Put(path, data, "", s3.Private, options)
	if err != nil {
		return err
	}
	return nil
}

// Get gets the s3 object at the specified path and saves to disk
// with its filename and a root prependeded
func Get(b *s3.Bucket, path string, root string) error {
	var conn Connection
	err := conn.Connect()
	if err != nil {
		return err
	}
	data, err := b.Get(path)
	if err != nil {
		panic(fmt.Sprintf("can't download %v \n ", err))
	}
	localpath := fmt.Sprintf("%v/%v", root, path)
	stripped := strings.Split(localpath, "/")
	pathonly := strings.Join(stripped[:len(stripped)-1], "/")
	err = os.MkdirAll(pathonly, 0777)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(localpath, data, 0666)
	if err != nil {
		return err
	}
	fmt.Printf("Downloaded: %v \n", localpath)
	return nil
}

// KeyBucket holds pointers to a bucket one wants to operate with
// and the key of interest inside that bucket
type KeyBucket struct {
	B   *s3.Bucket
	Key s3.Key
}

// Action is the the action on the key/bucket speficied in KeyBucket.
type Action func(KeyBucket)

//ApplyToMultiList applies the action a to all the keys that match prefix
//To select ALL contents of the bucket use prefix, delim = ""
func ApplyToMultiList(b *s3.Bucket, prefix, delim string, a Action) {
	//prints stats
	go func() {
		for {
			select {
			case <-time.After(3 * time.Second):
				r := atomic.LoadUint64(&donecounter)
				fmt.Printf("read %v keys\n", r)
				r = atomic.LoadUint64(&counter)
				fmt.Printf("processed %v keys\n", r)
			}
		}
	}()
	resp, err := b.List(prefix, delim, "", 1000)
	if err != nil {
		panic(err)
	}
	if len(resp.Contents) < 1 {
		log.Infof("got no Contents")
		return
	}
	lastSeen := resp.Contents[len(resp.Contents)-1]
	for _, obj := range resp.Contents {
		atomic.AddUint64(&counter, 1)
		go a(KeyBucket{b, obj})
	}
	for {
		if resp.IsTruncated {
			resp, err = b.List(prefix, delim, lastSeen.Key, 1000)
			if err != nil {
				panic(err)
			}
			lastSeen = resp.Contents[len(resp.Contents)-1]
			fmt.Printf("------ \n %v \n-----", lastSeen.Key)
			// TODO allow setting a max number of workers
			for _, obj := range resp.Contents {
				atomic.AddUint64(&counter, 1)
				go func() {
					wg.Add(1)
					a(KeyBucket{b, obj})
					wg.Done()
				}()
			}
		} else {
			break
		}
	}
	wg.Wait()
}

//Del deletes the key in the bucket
func Del(kb KeyBucket) {
	err := kb.B.Del(kb.Key.Key)
	if err != nil {
		log.Warnf("%v\n", err)
	}
	atomic.AddUint64(&donecounter, 1)
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Jose Luis Aracil Gomez (pepe@diselpro.com)
// Based on Nick Patavalis (npat@efault.net) poller package.
// All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package poll

import (
	"io"
	"sync"
	"syscall"
	"time"
)

const (
	O_RDONLY   int = syscall.O_RDONLY   // open the file read-only.
	O_WRONLY   int = syscall.O_WRONLY   // open the file write-only.
	O_RDWR     int = syscall.O_RDWR     // open the file read-write.
	O_NONBLOCK int = syscall.O_NONBLOCK // open in non block mode.
)

// fdCtl keeps control fields (locks, timers, etc) for a single
// direction. For every File there is one fdCtl for Read operations and
// another for Write operations.
type fdCtl struct {
	m        sync.Mutex
	cond     *sync.Cond
	deadline time.Time
	timer    *time.Timer
	timeout  bool
}

// OsFile interface with *os.File methods used in NewFromFile
type OsFile interface {
	Close() error
	Fd() uintptr
	Name() string
}

// File is an *os.File like object who adds polling capabilities
type File struct {
	closed bool // Set by Close(), never cleared
	fd     int
	name   string
	closeF func() error
	// Must hold respective lock to access
	r fdCtl // Control fields for Read operations
	w fdCtl // Control fields for Write operations
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) (*File, error) {
	err := syscall.SetNonblock(int(fd), true)
	if err != nil {
		return nil, err
	}
	file := &File{fd: int(fd), name: name}
	file.r.cond = sync.NewCond(&sync.Mutex{})
	file.w.cond = sync.NewCond(&sync.Mutex{})
	err = register(file)
	if err != nil {
		return nil, err
	}
	return file, nil
}

// Open the named path for reading, writing or both, depnding on the
// flags argument.
func Open(name string, flags int) (*File, error) {
	fd, err := syscall.Open(name, flags|syscall.O_CLOEXEC|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return nil, err
	}
	return NewFile(uintptr(fd), name)
}

// NewFromFile returns a new *poll.File based on the given *os.File.
// You don't need to worry about closing the *os.File, *poll.File already does it.
func NewFromFile(of OsFile) (*File, error) {
	f, err := NewFile(of.Fd(), of.Name())
	if err != nil {
		return nil, err
	}
	f.closeF = of.Close
	return f, nil
}

// Name returns the name of the file as presented to Open.
func (f *File) Name() string {
	return f.name
}

// Fd returns the integer Unix file descriptor referencing the open file.
func (f *File) Fd() uintptr {
	return uintptr(f.fd)
}

// WriteString is like Write, but writes the contents of string s rather than a slice of bytes.
func (f *File) WriteString(s string) (int, error) {
	return f.Write([]byte(s))
}

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) Read(p []byte) (n int, err error) {
	f.r.m.Lock()
	n, err = f.sysrw(false, p)
	f.r.m.Unlock()
	return
}

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(p []byte) (n int, err error) {
	f.w.m.Lock()
	for n != len(p) {
		var nn int
		nn, err = f.sysrw(true, p[n:])
		n += nn
		if err != nil {
			break
		}
	}
	f.w.m.Unlock()
	return
}

func (f *File) sysrw(write bool, p []byte) (n int, err error) {
	var fdc *fdCtl
	var rwfun func(int, []byte) (int, error)
	var errEOF error

	if !write {
		// Prepare things for Read.
		fdc = &f.r
		rwfun = syscall.Read
		errEOF = io.EOF
	} else {
		// Prepare things for Write.
		fdc = &f.w
		rwfun = syscall.Write
		errEOF = io.ErrUnexpectedEOF
	}
	// Read & Write are identical
	fdc.cond.L.Lock()
	defer fdc.cond.L.Unlock()
	for {
		if f.closed {
			return 0, ErrClosed
		}
		if fdc.timeout {
			return 0, ErrTimeout
		}
		n, err = rwfun(f.fd, p)
		if err != nil {
			n = 0
			if err != syscall.EAGAIN {
				break
			}
			// EAGAIN
			startTrack(f.fd, write)
			fdc.cond.Wait()
			if f.closed || fdc.timeout {
				stopTrack(f.fd, write)
			}
			continue
		}
		if n == 0 && len(p) != 0 {
			err = errEOF
			break
		}
		break
	}
	return n, err
}

//Close closes the File, rendering it unusable for I/O. It returns an error, if any.
func (f *File) Close() error {
	if err := f.Lock(); err != nil {
		return err
	}
	defer f.Unlock()
	if f.closed {
		return ErrClosed
	}
	f.closed = true
	unregister(f)
	if f.r.timer != nil {
		f.r.timer.Stop()
	}
	if f.w.timer != nil {
		f.w.timer.Stop()
	}
	// Wake up everybody waiting on File.
	f.r.cond.Broadcast()
	f.w.cond.Broadcast()
	if f.closeF != nil {
		return f.closeF()
	}
	return syscall.Close(f.fd)
}

// SetDeadline sets the deadline for Read and write operations on File.
func (f *File) SetDeadline(t time.Time) error {
	if err := f.SetReadDeadline(t); err != nil {
		return err
	}
	return f.SetWriteDeadline(t)
}

// SetReadDeadline sets the deadline for Read operations on File.
func (f *File) SetReadDeadline(t time.Time) error {
	return f.setDeadline(false, t)
}

// SetWriteDeadline sets the deadline for Write operations on File.
func (f *File) SetWriteDeadline(t time.Time) error {
	return f.setDeadline(true, t)
}

func (f *File) setDeadline(write bool, t time.Time) error {
	var fdc *fdCtl

	if !write {
		// Setting read deadline
		fdc = &f.r
	} else {
		// Setting write deadline
		fdc = &f.w
	}
	// R & W deadlines are handled identically
	fdc.cond.L.Lock()
	if f.closed {
		fdc.cond.L.Unlock()
		return ErrClosed
	}
	fdc.deadline = t
	fdc.timeout = false
	if t.IsZero() {
		if fdc.timer != nil {
			fdc.timer.Stop()
		}
	} else {
		d := t.Sub(time.Now())
		if fdc.timer == nil {
			fdc.timer = time.AfterFunc(d,
				func() { f.timerEvent(write) })
		} else {
			fdc.timer.Stop()
			fdc.timer.Reset(d)
		}
	}
	fdc.cond.L.Unlock()
	return nil
}

// Lock locks the file. It must be called before perfoming
// miscellaneous operations (e.g. ioctls) on the underlying system
// file descriptor.
func (f *File) Lock() error {
	f.r.cond.L.Lock()
	f.w.cond.L.Lock()
	if f.closed {
		f.w.cond.L.Unlock()
		f.r.cond.L.Unlock()
		return ErrClosed
	}
	return nil
}

// Unlock unlocks the file.
func (f *File) Unlock() {
	f.w.cond.L.Unlock()
	f.r.cond.L.Unlock()
}

func (f *File) timerEvent(write bool) {
	var fdc *fdCtl

	if !write {
		// A Read timeout
		fdc = &f.r
	} else {
		// A Write timeout
		fdc = &f.w
	}
	fdc.cond.L.Lock()
	if !f.closed && !fdc.timeout &&
		!fdc.deadline.IsZero() && !fdc.deadline.After(time.Now()) {
		fdc.timeout = true
		fdc.cond.Broadcast()
	}
	fdc.cond.L.Unlock()
}
<commit_msg>Remove redundant code in Close function.<commit_after>// Copyright (c) 2015, Jose Luis Aracil Gomez (pepe@diselpro.com)
// Based on Nick Patavalis (npat@efault.net) poller package.
// All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package poll

import (
	"io"
	"sync"
	"syscall"
	"time"
)

const (
	O_RDONLY   int = syscall.O_RDONLY   // open the file read-only.
	O_WRONLY   int = syscall.O_WRONLY   // open the file write-only.
	O_RDWR     int = syscall.O_RDWR     // open the file read-write.
	O_NONBLOCK int = syscall.O_NONBLOCK // open in non block mode.
)

// fdCtl keeps control fields (locks, timers, etc) for a single
// direction. For every File there is one fdCtl for Read operations and
// another for Write operations.
type fdCtl struct {
	m        sync.Mutex
	cond     *sync.Cond
	deadline time.Time
	timer    *time.Timer
	timeout  bool
}

// OsFile interface with *os.File methods used in NewFromFile
type OsFile interface {
	Close() error
	Fd() uintptr
	Name() string
}

// File is an *os.File like object who adds polling capabilities
type File struct {
	closed bool // Set by Close(), never cleared
	fd     int
	name   string
	closeF func() error
	// Must hold respective lock to access
	r fdCtl // Control fields for Read operations
	w fdCtl // Control fields for Write operations
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) (*File, error) {
	err := syscall.SetNonblock(int(fd), true)
	if err != nil {
		return nil, err
	}
	file := &File{fd: int(fd), name: name}
	file.r.cond = sync.NewCond(&sync.Mutex{})
	file.w.cond = sync.NewCond(&sync.Mutex{})
	err = register(file)
	if err != nil {
		return nil, err
	}
	return file, nil
}

// Open the named path for reading, writing or both, depnding on the
// flags argument.
func Open(name string, flags int) (*File, error) {
	fd, err := syscall.Open(name, flags|syscall.O_CLOEXEC|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return nil, err
	}
	return NewFile(uintptr(fd), name)
}

// NewFromFile returns a new *poll.File based on the given *os.File.
// You don't need to worry about closing the *os.File, *poll.File already does it.
func NewFromFile(of OsFile) (*File, error) {
	f, err := NewFile(of.Fd(), of.Name())
	if err != nil {
		return nil, err
	}
	f.closeF = of.Close
	return f, nil
}

// Name returns the name of the file as presented to Open.
func (f *File) Name() string {
	return f.name
}

// Fd returns the integer Unix file descriptor referencing the open file.
func (f *File) Fd() uintptr {
	return uintptr(f.fd)
}

// WriteString is like Write, but writes the contents of string s rather than a slice of bytes.
func (f *File) WriteString(s string) (int, error) {
	return f.Write([]byte(s))
}

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) Read(p []byte) (n int, err error) {
	f.r.m.Lock()
	n, err = f.sysrw(false, p)
	f.r.m.Unlock()
	return
}

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(p []byte) (n int, err error) {
	f.w.m.Lock()
	for n != len(p) {
		var nn int
		nn, err = f.sysrw(true, p[n:])
		n += nn
		if err != nil {
			break
		}
	}
	f.w.m.Unlock()
	return
}

func (f *File) sysrw(write bool, p []byte) (n int, err error) {
	var fdc *fdCtl
	var rwfun func(int, []byte) (int, error)
	var errEOF error

	if !write {
		// Prepare things for Read.
		fdc = &f.r
		rwfun = syscall.Read
		errEOF = io.EOF
	} else {
		// Prepare things for Write.
		fdc = &f.w
		rwfun = syscall.Write
		errEOF = io.ErrUnexpectedEOF
	}
	// Read & Write are identical
	fdc.cond.L.Lock()
	defer fdc.cond.L.Unlock()
	for {
		if f.closed {
			return 0, ErrClosed
		}
		if fdc.timeout {
			return 0, ErrTimeout
		}
		n, err = rwfun(f.fd, p)
		if err != nil {
			n = 0
			if err != syscall.EAGAIN {
				break
			}
			// EAGAIN
			startTrack(f.fd, write)
			fdc.cond.Wait()
			if f.closed || fdc.timeout {
				stopTrack(f.fd, write)
			}
			continue
		}
		if n == 0 && len(p) != 0 {
			err = errEOF
			break
		}
		break
	}
	return n, err
}

//Close closes the File, rendering it unusable for I/O. It returns an error, if any.
func (f *File) Close() error {
	if err := f.Lock(); err != nil {
		return err
	}
	defer f.Unlock()
	f.closed = true
	unregister(f)
	if f.r.timer != nil {
		f.r.timer.Stop()
	}
	if f.w.timer != nil {
		f.w.timer.Stop()
	}
	// Wake up everybody waiting on File.
	f.r.cond.Broadcast()
	f.w.cond.Broadcast()
	if f.closeF != nil {
		return f.closeF()
	}
	return syscall.Close(f.fd)
}

// SetDeadline sets the deadline for Read and write operations on File.
func (f *File) SetDeadline(t time.Time) error {
	if err := f.SetReadDeadline(t); err != nil {
		return err
	}
	return f.SetWriteDeadline(t)
}

// SetReadDeadline sets the deadline for Read operations on File.
func (f *File) SetReadDeadline(t time.Time) error {
	return f.setDeadline(false, t)
}

// SetWriteDeadline sets the deadline for Write operations on File.
func (f *File) SetWriteDeadline(t time.Time) error {
	return f.setDeadline(true, t)
}

func (f *File) setDeadline(write bool, t time.Time) error {
	var fdc *fdCtl

	if !write {
		// Setting read deadline
		fdc = &f.r
	} else {
		// Setting write deadline
		fdc = &f.w
	}
	// R & W deadlines are handled identically
	fdc.cond.L.Lock()
	if f.closed {
		fdc.cond.L.Unlock()
		return ErrClosed
	}
	fdc.deadline = t
	fdc.timeout = false
	if t.IsZero() {
		if fdc.timer != nil {
			fdc.timer.Stop()
		}
	} else {
		d := t.Sub(time.Now())
		if fdc.timer == nil {
			fdc.timer = time.AfterFunc(d,
				func() { f.timerEvent(write) })
		} else {
			fdc.timer.Stop()
			fdc.timer.Reset(d)
		}
	}
	fdc.cond.L.Unlock()
	return nil
}

// Lock locks the file. It must be called before perfoming
// miscellaneous operations (e.g. ioctls) on the underlying system
// file descriptor.
func (f *File) Lock() error {
	f.r.cond.L.Lock()
	f.w.cond.L.Lock()
	if f.closed {
		f.w.cond.L.Unlock()
		f.r.cond.L.Unlock()
		return ErrClosed
	}
	return nil
}

// Unlock unlocks the file.
func (f *File) Unlock() {
	f.w.cond.L.Unlock()
	f.r.cond.L.Unlock()
}

func (f *File) timerEvent(write bool) {
	var fdc *fdCtl

	if !write {
		// A Read timeout
		fdc = &f.r
	} else {
		// A Write timeout
		fdc = &f.w
	}
	fdc.cond.L.Lock()
	if !f.closed && !fdc.timeout &&
		!fdc.deadline.IsZero() && !fdc.deadline.After(time.Now()) {
		fdc.timeout = true
		fdc.cond.Broadcast()
	}
	fdc.cond.L.Unlock()
}
<|endoftext|>