<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

// Package semtech provides useful methods and types to handle communications with a gateway.
//
// This package relies on the SemTech Protocol 1.2 accessible on github: https://github.com/TheThingsNetwork/packet_forwarder/blob/master/PROTOCOL.TXT
package semtech

import (
	"time"
)

// RXPK represents an uplink json message format sent by the gateway
type RXPK struct {
	Chan *uint      `json:"chan,omitempty"` // Concentrator "IF" channel used for RX (unsigned integer)
	Codr *string    `json:"codr,omitempty"` // LoRa ECC coding rate identifier
	Data *string    `json:"data,omitempty"` // Base64 encoded RF packet payload, padded
	Datr *string    `json:"-"`              // FSK datarate (unsigned in bit per second) || LoRa datarate identifier
	Freq *float64   `json:"freq,omitempty"` // RX Central frequency in MHx (unsigned float, Hz precision)
	Lsnr *float64   `json:"lsnr,omitempty"` // LoRa SNR ratio in dB (signed float, 0.1 dB precision)
	Modu *string    `json:"modu,omitempty"` // Modulation identifier "LORA" or "FSK"
	Rfch *uint      `json:"rfch,omitempty"` // Concentrator "RF chain" used for RX (unsigned integer)
	Rssi *int       `json:"rssi,omitempty"` // RSSI in dBm (signed integer, 1 dB precision)
	Size *uint      `json:"size,omitempty"` // RF packet payload size in bytes (unsigned integer)
	Stat *int       `json:"stat,omitempty"` // CRC status: 1 - OK, -1 = fail, 0 = no CRC
	Time *time.Time `json:"-"`              // UTC time of pkt RX, us precision, ISO 8601 'compact' format
	Tmst *uint      `json:"tmst,omitempty"` // Internal timestamp of "RX finished" event (32b unsigned)
}

// TXPK represents a downlink j,omitemptyson message format received by the gateway.
// Most field are optional.
type TXPK struct {
	Codr *string    `json:"codr,omitempty"`  // LoRa ECC coding rate identifier
	Data *string    `json:"data,omirtmepty"` // Base64 encoded RF packet payload, padding optional
	Datr *string    `json:"-"`               // LoRa datarate identifier (eg. SF12BW500) || FSK Datarate (unsigned, in bits per second)
	Fdev *uint      `json:"fdev,omitempty"`  // FSK frequency deviation (unsigned integer, in Hz)
	Freq *float64   `json:"freq,omitempty"`  // TX central frequency in MHz (unsigned float, Hz precision)
	Imme *bool      `json:"imme,omitempty"`  // Send packet immediately (will ignore tmst & time)
	Ipol *bool      `json:"ipol,omitempty"`  // Lora modulation polarization inversion
	Modu *string    `json:"modu,omitempty"`  // Modulation identifier "LORA" or "FSK"
	Ncrc *bool      `json:"ncrc,omitempty"`  // If true, disable the CRC of the physical layer (optional)
	Powe *uint      `json:"powe,omitempty"`  // TX output power in dBm (unsigned integer, dBm precision)
	Prea *uint      `json:"prea,omitempty"`  // RF preamble size (unsigned integer)
	Rfch *uint      `json:"rfch,omitempty"`  // Concentrator "RF chain" used for TX (unsigned integer)
	Size *uint      `json:"size,omitempty"`  // RF packet payload size in bytes (unsigned integer)
	Time *time.Time `json:"-"`               // Send packet at a certain time (GPS synchronization required)
	Tmst *uint      `json:"tmst,omitempty"`  // Send packet on a certain timestamp value (will ignore time)
}

// Stat represents a status json message format sent by the gateway
type Stat struct {
	Ackr *float64   `json:"ackr,omitempty"` // Percentage of upstream datagrams that were acknowledged
	Alti *int       `json:"alti,omitempty"` // GPS altitude of the gateway in meter RX (integer)
	Dwnb *uint      `json:"dwnb,omitempty"` // Number of downlink datagrams received (unsigned integer)
	Lati *float64   `json:"lati,omitempty"` // GPS latitude of the gateway in degree (float, N is +)
	Long *float64   `json:"long,omitempty"` // GPS latitude of the gateway in dgree (float, E is +)
	Rxfw *uint      `json:"rxfw,omitempty"` // Number of radio packets forwarded (unsigned integer)
	Rxnb *uint      `json:"rxnb,omitempty"` // Number of radio packets received (unsigned integer)
	Rxok *uint      `json:"rxok,omitempty"` // Number of radio packets received with a valid PHY CRC
	Time *time.Time `json:"-"`              // UTC 'system' time of the gateway, ISO 8601 'expanded' format
	Txnb *uint      `json:"txnb,omitempty"` // Number of packets emitted (unsigned integer)
}

// Packet as seen by the gateway.
type Packet struct {
	Version    byte     // Protocol version, should always be 1 here
	Token      []byte   // Random number generated by the gateway on some request. 2-bytes long.
	Identifier byte     // Packet's command identifier
	GatewayId  []byte   // Source gateway's identifier (Only PULL_DATA and PUSH_DATA)
	Payload    *Payload // JSON payload transmitted if any, nil otherwise
}

// Payload refers to the JSON payload sent by a gateway or a server.
type Payload struct {
	Raw  []byte `json:"-"`              // The raw unparsed response
	RXPK []RXPK `json:"rxpk,omitempty"` // A list of RXPK messages transmitted if any
	Stat *Stat  `json:"stat,omitempty"` // A Stat message transmitted if any
	TXPK *TXPK  `json:"txpk,omitempty"` // A TXPK message transmitted if any
}

// Available packet commands
const (
	PUSH_DATA byte = iota // Sent by the gateway for an uplink message with data
	PUSH_ACK              // Sent by the gateway's recipient in response to a PUSH_DATA
	PULL_DATA             // Sent periodically by the gateway to keep a connection open
	PULL_RESP             // Sent by the gateway's recipient to transmit back data to the Gateway
	PULL_ACK              // Sent by the gateway's recipient in response to PULL_DATA
)

// Protocol version in use
const VERSION = 0x01
<commit_msg>[router] Add DevAddr() to semtech RXPK and TXPK to determine the associated end-device address<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

// Package semtech provides useful methods and types to handle communications with a gateway.
//
// This package relies on the SemTech Protocol 1.2 accessible on github: https://github.com/TheThingsNetwork/packet_forwarder/blob/master/PROTOCOL.TXT
package semtech

import (
	"encoding/base64"
	"time"
)

// RXPK represents an uplink json message format sent by the gateway
type RXPK struct {
	Chan    *uint      `json:"chan,omitempty"` // Concentrator "IF" channel used for RX (unsigned integer)
	Codr    *string    `json:"codr,omitempty"` // LoRa ECC coding rate identifier
	Data    *string    `json:"data,omitempty"` // Base64 encoded RF packet payload, padded
	Datr    *string    `json:"-"`              // FSK datarate (unsigned in bit per second) || LoRa datarate identifier
	Freq    *float64   `json:"freq,omitempty"` // RX Central frequency in MHx (unsigned float, Hz precision)
	Lsnr    *float64   `json:"lsnr,omitempty"` // LoRa SNR ratio in dB (signed float, 0.1 dB precision)
	Modu    *string    `json:"modu,omitempty"` // Modulation identifier "LORA" or "FSK"
	Rfch    *uint      `json:"rfch,omitempty"` // Concentrator "RF chain" used for RX (unsigned integer)
	Rssi    *int       `json:"rssi,omitempty"` // RSSI in dBm (signed integer, 1 dB precision)
	Size    *uint      `json:"size,omitempty"` // RF packet payload size in bytes (unsigned integer)
	Stat    *int       `json:"stat,omitempty"` // CRC status: 1 - OK, -1 = fail, 0 = no CRC
	Time    *time.Time `json:"-"`              // UTC time of pkt RX, us precision, ISO 8601 'compact' format
	Tmst    *uint      `json:"tmst,omitempty"` // Internal timestamp of "RX finished" event (32b unsigned)
	devAddr *[4]byte   // End-Device address, according to the Data. Memoized here.
}

// DevAddr returns the end-device address described in the payload
func (rxpk *RXPK) DevAddr() *[4]byte {
	if rxpk.devAddr != nil {
		return rxpk.devAddr
	}

	if rxpk.Data == nil {
		return nil
	}

	buf, err := base64.StdEncoding.DecodeString(*rxpk.Data)
	if err != nil || len(buf) < 5 {
		return nil
	}

	rxpk.devAddr = new([4]byte)
	copy((*rxpk.devAddr)[:], buf[1:5]) // Device Address corresponds to the first 4 bytes of the Frame Header, after one byte of MAC_HEADER
	return rxpk.devAddr

}

// TXPK represents a downlink j,omitemptyson message format received by the gateway.
// Most field are optional.
type TXPK struct {
	Codr    *string    `json:"codr,omitempty"`  // LoRa ECC coding rate identifier
	Data    *string    `json:"data,omirtmepty"` // Base64 encoded RF packet payload, padding optional
	Datr    *string    `json:"-"`               // LoRa datarate identifier (eg. SF12BW500) || FSK Datarate (unsigned, in bits per second)
	Fdev    *uint      `json:"fdev,omitempty"`  // FSK frequency deviation (unsigned integer, in Hz)
	Freq    *float64   `json:"freq,omitempty"`  // TX central frequency in MHz (unsigned float, Hz precision)
	Imme    *bool      `json:"imme,omitempty"`  // Send packet immediately (will ignore tmst & time)
	Ipol    *bool      `json:"ipol,omitempty"`  // Lora modulation polarization inversion
	Modu    *string    `json:"modu,omitempty"`  // Modulation identifier "LORA" or "FSK"
	Ncrc    *bool      `json:"ncrc,omitempty"`  // If true, disable the CRC of the physical layer (optional)
	Powe    *uint      `json:"powe,omitempty"`  // TX output power in dBm (unsigned integer, dBm precision)
	Prea    *uint      `json:"prea,omitempty"`  // RF preamble size (unsigned integer)
	Rfch    *uint      `json:"rfch,omitempty"`  // Concentrator "RF chain" used for TX (unsigned integer)
	Size    *uint      `json:"size,omitempty"`  // RF packet payload size in bytes (unsigned integer)
	Time    *time.Time `json:"-"`               // Send packet at a certain time (GPS synchronization required)
	Tmst    *uint      `json:"tmst,omitempty"`  // Send packet on a certain timestamp value (will ignore time)
	devAddr *[4]byte   // End-Device address, according to the Data. Memoized here.
}

// DevAddr returns the end-device address described in the payload
func (txpk *TXPK) DevAddr() *[4]byte {
	if txpk.devAddr != nil {
		return txpk.devAddr
	}

	if txpk.Data == nil {
		return nil
	}

	buf, err := base64.StdEncoding.DecodeString(*txpk.Data)
	if err != nil || len(buf) < 5 {
		return nil
	}

	txpk.devAddr = new([4]byte)
	copy((*txpk.devAddr)[:], buf[1:5]) // Device Address corresponds to the first 4 bytes of the Frame Header, after one byte of MAC_HEADER
	return txpk.devAddr
}

// Stat represents a status json message format sent by the gateway
type Stat struct {
	Ackr *float64   `json:"ackr,omitempty"` // Percentage of upstream datagrams that were acknowledged
	Alti *int       `json:"alti,omitempty"` // GPS altitude of the gateway in meter RX (integer)
	Dwnb *uint      `json:"dwnb,omitempty"` // Number of downlink datagrams received (unsigned integer)
	Lati *float64   `json:"lati,omitempty"` // GPS latitude of the gateway in degree (float, N is +)
	Long *float64   `json:"long,omitempty"` // GPS latitude of the gateway in dgree (float, E is +)
	Rxfw *uint      `json:"rxfw,omitempty"` // Number of radio packets forwarded (unsigned integer)
	Rxnb *uint      `json:"rxnb,omitempty"` // Number of radio packets received (unsigned integer)
	Rxok *uint      `json:"rxok,omitempty"` // Number of radio packets received with a valid PHY CRC
	Time *time.Time `json:"-"`              // UTC 'system' time of the gateway, ISO 8601 'expanded' format
	Txnb *uint      `json:"txnb,omitempty"` // Number of packets emitted (unsigned integer)
}

// Packet as seen by the gateway.
type Packet struct {
	Version    byte     // Protocol version, should always be 1 here
	Token      []byte   // Random number generated by the gateway on some request. 2-bytes long.
	Identifier byte     // Packet's command identifier
	GatewayId  []byte   // Source gateway's identifier (Only PULL_DATA and PUSH_DATA)
	Payload    *Payload // JSON payload transmitted if any, nil otherwise
}

// Payload refers to the JSON payload sent by a gateway or a server.
type Payload struct {
	Raw  []byte `json:"-"`              // The raw unparsed response
	RXPK []RXPK `json:"rxpk,omitempty"` // A list of RXPK messages transmitted if any
	Stat *Stat  `json:"stat,omitempty"` // A Stat message transmitted if any
	TXPK *TXPK  `json:"txpk,omitempty"` // A TXPK message transmitted if any
}

// Available packet commands
const (
	PUSH_DATA byte = iota // Sent by the gateway for an uplink message with data
	PUSH_ACK              // Sent by the gateway's recipient in response to a PUSH_DATA
	PULL_DATA             // Sent periodically by the gateway to keep a connection open
	PULL_RESP             // Sent by the gateway's recipient to transmit back data to the Gateway
	PULL_ACK              // Sent by the gateway's recipient in response to PULL_DATA
)

// Protocol version in use
const VERSION = 0x01
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// support for running "redwood -test http://example.com"

// runURLTest prints debugging information about how the URL and its content would be rated.
func runURLTest(u string) {
	URL, err := url.Parse(u)
	if err != nil {
		fmt.Println("Could not parse the URL.")
		return
	}

	if URL.Scheme == "" {
		url2, err := url.Parse("http://" + u)
		if err == nil {
			URL = url2
		}
	}

	fmt.Println("URL:", URL)
	fmt.Println()

	sc := scorecard{
		tally: URLRules.MatchingRules(URL),
	}
	sc.calculate("")

	if len(sc.tally) == 0 {
		fmt.Println("No URL rules match.")
	} else {
		fmt.Println("The following URL rules match:")
		for s, _ := range sc.tally {
			fmt.Println(s)
		}
	}

	if len(sc.scores) > 0 {
		fmt.Println()
		fmt.Println("The request has the following category scores:")
		printSortedTally(sc.scores)
	}

	if len(sc.blocked) > 0 {
		fmt.Println()
		fmt.Println("The request is blocked by the following categories:")
		for _, c := range sc.blocked {
			fmt.Println(c)
		}
		fmt.Println()
		fmt.Println("But we'll check the content too anyway.")
	}

	if changeQuery(URL) {
		fmt.Println()
		fmt.Println("URL modified to:", URL)
	}

	fmt.Println()
	fmt.Println("Downloading content...")
	resp, err := http.Get(URL.String())
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	fmt.Println()

	contentType, action := checkContentType(resp)
	switch action {
	case ALLOW:
		fmt.Println("The content doesn't seem to be text, so not running a phrase scan.")
		return
	case BLOCK:
		fmt.Println("The content has a banned MIME type:", contentType)
		return
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error while reading response body:", err)
		return
	}

	modified := false
	charset := findCharset(resp.Header.Get("Content-Type"), content)
	if strings.Contains(contentType, "html") {
		modified = pruneContent(URL, &content, charset)
		charset = "utf-8"
	}
	if modified {
		fmt.Println("Performed content pruning.")
		fmt.Println()
	}

	scanContent(content, contentType, charset, sc.tally)
	sc.calculate("")

	if len(sc.tally) == 0 {
		fmt.Println("No content phrases match.")
	} else {
		fmt.Println("The following rules match:")
		printSortedTally(stringTally(sc.tally))
	}

	if len(sc.scores) > 0 {
		fmt.Println()
		fmt.Println("The response has the following category scores:")
		printSortedTally(sc.scores)
	}

	if len(sc.blocked) > 0 {
		fmt.Println()
		fmt.Println("The page is blocked by the following categories:")
		for _, c := range sc.blocked {
			fmt.Println(c)
		}
	}
}

// printSortedTally prints tally's keys and values in descending order by value.
func printSortedTally(tally map[string]int) {
	for _, rule := range sortedKeys(tally) {
		fmt.Println(rule, tally[rule])
	}
}
<commit_msg>Properly handle non-UTF8 charsets in test mode.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// support for running "redwood -test http://example.com"

// runURLTest prints debugging information about how the URL and its content would be rated.
func runURLTest(u string) {
	URL, err := url.Parse(u)
	if err != nil {
		fmt.Println("Could not parse the URL.")
		return
	}

	if URL.Scheme == "" {
		url2, err := url.Parse("http://" + u)
		if err == nil {
			URL = url2
		}
	}

	fmt.Println("URL:", URL)
	fmt.Println()

	sc := scorecard{
		tally: URLRules.MatchingRules(URL),
	}
	sc.calculate("")

	if len(sc.tally) == 0 {
		fmt.Println("No URL rules match.")
	} else {
		fmt.Println("The following URL rules match:")
		for s, _ := range sc.tally {
			fmt.Println(s)
		}
	}

	if len(sc.scores) > 0 {
		fmt.Println()
		fmt.Println("The request has the following category scores:")
		printSortedTally(sc.scores)
	}

	if len(sc.blocked) > 0 {
		fmt.Println()
		fmt.Println("The request is blocked by the following categories:")
		for _, c := range sc.blocked {
			fmt.Println(c)
		}
		fmt.Println()
		fmt.Println("But we'll check the content too anyway.")
	}

	if changeQuery(URL) {
		fmt.Println()
		fmt.Println("URL modified to:", URL)
	}

	fmt.Println()
	fmt.Println("Downloading content...")
	resp, err := http.Get(URL.String())
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	fmt.Println()

	contentType, action := checkContentType(resp)
	switch action {
	case ALLOW:
		fmt.Println("The content doesn't seem to be text, so not running a phrase scan.")
		return
	case BLOCK:
		fmt.Println("The content has a banned MIME type:", contentType)
		return
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error while reading response body:", err)
		return
	}

	modified := false
	charset := findCharset(resp.Header.Get("Content-Type"), content)
	if strings.Contains(contentType, "html") {
		modified = pruneContent(URL, &content, charset)
	}
	if modified {
		charset = "utf-8"
		fmt.Println("Performed content pruning.")
		fmt.Println()
	}

	scanContent(content, contentType, charset, sc.tally)
	sc.calculate("")

	if len(sc.tally) == 0 {
		fmt.Println("No content phrases match.")
	} else {
		fmt.Println("The following rules match:")
		printSortedTally(stringTally(sc.tally))
	}

	if len(sc.scores) > 0 {
		fmt.Println()
		fmt.Println("The response has the following category scores:")
		printSortedTally(sc.scores)
	}

	if len(sc.blocked) > 0 {
		fmt.Println()
		fmt.Println("The page is blocked by the following categories:")
		for _, c := range sc.blocked {
			fmt.Println(c)
		}
	}
}

// printSortedTally prints tally's keys and values in descending order by value.
func printSortedTally(tally map[string]int) {
	for _, rule := range sortedKeys(tally) {
		fmt.Println(rule, tally[rule])
	}
}
<|endoftext|>


<commit_before>// Package BattleEye doco goes here
package BattleEye

import (
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"time"
)

// Config documentation
type Config struct {
	addr     *net.UDPAddr
	Password string
	// time in seconds to wait for a response. defaults to 2.
	ConnTimeout uint32
	// time in seconds to wait for response. defaults to 1.
	ResponseTimeout uint32
	// wait time after first command response to check if multiple packets arrive.
	// defaults. 0.5s
	MultiResponseTimeout uint32
	// Time in seconds between sending a heartbeat when no commands are being sent. defaults 5
	HeartBeatTimer uint32
}

// GetConfig Returns the config to satisfy the interface for setting up a new battleeye connection
func (bec Config) GetConfig() Config {
	return bec
}

// BeConfig is the interface for passing in a configuration for the client.
// this allows other types to be implemented that also contain the type desired
type BeConfig interface {
	GetConfig() Config
}
type transmission struct {
	packet   []byte
	sequence byte
	sent     time.Time
	w        io.Writer
}

//--------------------------------------------------

// BattleEye must do doco soon
type BattleEye struct {

	// Passed in config

	password             string
	addr                 *net.UDPAddr
	connTimeout          uint32
	responseTimeout      uint32
	multiResponseTimeout uint32
	heartbeatTimer       uint32

	// Sequence byte to determine the packet we are up to in the chain.
	sequence struct {
		sync.Locker
		n byte
	}
	chatWriter  io.Writer
	writebuffer []byte

	conn              *net.UDPConn
	lastCommandPacket struct {
		sync.Locker
		time.Time
	}
	running bool
	wg      sync.WaitGroup

	// use this to unlock before reading.
	// and match reads to waiting confirms to purge this list.
	// or possibly resend
	packetQueue struct {
		sync.Locker
		queue []transmission
	}
}

// New Creates and Returns a new Client
func New(config BeConfig) *BattleEye {
	// setup all variables
	cfg := config.GetConfig()
	if cfg.ConnTimeout == 0 {
		cfg.ConnTimeout = 2
	}
	if cfg.ResponseTimeout == 0 {
		cfg.ResponseTimeout = 1
	}
	if cfg.HeartBeatTimer == 0 {
		cfg.HeartBeatTimer = 5
	}

	return &BattleEye{
		password:        cfg.Password,
		addr:            cfg.addr,
		connTimeout:     cfg.ConnTimeout,
		responseTimeout: cfg.ResponseTimeout,
		heartbeatTimer:  cfg.HeartBeatTimer,
		writebuffer:     make([]byte, 4096),
	}

}

// SendCommand takes a byte array of a command string i.e 'ban xyz' and a io.Writer, it will
// Make sure the server recieves the command by retrying if needed and write the response to the writer.
// if no response is recieved then a response has not yet been recieved. a empty write
func (be *BattleEye) SendCommand(command []byte, w io.Writer) error {
	be.sequence.Lock()
	sequence := be.sequence.n
	// increment the sending packet.
	if be.sequence.n == 255 {
		be.sequence.n = 0
	} else {
		be.sequence.n++
	}
	be.sequence.Unlock()

	packet := buildCommandPacket(command, sequence)
	be.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(be.responseTimeout)))
	be.conn.Write(packet)

	be.lastCommandPacket.Lock()
	be.lastCommandPacket.Time = time.Now()
	be.lastCommandPacket.Unlock()

	/*
		be.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(be.responseTimeout)))

		// have to somehow look for multi Packet with this shit,
		// and handle when i am reading irelevent information.
		n, err := be.conn.Read(be.writebuffer)
		if err != nil {
			return err
		}
		w.Write(be.writebuffer[:n])
	*/

	return nil
}

// Connect attempts to establish a connection with the BattlEye Rcon server and if it works it then sets up a loop in a goroutine
// to recieve all callbacks.
func (be *BattleEye) Connect() (bool, error) {
	be.wg = sync.WaitGroup{}
	var err error
	// dial the Address
	be.conn, err = net.DialUDP("udp", nil, be.addr)
	if err != nil {
		return false, err
	}
	// make a buffer to read the packet packed with extra space
	packet := make([]byte, 9)

	// set timeout deadline so we dont block forever
	be.conn.SetReadDeadline(time.Now().Add(time.Second * 2))
	// Send a Connection Packet
	be.conn.Write(buildConnectionPacket(be.password))
	// Read connection and hope it doesn't time out and the server responds
	n, err := be.conn.Read(packet)
	// check if this is a timeout error.
	if err, ok := err.(net.Error); ok && err.Timeout() {
		return false, errors.New("Connection Timed Out")
	}
	if err != nil {
		return false, err
	}

	result, err := checkLogin(packet[:n])
	if err != nil {
		return false, err
	}

	if result == packetResponse.LoginFail {
		return false, nil
	}

	// nothing has failed we are good to go :).
	// Spin up a go routine to read back on a connection
	be.wg.Add(1)
	//go
	return true, nil
}

func (be *BattleEye) updateLoop() {
	defer be.wg.Done()
	for {
		if be.conn == nil {
			return
		}
		t := time.Now()

		be.lastCommandPacket.Lock()
		if t.After(be.lastCommandPacket.Add(time.Second * time.Duration(be.heartbeatTimer))) {
			err := be.SendCommand([]byte{}, ioutil.Discard)
			if err != nil {
				return
			}
			be.lastCommandPacket.Time = t
		}
		be.lastCommandPacket.Unlock()

		// do check for new incoming data
		be.conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
		n, err := be.conn.Read(be.writebuffer)
		if err != nil {
			continue
		}
		data := be.writebuffer[:n]
		be.processPacket(data)
	}
}
func (be *BattleEye) processPacket(data []byte) {
	// validate packet is good.
	checksum, err := getCheckSumFromBEPacket(data)
	if err != nil {
		return
	}

	pType, err := responseType(data)
	if err != nil {
		return
	}

	match := dataMatchesCheckSum(data, checksum)
	// if invalid data for checksum we got something corrupt
	if !match {
		return
	}
	// Get Sequence
	sequence, err := getSequenceFromPacket(data)
	if err != nil {
		return
	}

	// Strip Header
	content, err := stripHeader(data)
	if err != nil {
		// maybe should log this shit somewhere
		return
	}
	// if say command write acknoledge and leave
	if pType == packetType.ServerMessage {
		be.sequence.Lock()
		if sequence >= be.sequence.n {
			// not sure how byte overflow is handled in golang... not sure if it would roll over to 0 or throw and error.
			if sequence == 255 {
				be.sequence.n = 0x00
			} else {
				be.sequence.n = sequence + 1
			}
		}
		be.sequence.Unlock()
		be.chatWriter.Write(content)
		// we must acknoledge we recieved this first
		be.conn.Write(buildPacket([]byte{sequence}, packetType.ServerMessage))
		return
	}

	// else for command check if we expect more packets and how many.
	if pType != packetType.Command {
		return
	}
	packetCount, currentPacket, isMultiPacket := checkMultiPacketResponse(content)
	// process the packet if it is not a multipacket
	if !isMultiPacket {
		be.handleResponseToQueue(sequence, content[2:])
	}
	// loop till we have all the messages and i guess send confirms back.
	for ; packetCount < currentPacket; packetCount++ {
		be.conn.SetReadDeadline(time.Now().Add(time.Second))
		n, err := be.conn.Read(be.writebuffer)
		if err != nil {
			return
		}
		p := be.writebuffer[:n]

	}
}

// Disconnect shuts down the infinite loop to recieve packets and closes the connection
func (be *BattleEye) Disconnect() error {
	// maybe also close the main loop and wait for that?
	be.conn.Close()
	be.wg.Wait()
	return nil
}

func checkLogin(packet []byte) (byte, error) {
	var err error
	if len(packet) != 9 {
		return 0, errors.New("Packet Size Invalid for Response")
	}
	// check if we have a valid packet
	if match, err := packetMatchesChecksum(packet); match == false || err != nil {
		return 0, err
	}
	// now check if we got a success or a fail
	// 2 byte prefix. 4 byte checksum. 1 byte terminate header. 1 byte login type. 1 byte result
	return packet[8], err
}

func (be *BattleEye) handleResponseToQueue(sequence byte, response []byte) {
	be.packetQueue.Lock()
	for k, v := range be.packetQueue.queue {
		if v.sequence == sequence {
			v.w.Write(response)

			be.packetQueue.queue = append(be.packetQueue.queue[:k], be.packetQueue.queue[k+1:]...)
			break
		}
	}
	be.packetQueue.Unlock()
}
<commit_msg>should now successfully send and receive packets and wait on packets from a queue and if they aren't received the command is resent till it is received. This allows assurance that a command will be sent to the server even if the connection drops presuming that a connection is reestablished while it is still running.<commit_after>// Package BattleEye doco goes here
package BattleEye

import (
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"time"
)

// Config documentation
type Config struct {
	addr     *net.UDPAddr
	Password string
	// time in seconds to wait for a response. defaults to 2.
	ConnTimeout uint32
	// time in seconds to wait for response. defaults to 1.
	ResponseTimeout uint32
	// wait time after first command response to check if multiple packets arrive.
	// defaults. 0.5s
	MultiResponseTimeout uint32
	// Time in seconds between sending a heartbeat when no commands are being sent. defaults 5
	HeartBeatTimer uint32
}

// GetConfig Returns the config to satisfy the interface for setting up a new battleeye connection
func (bec Config) GetConfig() Config {
	return bec
}

// BeConfig is the interface for passing in a configuration for the client.
// this allows other types to be implemented that also contain the type desired
type BeConfig interface {
	GetConfig() Config
}
type transmission struct {
	packet   []byte
	sequence byte
	sent     time.Time
	w        io.Writer
}

//--------------------------------------------------

// BattleEye must do doco soon
type BattleEye struct {

	// Passed in config

	password             string
	addr                 *net.UDPAddr
	connTimeout          uint32
	responseTimeout      uint32
	multiResponseTimeout uint32
	heartbeatTimer       uint32

	// Sequence byte to determine the packet we are up to in the chain.
	sequence struct {
		sync.Locker
		n byte
	}
	chatWriter  io.Writer
	writebuffer []byte

	conn              *net.UDPConn
	lastCommandPacket struct {
		sync.Locker
		time.Time
	}
	running bool
	wg      sync.WaitGroup

	// use this to unlock before reading.
	// and match reads to waiting confirms to purge this list.
	// or possibly resend
	packetQueue struct {
		sync.Locker
		queue []transmission
	}
}

// New Creates and Returns a new Client
func New(config BeConfig) *BattleEye {
	// setup all variables
	cfg := config.GetConfig()
	if cfg.ConnTimeout == 0 {
		cfg.ConnTimeout = 2
	}
	if cfg.ResponseTimeout == 0 {
		cfg.ResponseTimeout = 1
	}
	if cfg.HeartBeatTimer == 0 {
		cfg.HeartBeatTimer = 5
	}

	return &BattleEye{
		password:        cfg.Password,
		addr:            cfg.addr,
		connTimeout:     cfg.ConnTimeout,
		responseTimeout: cfg.ResponseTimeout,
		heartbeatTimer:  cfg.HeartBeatTimer,
		writebuffer:     make([]byte, 4096),
	}

}

// SendCommand takes a byte array of a command string i.e 'ban xyz' and a io.Writer, it will
// Make sure the server recieves the command by retrying if needed and write the response to the writer.
// if no response is recieved then a response has not yet been recieved. a empty write
func (be *BattleEye) SendCommand(command []byte, w io.Writer) error {
	be.sequence.Lock()
	sequence := be.sequence.n
	// increment the sending packet.
	if be.sequence.n == 255 {
		be.sequence.n = 0
	} else {
		be.sequence.n++
	}
	be.sequence.Unlock()

	packet := buildCommandPacket(command, sequence)
	be.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(be.responseTimeout)))
	be.conn.Write(packet)

	be.lastCommandPacket.Lock()
	be.lastCommandPacket.Time = time.Now()
	be.lastCommandPacket.Unlock()

	/*
		be.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(be.responseTimeout)))

		// have to somehow look for multi Packet with this shit,
		// and handle when i am reading irelevent information.
		n, err := be.conn.Read(be.writebuffer)
		if err != nil {
			return err
		}
		w.Write(be.writebuffer[:n])
	*/

	return nil
}

// Connect attempts to establish a connection with the BattlEye Rcon server and if it works it then sets up a loop in a goroutine
// to recieve all callbacks.
func (be *BattleEye) Connect() (bool, error) {
	be.wg = sync.WaitGroup{}
	var err error
	// dial the Address
	be.conn, err = net.DialUDP("udp", nil, be.addr)
	if err != nil {
		return false, err
	}
	// make a buffer to read the packet packed with extra space
	packet := make([]byte, 9)

	// set timeout deadline so we dont block forever
	be.conn.SetReadDeadline(time.Now().Add(time.Second * 2))
	// Send a Connection Packet
	be.conn.Write(buildConnectionPacket(be.password))
	// Read connection and hope it doesn't time out and the server responds
	n, err := be.conn.Read(packet)
	// check if this is a timeout error.
	if err, ok := err.(net.Error); ok && err.Timeout() {
		return false, errors.New("Connection Timed Out")
	}
	if err != nil {
		return false, err
	}

	result, err := checkLogin(packet[:n])
	if err != nil {
		return false, err
	}

	if result == packetResponse.LoginFail {
		return false, nil
	}

	// nothing has failed we are good to go :).
	// Spin up a go routine to read back on a connection
	be.wg.Add(1)
	//go
	return true, nil
}

func (be *BattleEye) updateLoop() {
	defer be.wg.Done()
	for {
		if be.conn == nil {
			return
		}
		t := time.Now()

		be.lastCommandPacket.Lock()
		if t.After(be.lastCommandPacket.Add(time.Second * time.Duration(be.heartbeatTimer))) {
			err := be.SendCommand([]byte{}, ioutil.Discard)
			if err != nil {
				return
			}
			be.lastCommandPacket.Time = t
		}
		be.lastCommandPacket.Unlock()

		// do check for new incoming data
		be.conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
		n, err := be.conn.Read(be.writebuffer)
		if err != nil {
			continue
		}
		data := be.writebuffer[:n]
		be.processPacket(data)
	}
}
func (be *BattleEye) processPacket(data []byte) {
	sequence, content, pType, err := verifyPacket(data)

	if err != nil {
		// maybe should log this shit somewhere
		return
	}
	// if say command write acknoledge and leave
	if pType == packetType.ServerMessage {
		be.sequence.Lock()
		if sequence >= be.sequence.n {
			// not sure how byte overflow is handled in golang... not sure if it would roll over to 0 or throw and error.
			if sequence == 255 {
				be.sequence.n = 0x00
			} else {
				be.sequence.n = sequence + 1
			}
		}
		be.sequence.Unlock()
		be.chatWriter.Write(content)
		// we must acknoledge we recieved this first
		be.conn.Write(buildPacket([]byte{sequence}, packetType.ServerMessage))
		return
	}

	// else for command check if we expect more packets and how many.
	if pType != packetType.Command {
		return
	}
	packetCount, currentPacket, isMultiPacket := checkMultiPacketResponse(content)
	// process the packet if it is not a multipacket
	if !isMultiPacket {
		be.handleResponseToQueue(sequence, content[2:], false)
		return
	}
	// loop till we have all the messages and i guess send confirms back.
	for ; packetCount < currentPacket; packetCount++ {
		be.conn.SetReadDeadline(time.Now().Add(time.Second))
		n, err := be.conn.Read(be.writebuffer)
		if err != nil {
			return
		}
		// lets re verify this entire thing
		p := be.writebuffer[:n]
		seq, cont, _, err := verifyPacket(p)
		if err != nil {
			return
		}
		if seq != sequence {
			be.processPacket(p)
			packetCount--
		}
		be.handleResponseToQueue(seq, cont[2:], true)

	}
	be.handleResponseToQueue(sequence, []byte{}, false)
	// now that we have goten all the packets we are after and writen them to the buffer lets return the result.
}

// Disconnect shuts down the infinite loop to recieve packets and closes the connection
func (be *BattleEye) Disconnect() error {
	// maybe also close the main loop and wait for that?
	be.conn.Close()
	be.wg.Wait()
	return nil
}

func checkLogin(packet []byte) (byte, error) {
	var err error
	if len(packet) != 9 {
		return 0, errors.New("Packet Size Invalid for Response")
	}
	// check if we have a valid packet
	if match, err := packetMatchesChecksum(packet); match == false || err != nil {
		return 0, err
	}
	// now check if we got a success or a fail
	// 2 byte prefix. 4 byte checksum. 1 byte terminate header. 1 byte login type. 1 byte result
	return packet[8], err
}

func (be *BattleEye) handleResponseToQueue(sequence byte, response []byte, moreToCome bool) {
	be.packetQueue.Lock()
	for k, v := range be.packetQueue.queue {
		if v.sequence == sequence {
			v.w.Write(response)
			if !moreToCome {
				be.packetQueue.queue = append(be.packetQueue.queue[:k], be.packetQueue.queue[k+1:]...)
			}

			break
		}
	}
	be.packetQueue.Unlock()
}

func verifyPacket(data []byte) (sequence byte, content []byte, pType byte, err error) {
	checksum, err := getCheckSumFromBEPacket(data)
	if err != nil {
		return
	}

	match := dataMatchesCheckSum(data, checksum)
	if !match {
		err = errors.New("Checksum does not match data")
		return
	}
	sequence, err = getSequenceFromPacket(data)
	if err != nil {
		return
	}

	content, err = stripHeader(data)
	if err != nil {
		return
	}
	pType, err = responseType(data)

	return
}
<|endoftext|>


<commit_before>package myaws

import (
	"fmt"
	"strings"

	"github.com/aws/aws-sdk-go/service/ecs"
)

// ECSNodeLsOptions customize the behavior of the Ls command.
type ECSNodeLsOptions struct {
	Cluster string
}

// ECSNodeLs describes ECS container instances.
func (client *Client) ECSNodeLs(options ECSNodeLsOptions) error {
	instances, err := client.findECSNodes(options.Cluster)
	if err != nil {
		return err
	}

	for _, instance := range instances {
		fmt.Fprintln(client.stdout, formatECSNode(client, options, instance))
	}

	return nil
}

func formatECSNode(client *Client, options ECSNodeLsOptions, instance *ecs.ContainerInstance) string {
	arn := strings.Split(*instance.ContainerInstanceArn, "/")

	// To fix misalignment, we use the width of state is 10 characters here,
	// because 8 characters + 2 characters as future margin of change.
	// The valid values of status are ACTIVE, INACTIVE, or DRAINING.
	return fmt.Sprintf("%s\t%s\t%-10s\t%d\t%d\t%s",
		arn[1],
		*instance.Ec2InstanceId,
		*instance.Status,
		*instance.RunningTasksCount,
		*instance.PendingTasksCount,
		client.FormatTime(instance.RegisteredAt),
	)
}
<commit_msg>Drop unused an argument from formatECSNode<commit_after>package myaws

import (
	"fmt"
	"strings"

	"github.com/aws/aws-sdk-go/service/ecs"
)

// ECSNodeLsOptions customize the behavior of the Ls command.
type ECSNodeLsOptions struct {
	Cluster string
}

// ECSNodeLs describes ECS container instances.
func (client *Client) ECSNodeLs(options ECSNodeLsOptions) error {
	instances, err := client.findECSNodes(options.Cluster)
	if err != nil {
		return err
	}

	for _, instance := range instances {
		fmt.Fprintln(client.stdout, formatECSNode(client, instance))
	}

	return nil
}

func formatECSNode(client *Client, instance *ecs.ContainerInstance) string {
	arn := strings.Split(*instance.ContainerInstanceArn, "/")

	// To fix misalignment, we use the width of state is 10 characters here,
	// because 8 characters + 2 characters as future margin of change.
	// The valid values of status are ACTIVE, INACTIVE, or DRAINING.
	return fmt.Sprintf("%s\t%s\t%-10s\t%d\t%d\t%s",
		arn[1],
		*instance.Ec2InstanceId,
		*instance.Status,
		*instance.RunningTasksCount,
		*instance.PendingTasksCount,
		client.FormatTime(instance.RegisteredAt),
	)
}
<|endoftext|>


<commit_before>// 30 july 2014

package ui

// #include "winapi_windows.h"
import "C"

type controlbase struct {
	*controldefs
	hwnd	C.HWND
	parent	C.HWND		// for Tab and Group
	textlen	C.LONG
}

type controlParent struct {
	hwnd	C.HWND
}

func newControl(class C.LPWSTR, style C.DWORD, extstyle C.DWORD) *controlbase {
	c := new(controlbase)
	c.hwnd = C.newWidget(class, style, extstyle)
	c.controldefs = new(controldefs)
	c.fsetParent = func(p *controlParent) {
		C.controlSetParent(c.hwnd, p.hwnd)
		c.parent = p.hwnd
	}
	c.fcontainerShow = func() {
		C.ShowWindow(c.hwnd, C.SW_SHOW)
	}
	c.fcontainerHide = func() {
		C.ShowWindow(c.hwnd, C.SW_HIDE)
	}
	c.fallocate = baseallocate(c)
	c.fpreferredSize = func(d *sizing) (int, int) {
		// TODO
		return 75, 23
	}
	c.fcommitResize = func(a *allocation, d *sizing) {
		C.moveWindow(c.hwnd, C.int(a.x), C.int(a.y), C.int(a.width), C.int(a.height))
	}
	c.fgetAuxResizeInfo = func(d *sizing) {
		// do nothing
	}
	return c
}

// these are provided for convenience

func (c *controlbase) text() string {
	return getWindowText(c.hwnd)
}

func (c *controlbase) setText(text string) {
	t := toUTF16(text)
	C.setWindowText(c.hwnd, t)
	c.textlen = C.controlTextLength(c.hwnd, t)
}
<commit_msg>Removed the generic preferredSize() from the Windows backend; all controls are now expected to provide their own on Windows (this is really the only way to go there). NOW I can rework the whole control nonsense...<commit_after>// 30 july 2014

package ui

// #include "winapi_windows.h"
import "C"

type controlbase struct {
	*controldefs
	hwnd	C.HWND
	parent	C.HWND		// for Tab and Group
	textlen	C.LONG
}

type controlParent struct {
	hwnd	C.HWND
}

func newControl(class C.LPWSTR, style C.DWORD, extstyle C.DWORD) *controlbase {
	c := new(controlbase)
	c.hwnd = C.newWidget(class, style, extstyle)
	c.controldefs = new(controldefs)
	c.fsetParent = func(p *controlParent) {
		C.controlSetParent(c.hwnd, p.hwnd)
		c.parent = p.hwnd
	}
	c.fcontainerShow = func() {
		C.ShowWindow(c.hwnd, C.SW_SHOW)
	}
	c.fcontainerHide = func() {
		C.ShowWindow(c.hwnd, C.SW_HIDE)
	}
	c.fallocate = baseallocate(c)
	// don't specify c.fpreferredSize; it is custom on ALL controls
	c.fcommitResize = func(a *allocation, d *sizing) {
		C.moveWindow(c.hwnd, C.int(a.x), C.int(a.y), C.int(a.width), C.int(a.height))
	}
	c.fgetAuxResizeInfo = func(d *sizing) {
		// do nothing
	}
	return c
}

// these are provided for convenience

func (c *controlbase) text() string {
	return getWindowText(c.hwnd)
}

func (c *controlbase) setText(text string) {
	t := toUTF16(text)
	C.setWindowText(c.hwnd, t)
	c.textlen = C.controlTextLength(c.hwnd, t)
}
<|endoftext|>


<commit_before>package resp2

import (
	"bufio"
	"fmt"
	"strings"
	"testing"
)

func BenchmarkIntUnmarshalRESP(b *testing.B) {
	tests := []struct {
		In string
	}{
		{"-1"},
		{"-123"},
		{"1"},
		{"123"},
		{"+1"},
		{"+123"},
	}

	for _, test := range tests {
		input := ":" + test.In + "\r\n"

		b.Run(fmt.Sprint(test.In), func(b *testing.B) {
			var sr strings.Reader
			br := bufio.NewReader(&sr)

			for i := 0; i < b.N; i++ {
				sr.Reset(input)
				br.Reset(&sr)

				var i Int
				if err := i.UnmarshalRESP(br); err != nil {
					b.Fatalf("failed to unmarshal %q: %s", input, err)
				}
			}
		})
	}
}
<commit_msg>Add benchmark for struct unmarshalling<commit_after>package resp2

import (
	"bufio"
	"fmt"
	"strings"
	"testing"
)

func BenchmarkIntUnmarshalRESP(b *testing.B) {
	tests := []struct {
		In string
	}{
		{"-1"},
		{"-123"},
		{"1"},
		{"123"},
		{"+1"},
		{"+123"},
	}

	for _, test := range tests {
		input := ":" + test.In + "\r\n"

		b.Run(fmt.Sprint(test.In), func(b *testing.B) {
			var sr strings.Reader
			br := bufio.NewReader(&sr)

			for i := 0; i < b.N; i++ {
				sr.Reset(input)
				br.Reset(&sr)

				var i Int
				if err := i.UnmarshalRESP(br); err != nil {
					b.Fatalf("failed to unmarshal %q: %s", input, err)
				}
			}
		})
	}
}

func BenchmarkAnyUnmarshalRESP(b *testing.B) {
	b.Run("Struct", func(b *testing.B) {
		b.ReportAllocs()

		const input = "*8\r\n" +
			"$3\r\nFoo\r\n" + ":1\r\n" +
			"$3\r\nBAZ\r\n" + "$1\r\n3\r\n" +
			"$3\r\nBoz\r\n" + ":5\r\n" +
			"$3\r\nBiz\r\n" + "$2\r\n10\r\n"

		var sr strings.Reader
		br := bufio.NewReader(&sr)

		for i := 0; i < b.N; i++ {
			sr.Reset(input)
			br.Reset(&sr)

			var s testStructA
			if err := (Any{I: &s}).UnmarshalRESP(br); err != nil {
				b.Fatalf("failed to unmarshal %q: %s", input, err)
			}
		}
	})
}<|endoftext|>


<commit_before>package cli

import (
	"flag"
	"github.com/masterminds/cookoo"
	//"os"
	"strings"
	//"fmt"
)

type RequestResolver struct {
	registry *cookoo.Registry
}

func (r *RequestResolver) Init(registry *cookoo.Registry) {
	r.registry = registry
}

func (r *RequestResolver) Resolve(path string, cxt cookoo.Context) (string, error) {
	// Parse out any flags. Maybe flag specs are in context?

	flagsetO, ok := cxt.Has("globalFlags")
	if !ok {
		// No args to parse. Just return path.
		return path, nil
	}
	flagset := flagsetO.(*flag.FlagSet)
	flagset.Parse(strings.Split(path, " "));
	r.addFlagsToContext(flagset, cxt)
	args := flagset.Args()

	// This is a failure condition... Need to fix Cookoo to support error return.
	if len(args) == 0 {
		return path, &cookoo.RouteError{"Could not resolve route " + path}
	}

	// Add the rest of the args to the context.
	cxt.Add("args", args[1:])

	// Parse argv[0] as subcommand
	return args[0], nil
}

func (r *RequestResolver) addFlagsToContext(flagset *flag.FlagSet, cxt cookoo.Context) {
	store := func(f *flag.Flag) {
		// fmt.Printf("Storing %s in context with value %s.\n", f.Name, f.Value.String())
		cxt.Add(f.Name, f)
	}

	flagset.VisitAll(store)
}
<commit_msg>Minor refacgor of requestresolver.<commit_after>package cli

import (
	"flag"
	"github.com/masterminds/cookoo"
	//"os"
	"strings"
	//"fmt"
)

type RequestResolver struct {
	registry *cookoo.Registry
}

func (r *RequestResolver) Init(registry *cookoo.Registry) {
	r.registry = registry
}

func (r *RequestResolver) Resolve(path string, cxt cookoo.Context) (string, error) {
	// Parse out any flags. Maybe flag specs are in context?

	flagsetO, ok := cxt.Has("globalFlags")
	if !ok {
		// No args to parse. Just return path.
		return path, nil
	}
	flagset := flagsetO.(*flag.FlagSet)
	flagset.Parse(strings.Split(path, " "));
	addFlagsToContext(flagset, cxt)
	args := flagset.Args()

	// This is a failure condition... Need to fix Cookoo to support error return.
	if len(args) == 0 {
		return path, &cookoo.RouteError{"Could not resolve route " + path}
	}

	// Add the rest of the args to the context.
	cxt.Add("args", args[1:])

	// Parse argv[0] as subcommand
	return args[0], nil
}

func addFlagsToContext(flagset *flag.FlagSet, cxt cookoo.Context) {
	store := func(f *flag.Flag) {
		// fmt.Printf("Storing %s in context with value %s.\n", f.Name, f.Value.String())
		cxt.Add(f.Name, f)
	}

	flagset.VisitAll(store)
}
<|endoftext|>


<commit_before>package ui

import (
	"strings"
	"sync"
	"time"

	termui "github.com/gizak/termui"
	"github.com/nsf/termbox-go"
)

// Screen is thin wrapper aroung Termbox library to provide basic display
// capabilities as required by dry.
type Screen struct {
	Width    int     // Current number of columns.
	Height   int     // Current number of rows.
	markup   *Markup // Pointer to markup processor (gets created by screen).
	pausedAt *time.Time
	Cursor   *Cursor // Pointer to cursor (gets created by screen).
	sync.RWMutex
	theme *ColorTheme
}

//Cursor represents the cursor position on the screen
type Cursor struct {
	line int
	sync.RWMutex
}

//NewScreen initializes Termbox, creates screen along with layout and markup, and
//calculates current screen dimensions. Once initialized the screen is
//ready for display.
func NewScreen(theme *ColorTheme) *Screen {

	if err := termbox.Init(); err != nil {
		panic(err)
	}
	termbox.SetOutputMode(termbox.Output256)
	screen := &Screen{}
	screen.markup = NewMarkup(theme)
	screen.Cursor = &Cursor{line: 0}
	screen.theme = theme
	return screen.Resize()
}

// Close gets called upon program termination to close the Termbox.
func (screen *Screen) Close() *Screen {
	termbox.Close()
	return screen
}

// Resize gets called when the screen is being resized. It recalculates screen
// dimensions and requests to clear the screen on next update.
func (screen *Screen) Resize() *Screen {
	screen.Width, screen.Height = termbox.Size()
	return screen
}

//Clear makes the entire screen blank using default background color.
func (screen *Screen) Clear() *Screen {
	screen.RLock()
	defer screen.RUnlock()
	termbox.Clear(termbox.Attribute(screen.theme.Fg), termbox.Attribute(screen.theme.Bg))
	return screen
}

//ClearAndFlush cleares the screen and then flushes internal buffers
func (screen *Screen) ClearAndFlush() *Screen {
	screen.Clear()
	screen.Flush()
	return screen
}

// Sync forces a complete resync between the termbox and a terminal.
func (screen *Screen) Sync() *Screen {
	screen.Lock()
	defer screen.Unlock()
	termbox.Sync()
	return screen
}

// ClearLine erases the contents of the line starting from (x,y) coordinate
// till the end of the line.
func (screen *Screen) ClearLine(x int, y int) *Screen {
	screen.RLock()
	defer screen.RUnlock()
	for i := x; i < screen.Width; i++ {
		termbox.SetCell(i, y, ' ', termbox.Attribute(screen.theme.Fg), termbox.Attribute(screen.theme.Bg))
	}
	screen.Flush()

	return screen
}

func (screen *Screen) ColorTheme(theme *ColorTheme) *Screen {
	screen.Lock()
	defer screen.Unlock()
	screen.markup = NewMarkup(theme)
	return screen
}

//Flush synchronizes the internal buffer with the terminal.
func (screen *Screen) Flush() *Screen {
	screen.RLock()
	defer screen.RUnlock()
	termbox.Flush()
	return screen
}

//Position tells on which screen line the cursor is
func (cursor *Cursor) Position() int {
	cursor.RLock()
	defer cursor.RUnlock()
	return cursor.line
}

// RenderBufferer renders all Bufferer in the given order from left to right,
// right could overlap on left ones.
// This allows usage of termui widgets.
func (screen *Screen) RenderBufferer(bs ...termui.Bufferer) {
	screen.Lock()
	defer screen.Unlock()
	for _, b := range bs {
		buf := b.Buffer()
		// set cels in buf
		for p, c := range buf.CellMap {
			if p.In(buf.Area) {
				termbox.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
			}
		}
	}
	termbox.Flush()
}

// RenderLine takes the incoming string, tokenizes it to extract markup
// elements, and displays it all starting at (x,y) location.
func (screen *Screen) RenderLine(x int, y int, str string) {
	screen.Lock()
	defer screen.Unlock()

	start, column := 0, 0
	for _, token := range Tokenize(str, supportedTags) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: displays it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, screen.markup.Background)
		}
	}
}

//RenderLineWithBackGround does what RenderLine does but rendering the line
//with the given background color
func (screen *Screen) RenderLineWithBackGround(x int, y int, str string, bgColor Color) {
	screen.Lock()
	defer screen.Unlock()
	start, column := 0, 0
	if x > 0 {
		fill(0, y, x, y, termbox.Cell{Ch: ' ', Bg: termbox.Attribute(bgColor)})
	}
	for _, token := range Tokenize(str, supportedTags) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: display it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, termbox.Attribute(bgColor))
		}
	}
	fill(start+1, y, screen.Width, y, termbox.Cell{Ch: ' ', Bg: termbox.Attribute(bgColor)})
}

//Render renders the given content starting from the given row
func (screen *Screen) Render(row int, str string) {
	screen.RenderAtColumn(0, row, str)
}

//RenderAtColumn renders the given content starting from
//the given row at the given column
func (screen *Screen) RenderAtColumn(column, initialRow int, str string) {
	for row, line := range strings.Split(str, "\n") {
		screen.RenderLine(column, initialRow+row, line)
	}
}

//RenderRenderer renders the given renderer starting from the given row
func (screen *Screen) RenderRenderer(row int, renderer Renderer) {
	screen.Render(row, renderer.Render())
}

//Reset sets the cursor in the initial position
func (cursor *Cursor) Reset() {
	cursor.Lock()
	defer cursor.Unlock()
	cursor.line = 0
}

//ScrollCursorDown moves the cursor to the line below the current one
func (cursor *Cursor) ScrollCursorDown() {
	cursor.Lock()
	defer cursor.Unlock()
	cursor.line = cursor.line + 1
}

//ScrollCursorUp moves the cursor to the line above the current one
func (cursor *Cursor) ScrollCursorUp() {
	cursor.Lock()
	defer cursor.Unlock()
	if cursor.line > 0 {
		cursor.line = cursor.line - 1
	} else {
		cursor.line = 0
	}
}

//ScrollTo moves the cursor to the given line
func (cursor *Cursor) ScrollTo(pos int) {
	cursor.Lock()
	defer cursor.Unlock()
	cursor.line = pos

}

func toTmAttr(x termui.Attribute) termbox.Attribute {
	return termbox.Attribute(x)
}
<commit_msg>Lock screen on Clear and Flush operations<commit_after>package ui

import (
	"strings"
	"sync"
	"time"

	termui "github.com/gizak/termui"
	"github.com/nsf/termbox-go"
)

// Screen is thin wrapper aroung Termbox library to provide basic display
// capabilities as required by dry.
type Screen struct {
	Width    int     // Current number of columns.
	Height   int     // Current number of rows.
	markup   *Markup // Pointer to markup processor (gets created by screen).
	pausedAt *time.Time
	Cursor   *Cursor // Pointer to cursor (gets created by screen).
	sync.RWMutex
	theme *ColorTheme
}

//Cursor represents the cursor position on the screen
type Cursor struct {
	line int
	sync.RWMutex
}

//NewScreen initializes Termbox, creates screen along with layout and markup, and
//calculates current screen dimensions. Once initialized the screen is
//ready for display.
func NewScreen(theme *ColorTheme) *Screen {

	if err := termbox.Init(); err != nil {
		panic(err)
	}
	termbox.SetOutputMode(termbox.Output256)
	screen := &Screen{}
	screen.markup = NewMarkup(theme)
	screen.Cursor = &Cursor{line: 0}
	screen.theme = theme
	return screen.Resize()
}

// Close gets called upon program termination to close the Termbox.
func (screen *Screen) Close() *Screen {
	termbox.Close()
	return screen
}

// Resize gets called when the screen is being resized. It recalculates screen
// dimensions and requests to clear the screen on next update.
func (screen *Screen) Resize() *Screen {
	screen.Width, screen.Height = termbox.Size()
	return screen
}

//Clear makes the entire screen blank using default background color.
func (screen *Screen) Clear() *Screen {
	screen.Lock()
	defer screen.Unlock()
	termbox.Clear(termbox.Attribute(screen.theme.Fg), termbox.Attribute(screen.theme.Bg))
	return screen
}

//ClearAndFlush cleares the screen and then flushes internal buffers
func (screen *Screen) ClearAndFlush() *Screen {
	screen.Clear()
	screen.Flush()
	return screen
}

// Sync forces a complete resync between the termbox and a terminal.
func (screen *Screen) Sync() *Screen {
	screen.Lock()
	defer screen.Unlock()
	termbox.Sync()
	return screen
}

// ClearLine erases the contents of the line starting from (x,y) coordinate
// till the end of the line.
func (screen *Screen) ClearLine(x int, y int) *Screen {
	screen.RLock()
	defer screen.RUnlock()
	for i := x; i < screen.Width; i++ {
		termbox.SetCell(i, y, ' ', termbox.Attribute(screen.theme.Fg), termbox.Attribute(screen.theme.Bg))
	}
	screen.Flush()

	return screen
}

//ColorTheme changes the color theme of the screen to the given one.
func (screen *Screen) ColorTheme(theme *ColorTheme) *Screen {
	screen.Lock()
	defer screen.Unlock()
	screen.markup = NewMarkup(theme)
	return screen
}

//Flush synchronizes the internal buffer with the terminal.
func (screen *Screen) Flush() *Screen {
	screen.Lock()
	defer screen.Unlock()
	termbox.Flush()
	return screen
}

//Position tells on which screen line the cursor is
func (cursor *Cursor) Position() int {
	cursor.RLock()
	defer cursor.RUnlock()
	return cursor.line
}

// RenderBufferer renders all Bufferer in the given order from left to right,
// right could overlap on left ones.
// This allows usage of termui widgets.
func (screen *Screen) RenderBufferer(bs ...termui.Bufferer) {
	screen.Lock()
	defer screen.Unlock()
	for _, b := range bs {
		buf := b.Buffer()
		// set cels in buf
		for p, c := range buf.CellMap {
			if p.In(buf.Area) {
				termbox.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
			}
		}
	}
	termbox.Flush()
}

// RenderLine takes the incoming string, tokenizes it to extract markup
// elements, and displays it all starting at (x,y) location.
func (screen *Screen) RenderLine(x int, y int, str string) {
	screen.Lock()
	defer screen.Unlock()

	start, column := 0, 0
	for _, token := range Tokenize(str, supportedTags) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: displays it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, screen.markup.Background)
		}
	}
}

//RenderLineWithBackGround does what RenderLine does but rendering the line
//with the given background color
func (screen *Screen) RenderLineWithBackGround(x int, y int, str string, bgColor Color) {
	screen.Lock()
	defer screen.Unlock()
	start, column := 0, 0
	if x > 0 {
		fill(0, y, x, y, termbox.Cell{Ch: ' ', Bg: termbox.Attribute(bgColor)})
	}
	for _, token := range Tokenize(str, supportedTags) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: display it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, termbox.Attribute(bgColor))
		}
	}
	fill(start+1, y, screen.Width, y, termbox.Cell{Ch: ' ', Bg: termbox.Attribute(bgColor)})
}

//Render renders the given content starting from the given row
func (screen *Screen) Render(row int, str string) {
	screen.RenderAtColumn(0, row, str)
}

//RenderAtColumn renders the given content starting from
//the given row at the given column
func (screen *Screen) RenderAtColumn(column, initialRow int, str string) {
	for row, line := range strings.Split(str, "\n") {
		screen.RenderLine(column, initialRow+row, line)
	}
}

//RenderRenderer renders the given renderer starting from the given row
func (screen *Screen) RenderRenderer(row int, renderer Renderer) {
	screen.Render(row, renderer.Render())
}

//Reset sets the cursor in the initial position
func (cursor *Cursor) Reset() {
	cursor.Lock()
	defer cursor.Unlock()
	cursor.line = 0
}

//ScrollCursorDown moves the cursor to the line below the current one
func (cursor *Cursor) ScrollCursorDown() {
	cursor.Lock()
	defer cursor.Unlock()
	cursor.line = cursor.line + 1
}

//ScrollCursorUp moves the cursor to the line above the current one
func (cursor *Cursor) ScrollCursorUp() {
	cursor.Lock()
	defer cursor.Unlock()
	if cursor.line > 0 {
		cursor.line = cursor.line - 1
	} else {
		cursor.line = 0
	}
}

//ScrollTo moves the cursor to the given line
func (cursor *Cursor) ScrollTo(pos int) {
	cursor.Lock()
	defer cursor.Unlock()
	cursor.line = pos

}

func toTmAttr(x termui.Attribute) termbox.Attribute {
	return termbox.Attribute(x)
}
<|endoftext|>


<commit_before>package client

// TODO implement roster versioning
// TODO implement pre-approval
// TODO handle a roster, that keeps track of presence, the contacts
// who are in it, etc

import (
	"encoding/xml"
	"github.com/davecgh/go-spew/spew"
	"honnef.co/go/xmpp/rfc6120/client"
)

var _ = spew.Dump

type Client interface {
	client.Client
	GetRoster() Roster
	AddToRoster(item RosterItem) error
	RemoveFromRoster(jid string) error
	Subscribe(jid string) (cookie string, err error)
	Unsubscribe(jid string) (cookie string, err error)
	ApproveSubscription(jid string)
	DenySubscription(jid string)
	BecomeAvailable()
	BecomeUnavailable()
	SendMessage(typ, to, message string)
	Reply(orig *client.Message, reply string)
}

type connection struct {
	client.Client
	stanzas chan client.Stanza
}

func Wrap(c client.Client) Client {
	conn := &connection{
		Client:  c,
		stanzas: make(chan client.Stanza, 100),
	}
	go conn.read()
	c.SubscribeStanzas(conn.stanzas)
	return conn
}

type AuthorizationRequest client.Presence

func (c *connection) read() {
	for stanza := range c.stanzas {
		// TODO way to subscribe to roster events (roster push, subscription requests, ...)
		switch t := stanza.(type) {
		case *client.IQ:
			if t.Query.Space == "jabber:iq:roster" && t.Type == "set" {
				// TODO check 'from' ("Security Warning:
				// Traditionally, a roster push included no 'from'
				// address")
				c.SendIQReply("", "result", stanza.ID(), nil)
			}
		case *client.Presence:
			if t.Type == "subscribe" {
				c.EmitStanza((*AuthorizationRequest)(t))
				// c.subscribers.send((*AuthorizationRequest)(t))
			}
		default:
			// TODO track JID etc
		}
	}
}

type Roster []RosterItem

type RosterItem struct {
	JID  string `xml:"jid,attr"`
	Name string `xml:"name,attr,omitempty"`
	// Groups []string // TODO
	Subscription string `xml:"subscription,attr,omitempty"`
}

type rosterQuery struct {
	XMLName xml.Name    `xml:"jabber:iq:roster query"`
	Item    *RosterItem `xml:"item,omitempty"`
}

func (c *connection) GetRoster() Roster {
	// TODO implement

	ch, _ := c.SendIQ("", "get", rosterQuery{})
	<-ch

	return nil
}

// AddToRoster adds an item to the roster. If no item with the
// specified JID exists yet, a new one will be created. Otherwise an
// existing one will be updated.
func (c *connection) AddToRoster(item RosterItem) error {
	ch, _ := c.SendIQ("", "set", rosterQuery{Item: &item})
	// TODO implement error handling
	<-ch
	return nil
}

func (c *connection) RemoveFromRoster(jid string) error {
	ch, _ := c.SendIQ("", "set", rosterQuery{Item: &RosterItem{
		JID:          jid,
		Subscription: "remove",
	}})
	<-ch
	return nil
	// TODO handle error
}

func (c *connection) Subscribe(jid string) (cookie string, err error) {
	cookie, err = c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "subscribe",
		},
	})
	return
	// TODO handle error
}

func (c *connection) Unsubscribe(jid string) (cookie string, err error) {
	cookie, err = c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "unsubscribe",
		},
	})
	return
	// TODO handle error
}

func (c *connection) ApproveSubscription(jid string) {
	c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "subscribed",
		},
	})
}

func (c *connection) DenySubscription(jid string) {
	// TODO document that this can also be used to revoke an existing
	// subscription
	c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "unsubscribed",
		},
	})
}

func (c *connection) BecomeAvailable() {
	// TODO document SendPresence (rfc6120) for more specific needs
	c.SendPresence(client.Presence{})
}

func (c *connection) BecomeUnavailable() {
	// TODO document SendPresence (rfc6120) for more specific needs
	// TODO can't be have one global xml encoder?
	xml.NewEncoder(c).Encode(client.Presence{Header: client.Header{Type: "unavailable"}})
}

func (c *connection) SendMessage(typ, to, message string) {
	// TODO support extended items in the mssage
	// TODO if `to` is a bare JID, see if we know about a full JID to
	// use instead
	// TODO actually keep track of JIDs
	// TODO support <thread>
	// TODO support subject

	m := client.Message{
		Header: client.Header{
			From: c.JID(),
			To:   to,
			Type: typ,
		},
		Body: message,
	}

	xml.NewEncoder(c).Encode(m)
}

func (c *connection) Reply(orig *client.Message, reply string) {
	// TODO threading
	// TODO use bare JID if full JID isn't up to date anymore
	// TODO support subject
	// TODO support extended items
	c.SendMessage(orig.Type, orig.From, reply)
}

// The user's client SHOULD address the initial message in a chat
// session to the bare JID <contact@domainpart> of the contact (rather
// than attempting to guess an appropriate full JID
// <contact@domainpart/resourcepart> based on the <show/>, <status/>,
// or <priority/> value of any presence notifications it might have
// received from the contact). Until and unless the user's client
// receives a reply from the contact, it SHOULD send any further
// messages to the contact's bare JID. The contact's client SHOULD
// address its replies to the user's full JID
// <user@domainpart/resourcepart> as provided in the 'from' address of
// the initial message. Once the user's client receives a reply from
// the contact's full JID, it SHOULD address its subsequent messages
// to the contact's full JID as provided in the 'from' address of the
// contact's replies, thus "locking in" on that full JID. A client
// SHOULD "unlock" after having received a <message/> or <presence/>
// stanza from any other resource controlled by the peer (or a
// presence stanza from the locked resource); as a result, it SHOULD
// address its next message(s) in the chat session to the bare JID of
// the peer (thus "unlocking" the previous "lock") until it receives a
// message from one of the peer's full JIDs.

// When two parties engage in a chat session but do not share presence
// with each other based on a presence subscription, they SHOULD send
// directed presence to each other so that either party can easily
// discover if the peer goes offline during the course of the chat
// session. However, a client MUST provide a way for a user to disable
// such presence sharing globally or to enable it only with particular
// entities. Furthermore, a party SHOULD send directed unavailable
// presence to the peer when it has reason to believe that the chat
// session is over (e.g., if, after some reasonable amount of time, no
// subsequent messages have been exchanged between the parties).
<commit_msg>add a Dial method to rfc6121<commit_after>package client

// TODO implement roster versioning
// TODO implement pre-approval
// TODO handle a roster, that keeps track of presence, the contacts
// who are in it, etc

import (
	"encoding/xml"
	"github.com/davecgh/go-spew/spew"
	"honnef.co/go/xmpp/rfc6120/client"
)

var _ = spew.Dump

type Client interface {
	client.Client
	GetRoster() Roster
	AddToRoster(item RosterItem) error
	RemoveFromRoster(jid string) error
	Subscribe(jid string) (cookie string, err error)
	Unsubscribe(jid string) (cookie string, err error)
	ApproveSubscription(jid string)
	DenySubscription(jid string)
	BecomeAvailable()
	BecomeUnavailable()
	SendMessage(typ, to, message string)
	Reply(orig *client.Message, reply string)
}

type connection struct {
	client.Client
	stanzas chan client.Stanza
}

func Wrap(c client.Client) Client {
	conn := &connection{
		Client:  c,
		stanzas: make(chan client.Stanza, 100),
	}
	go conn.read()
	c.SubscribeStanzas(conn.stanzas)
	return conn
}

func Dial(user, host, password string) (Client, []error, bool) {
	c, errs, ok := client.Dial(user, host, password)
	if !ok {
		return nil, errs, ok
	}

	return Wrap(c), errs, true
}

type AuthorizationRequest client.Presence

func (c *connection) read() {
	for stanza := range c.stanzas {
		// TODO way to subscribe to roster events (roster push, subscription requests, ...)
		switch t := stanza.(type) {
		case *client.IQ:
			if t.Query.Space == "jabber:iq:roster" && t.Type == "set" {
				// TODO check 'from' ("Security Warning:
				// Traditionally, a roster push included no 'from'
				// address")
				c.SendIQReply("", "result", stanza.ID(), nil)
			}
		case *client.Presence:
			if t.Type == "subscribe" {
				c.EmitStanza((*AuthorizationRequest)(t))
				// c.subscribers.send((*AuthorizationRequest)(t))
			}
		default:
			// TODO track JID etc
		}
	}
}

type Roster []RosterItem

type RosterItem struct {
	JID  string `xml:"jid,attr"`
	Name string `xml:"name,attr,omitempty"`
	// Groups []string // TODO
	Subscription string `xml:"subscription,attr,omitempty"`
}

type rosterQuery struct {
	XMLName xml.Name    `xml:"jabber:iq:roster query"`
	Item    *RosterItem `xml:"item,omitempty"`
}

func (c *connection) GetRoster() Roster {
	// TODO implement

	ch, _ := c.SendIQ("", "get", rosterQuery{})
	<-ch

	return nil
}

// AddToRoster adds an item to the roster. If no item with the
// specified JID exists yet, a new one will be created. Otherwise an
// existing one will be updated.
func (c *connection) AddToRoster(item RosterItem) error {
	ch, _ := c.SendIQ("", "set", rosterQuery{Item: &item})
	// TODO implement error handling
	<-ch
	return nil
}

func (c *connection) RemoveFromRoster(jid string) error {
	ch, _ := c.SendIQ("", "set", rosterQuery{Item: &RosterItem{
		JID:          jid,
		Subscription: "remove",
	}})
	<-ch
	return nil
	// TODO handle error
}

func (c *connection) Subscribe(jid string) (cookie string, err error) {
	cookie, err = c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "subscribe",
		},
	})
	return
	// TODO handle error
}

func (c *connection) Unsubscribe(jid string) (cookie string, err error) {
	cookie, err = c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "unsubscribe",
		},
	})
	return
	// TODO handle error
}

func (c *connection) ApproveSubscription(jid string) {
	c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "subscribed",
		},
	})
}

func (c *connection) DenySubscription(jid string) {
	// TODO document that this can also be used to revoke an existing
	// subscription
	c.SendPresence(client.Presence{
		Header: client.Header{
			To:   jid,
			Type: "unsubscribed",
		},
	})
}

func (c *connection) BecomeAvailable() {
	// TODO document SendPresence (rfc6120) for more specific needs
	c.SendPresence(client.Presence{})
}

func (c *connection) BecomeUnavailable() {
	// TODO document SendPresence (rfc6120) for more specific needs
	// TODO can't be have one global xml encoder?
	xml.NewEncoder(c).Encode(client.Presence{Header: client.Header{Type: "unavailable"}})
}

func (c *connection) SendMessage(typ, to, message string) {
	// TODO support extended items in the mssage
	// TODO if `to` is a bare JID, see if we know about a full JID to
	// use instead
	// TODO actually keep track of JIDs
	// TODO support <thread>
	// TODO support subject

	m := client.Message{
		Header: client.Header{
			From: c.JID(),
			To:   to,
			Type: typ,
		},
		Body: message,
	}

	xml.NewEncoder(c).Encode(m)
}

func (c *connection) Reply(orig *client.Message, reply string) {
	// TODO threading
	// TODO use bare JID if full JID isn't up to date anymore
	// TODO support subject
	// TODO support extended items
	c.SendMessage(orig.Type, orig.From, reply)
}

// The user's client SHOULD address the initial message in a chat
// session to the bare JID <contact@domainpart> of the contact (rather
// than attempting to guess an appropriate full JID
// <contact@domainpart/resourcepart> based on the <show/>, <status/>,
// or <priority/> value of any presence notifications it might have
// received from the contact). Until and unless the user's client
// receives a reply from the contact, it SHOULD send any further
// messages to the contact's bare JID. The contact's client SHOULD
// address its replies to the user's full JID
// <user@domainpart/resourcepart> as provided in the 'from' address of
// the initial message. Once the user's client receives a reply from
// the contact's full JID, it SHOULD address its subsequent messages
// to the contact's full JID as provided in the 'from' address of the
// contact's replies, thus "locking in" on that full JID. A client
// SHOULD "unlock" after having received a <message/> or <presence/>
// stanza from any other resource controlled by the peer (or a
// presence stanza from the locked resource); as a result, it SHOULD
// address its next message(s) in the chat session to the bare JID of
// the peer (thus "unlocking" the previous "lock") until it receives a
// message from one of the peer's full JIDs.

// When two parties engage in a chat session but do not share presence
// with each other based on a presence subscription, they SHOULD send
// directed presence to each other so that either party can easily
// discover if the peer goes offline during the course of the chat
// session. However, a client MUST provide a way for a user to disable
// such presence sharing globally or to enable it only with particular
// entities. Furthermore, a party SHOULD send directed unavailable
// presence to the peer when it has reason to believe that the chat
// session is over (e.g., if, after some reasonable amount of time, no
// subsequent messages have been exchanged between the parties).
<|endoftext|>


<commit_before>package tchannel_test

import (
	"fmt"
	"sync"
	"testing"
	"time"

	. "github.com/uber/tchannel-go"

	"github.com/uber/tchannel-go/benchmark"
	"github.com/uber/tchannel-go/testutils"

	"github.com/bmizerany/perks/quantile"
	"github.com/stretchr/testify/require"
)

type benchmarkParams struct {
	servers, clients int
	requestSize      int
}

type workerControl struct {
	start        sync.WaitGroup
	unblockStart chan struct{}
	done         sync.WaitGroup
}

func init() {
	benchmark.BenchmarkDir = "./benchmark/"
}

func newWorkerControl(numWorkers int) *workerControl {
	wc := &workerControl{
		unblockStart: make(chan struct{}),
	}
	wc.start.Add(numWorkers)
	wc.done.Add(numWorkers)
	return wc
}

func (c *workerControl) WaitForStart(f func()) {
	c.start.Wait()
	f()
	close(c.unblockStart)
}

func (c *workerControl) WaitForEnd() {
	c.done.Wait()
}

func (c *workerControl) WorkerStart() {
	c.start.Done()
	<-c.unblockStart
}

func (c *workerControl) WorkerDone() {
	c.done.Done()
}

func defaultParams() benchmarkParams {
	return benchmarkParams{
		servers:     2,
		clients:     2,
		requestSize: 1024,
	}
}

func closeAndVerify(b *testing.B, ch *Channel) {
	ch.Close()
	isChanClosed := func() bool {
		return ch.State() == ChannelClosed
	}
	if !testutils.WaitFor(time.Second, isChanClosed) {
		b.Errorf("Timed out waiting for channel to close, state: %v", ch.State())
	}
}

func benchmarkRelay(b *testing.B, p benchmarkParams) {
	b.SetBytes(int64(p.requestSize))
	b.ReportAllocs()

	services := make(map[string][]string)

	servers := make([]benchmark.Server, p.servers)
	for i := range servers {
		servers[i] = benchmark.NewServer(
			benchmark.WithServiceName("svc"),
			benchmark.WithRequestSize(p.requestSize),
			benchmark.WithExternalProcess(),
		)
		defer servers[i].Close()
		services["svc"] = append(services["svc]"], servers[i].HostPort())
	}

	relay, err := benchmark.NewRealRelay(services)
	require.NoError(b, err, "Failed to create relay")
	defer relay.Close()

	clients := make([]benchmark.Client, p.clients)
	for i := range clients {
		clients[i] = benchmark.NewClient([]string{relay.HostPort()},
			benchmark.WithServiceName("svc"),
			benchmark.WithRequestSize(p.requestSize),
			benchmark.WithExternalProcess(),
			benchmark.WithTimeout(10*time.Second),
		)
		defer clients[i].Close()
		require.NoError(b, clients[i].Warmup(), "Warmup failed")
	}

	quantileVals := []float64{0.50, 0.95, 0.99, 1.0}
	quantiles := make([]*quantile.Stream, p.clients)
	for i := range quantiles {
		quantiles[i] = quantile.NewTargeted(quantileVals...)
	}

	wc := newWorkerControl(p.clients)
	dec := testutils.Decrementor(b.N)

	for i, c := range clients {
		go func(i int, c benchmark.Client) {
			// Do a warm up call.
			c.RawCall(1)

			wc.WorkerStart()
			defer wc.WorkerDone()

			for {
				tokens := dec.Multiple(200)
				if tokens == 0 {
					break
				}

				durations, err := c.RawCall(tokens)
				if err != nil {
					b.Fatalf("Call failed: %v", err)
				}

				for _, d := range durations {
					quantiles[i].Insert(float64(d))
				}
			}
		}(i, c)
	}

	var started time.Time
	wc.WaitForStart(func() {
		b.ResetTimer()
		started = time.Now()
	})
	wc.WaitForEnd()
	duration := time.Since(started)

	fmt.Printf("\nb.N: %v Duration: %v RPS = %0.0f\n", b.N, duration, float64(b.N)/duration.Seconds())

	// Merge all the quantiles into 1
	for _, q := range quantiles[1:] {
		quantiles[0].Merge(q.Samples())
	}

	for _, q := range quantileVals {
		fmt.Printf("  %0.4f = %v\n", q, time.Duration(quantiles[0].Query(q)))
	}
	fmt.Println()
}

func BenchmarkRelayNoLatencies(b *testing.B) {
	server := benchmark.NewServer(
		benchmark.WithServiceName("svc"),
		benchmark.WithExternalProcess(),
		benchmark.WithNoLibrary(),
	)
	defer server.Close()

	hostMapping := map[string][]string{"svc": {server.HostPort()}}
	relay, err := benchmark.NewRealRelay(hostMapping)
	require.NoError(b, err, "NewRealRelay failed")
	defer relay.Close()

	client := benchmark.NewClient([]string{relay.HostPort()},
		benchmark.WithServiceName("svc"),
		benchmark.WithExternalProcess(),
		benchmark.WithNoLibrary(),
		benchmark.WithNumClients(10),
		benchmark.WithNoChecking(),
		benchmark.WithNoDurations(),
		benchmark.WithTimeout(10*time.Second),
	)
	defer client.Close()
	require.NoError(b, err, client.Warmup(), "client.Warmup failed")

	b.ResetTimer()
	started := time.Now()
	for _, calls := range testutils.Batch(b.N, 10000) {
		if _, err := client.RawCall(calls); err != nil {
			b.Fatalf("Calls failed: %v", err)
		}
	}

	duration := time.Since(started)
	fmt.Printf("\nb.N: %v Duration: %v RPS = %0.0f\n", b.N, duration, float64(b.N)/duration.Seconds())
}

func BenchmarkRelay2Servers5Clients1k(b *testing.B) {
	p := defaultParams()
	p.clients = 5
	p.servers = 2
	benchmarkRelay(b, p)
}

func BenchmarkRelay4Servers20Clients1k(b *testing.B) {
	p := defaultParams()
	p.clients = 20
	p.servers = 4
	benchmarkRelay(b, p)
}

func BenchmarkRelay2Servers5Clients4k(b *testing.B) {
	p := defaultParams()
	p.requestSize = 4 * 1024
	p.clients = 5
	p.servers = 2
	benchmarkRelay(b, p)
}
<commit_msg>Fix assertion that warmup succeeded<commit_after>package tchannel_test

import (
	"fmt"
	"sync"
	"testing"
	"time"

	. "github.com/uber/tchannel-go"

	"github.com/uber/tchannel-go/benchmark"
	"github.com/uber/tchannel-go/testutils"

	"github.com/bmizerany/perks/quantile"
	"github.com/stretchr/testify/require"
)

type benchmarkParams struct {
	servers, clients int
	requestSize      int
}

type workerControl struct {
	start        sync.WaitGroup
	unblockStart chan struct{}
	done         sync.WaitGroup
}

func init() {
	benchmark.BenchmarkDir = "./benchmark/"
}

func newWorkerControl(numWorkers int) *workerControl {
	wc := &workerControl{
		unblockStart: make(chan struct{}),
	}
	wc.start.Add(numWorkers)
	wc.done.Add(numWorkers)
	return wc
}

func (c *workerControl) WaitForStart(f func()) {
	c.start.Wait()
	f()
	close(c.unblockStart)
}

func (c *workerControl) WaitForEnd() {
	c.done.Wait()
}

func (c *workerControl) WorkerStart() {
	c.start.Done()
	<-c.unblockStart
}

func (c *workerControl) WorkerDone() {
	c.done.Done()
}

func defaultParams() benchmarkParams {
	return benchmarkParams{
		servers:     2,
		clients:     2,
		requestSize: 1024,
	}
}

func closeAndVerify(b *testing.B, ch *Channel) {
	ch.Close()
	isChanClosed := func() bool {
		return ch.State() == ChannelClosed
	}
	if !testutils.WaitFor(time.Second, isChanClosed) {
		b.Errorf("Timed out waiting for channel to close, state: %v", ch.State())
	}
}

func benchmarkRelay(b *testing.B, p benchmarkParams) {
	b.SetBytes(int64(p.requestSize))
	b.ReportAllocs()

	services := make(map[string][]string)

	servers := make([]benchmark.Server, p.servers)
	for i := range servers {
		servers[i] = benchmark.NewServer(
			benchmark.WithServiceName("svc"),
			benchmark.WithRequestSize(p.requestSize),
			benchmark.WithExternalProcess(),
		)
		defer servers[i].Close()
		services["svc"] = append(services["svc]"], servers[i].HostPort())
	}

	relay, err := benchmark.NewRealRelay(services)
	require.NoError(b, err, "Failed to create relay")
	defer relay.Close()

	clients := make([]benchmark.Client, p.clients)
	for i := range clients {
		clients[i] = benchmark.NewClient([]string{relay.HostPort()},
			benchmark.WithServiceName("svc"),
			benchmark.WithRequestSize(p.requestSize),
			benchmark.WithExternalProcess(),
			benchmark.WithTimeout(10*time.Second),
		)
		defer clients[i].Close()
		require.NoError(b, clients[i].Warmup(), "Warmup failed")
	}

	quantileVals := []float64{0.50, 0.95, 0.99, 1.0}
	quantiles := make([]*quantile.Stream, p.clients)
	for i := range quantiles {
		quantiles[i] = quantile.NewTargeted(quantileVals...)
	}

	wc := newWorkerControl(p.clients)
	dec := testutils.Decrementor(b.N)

	for i, c := range clients {
		go func(i int, c benchmark.Client) {
			// Do a warm up call.
			c.RawCall(1)

			wc.WorkerStart()
			defer wc.WorkerDone()

			for {
				tokens := dec.Multiple(200)
				if tokens == 0 {
					break
				}

				durations, err := c.RawCall(tokens)
				if err != nil {
					b.Fatalf("Call failed: %v", err)
				}

				for _, d := range durations {
					quantiles[i].Insert(float64(d))
				}
			}
		}(i, c)
	}

	var started time.Time
	wc.WaitForStart(func() {
		b.ResetTimer()
		started = time.Now()
	})
	wc.WaitForEnd()
	duration := time.Since(started)

	fmt.Printf("\nb.N: %v Duration: %v RPS = %0.0f\n", b.N, duration, float64(b.N)/duration.Seconds())

	// Merge all the quantiles into 1
	for _, q := range quantiles[1:] {
		quantiles[0].Merge(q.Samples())
	}

	for _, q := range quantileVals {
		fmt.Printf("  %0.4f = %v\n", q, time.Duration(quantiles[0].Query(q)))
	}
	fmt.Println()
}

func BenchmarkRelayNoLatencies(b *testing.B) {
	server := benchmark.NewServer(
		benchmark.WithServiceName("svc"),
		benchmark.WithExternalProcess(),
		benchmark.WithNoLibrary(),
	)
	defer server.Close()

	hostMapping := map[string][]string{"svc": {server.HostPort()}}
	relay, err := benchmark.NewRealRelay(hostMapping)
	require.NoError(b, err, "NewRealRelay failed")
	defer relay.Close()

	client := benchmark.NewClient([]string{relay.HostPort()},
		benchmark.WithServiceName("svc"),
		benchmark.WithExternalProcess(),
		benchmark.WithNoLibrary(),
		benchmark.WithNumClients(10),
		benchmark.WithNoChecking(),
		benchmark.WithNoDurations(),
		benchmark.WithTimeout(10*time.Second),
	)
	defer client.Close()
	require.NoError(b, client.Warmup(), "client.Warmup failed")

	b.ResetTimer()
	started := time.Now()
	for _, calls := range testutils.Batch(b.N, 10000) {
		if _, err := client.RawCall(calls); err != nil {
			b.Fatalf("Calls failed: %v", err)
		}
	}

	duration := time.Since(started)
	fmt.Printf("\nb.N: %v Duration: %v RPS = %0.0f\n", b.N, duration, float64(b.N)/duration.Seconds())
}

func BenchmarkRelay2Servers5Clients1k(b *testing.B) {
	p := defaultParams()
	p.clients = 5
	p.servers = 2
	benchmarkRelay(b, p)
}

func BenchmarkRelay4Servers20Clients1k(b *testing.B) {
	p := defaultParams()
	p.clients = 20
	p.servers = 4
	benchmarkRelay(b, p)
}

func BenchmarkRelay2Servers5Clients4k(b *testing.B) {
	p := defaultParams()
	p.requestSize = 4 * 1024
	p.clients = 5
	p.servers = 2
	benchmarkRelay(b, p)
}
<|endoftext|>


<commit_before>package masterapi

import (
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/master"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"net/http"
	"strconv"
)

var slaves = []Slave{
	Slave{Id: 0, Hostname: "mksuns31", Port: 1912, MongodPortRangeBegin: 20000, MongodPortRangeEnd: 20100, PersistantStorage: true, RootDataDirectory: "/home/mongo/data", State: "active"},
	Slave{Id: 1, Hostname: "mksuns32", Port: 1912, MongodPortRangeBegin: 20000, MongodPortRangeEnd: 20001, PersistantStorage: false, RootDataDirectory: "/home/mongo/data", State: "active"},
	Slave{Id: 2, Hostname: "mksuns33", Port: 1912, MongodPortRangeBegin: 20000, MongodPortRangeEnd: 20001, PersistantStorage: false, RootDataDirectory: "/home/mongo/data", State: "active"},
	Slave{Id: 3, Hostname: "mksuns34", Port: 1912, MongodPortRangeBegin: 20000, MongodPortRangeEnd: 20001, PersistantStorage: false, RootDataDirectory: "/home/mongo/data", State: "active"},
}

type SlaveAPI struct {
	DB               *gorm.DB
	ClusterAllocator *master.ClusterAllocator
}

type Slave struct {
	Id                   uint   `json:"id"`
	Hostname             string `json:"hostname"`
	Port                 uint   `json:"slave_port"`
	MongodPortRangeBegin uint   `json:"mongod_port_range_begin"` //inclusive
	MongodPortRangeEnd   uint   `json:"mongod_port_range_end"`   //exclusive
	PersistantStorage    bool   `json:"persistant_storage"`
	RootDataDirectory    string `json:"root_data_directory"`
	State                string `json:"state"`
}

func (m *MasterAPI) SlaveIndex(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(slaves)
}

func (m *MasterAPI) SlaveById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)
	for _, slave := range slaves {
		if slave.Id == id {
			json.NewEncoder(w).Encode(slave)
			return
		}
	}
	w.WriteHeader(http.StatusNotFound)
	return
}

func (m *MasterAPI) SlavePut(w http.ResponseWriter, r *http.Request) {
	var postSlave Slave
	err := json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Could not parse object (%s)", err.Error())
		return
	}

	var maxId uint = 0
	for _, slave := range slaves {
		if slave.Id > maxId {
			maxId = slave.Id
		}
	}
	postSlave.Id = maxId + 1

	slaves = append(slaves, postSlave)
	return
}

func (m *MasterAPI) SlaveUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	var postSlave Slave
	err = json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Could not parse object (%s)", err.Error())
		return
	}

	for idx, slave := range slaves {
		if slave.Id == id {
			if postSlave.Id != id {
				w.WriteHeader(http.StatusBadRequest)
				fmt.Fprintf(w, "You can not change the id of an object")
				return
			}
			slaves[idx] = postSlave
			w.WriteHeader(http.StatusOK)
			return
		}
	}
	w.WriteHeader(http.StatusNotFound)
	return
}

func (m *MasterAPI) SlaveDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	var slaveIdx int = -1
	for idx, slave := range slaves {
		if slave.Id == id {
			slaveIdx = idx
		}
	}
	if slaveIdx == -1 {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	slaves = append(slaves[:slaveIdx], slaves[slaveIdx+1:]...)
}
<commit_msg>ADD: masterapi: slave handlers now use database.<commit_after>package masterapi

import (
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"strconv"
)

type Slave struct {
	ID                   uint   `json:"id"`
	Hostname             string `json:"hostname"`
	Port                 uint   `json:"slave_port"`
	MongodPortRangeBegin uint   `json:"mongod_port_range_begin"` //inclusive
	MongodPortRangeEnd   uint   `json:"mongod_port_range_end"`   //exclusive
	PersistentStorage    bool   `json:"persistent_storage"`
	ConfiguredState      string `json:"state"`
}

func (m *MasterAPI) SlaveIndex(w http.ResponseWriter, r *http.Request) {

	var slaves []model.Slave
	err := m.DB.Find(&slaves).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}
	json.NewEncoder(w).Encode(slaves)
}

func (m *MasterAPI) SlaveById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	var slaves []model.Slave
	err = m.DB.Find(&slaves, &model.Slave{ID: id}).Error

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}
	if len(slaves) == 0 { // Not found?
		w.WriteHeader(http.StatusNotFound)
		return
	}

	if len(slaves) > 1 {
		log.Printf("inconsistency: multiple slaves for slave.ID = %d found in database", len(slaves))
	}
	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(&slaves[0]))
	return
}

func (m *MasterAPI) SlavePut(w http.ResponseWriter, r *http.Request) {
	var postSlave Slave
	err := json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the slave ID in PUT request")
		return
	}

	modelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Persist to database

	err = m.DB.Create(modelSlave).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	// TODO set location header. Would it be better to return the ID? YES.

	return
}

func (m *MasterAPI) SlaveUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	var postSlave Slave
	err = json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	if err = postSlave.assertNoZeroFieldsSet(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not POST JSON with zero values in any field: %s", err.Error())
		return
	}

	modelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	// Persist to database

	m.DB.Model(&modelSlave).Updates(&modelSlave)
}

func (m *MasterAPI) SlaveDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	s := m.DB.Delete(&model.Slave{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}
	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
	}

	if s.RowsAffected > 1 {
		log.Printf("inconsistency: slave DELETE affected more than one row. Slave.ID = %v", id)
	}
}
<|endoftext|>