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

package dlshared

import (
	"net/url"
	"net/http"
	"io/ioutil"
	"time"
	"bytes"
	"strings"
	"crypto/tls"
	"labix.org/v2/mgo/bson"
	"encoding/json"
	"github.com/mreiferson/go-httpclient"
)

const (
	DefaultSocketTimeout = 40000
	HttpPostMethod = "POST"
	HttpGetMethod = "GET"

	ContentTypeHeader = "Content-Type"

	NoStatusCode = -1 // This is the value used if an error is generated before the status code is available

	ContentTypeTextPlain = "text/plain; charset=utf-8"
	ContentTypePostForm = "application/x-www-form-urlencoded"
	ContentTypeJson = "application/json"
)

// The http request client allows you to configure global values for timeout/connect etc. It also
// handles closing the transport and provides convenience methods for posting some types of data.
type HttpRequestClient struct {
	DisableKeepAlives bool
	DisableCompression bool
	SkipSslVerify bool
	MaxIdleConnsPerHost int
	ConnectTimeout time.Duration
	ResponseHeaderTimeout time.Duration
	RequestTimeout time.Duration
	ReadWriteTimeout time.Duration
}

func NewDefaultHttpRequestClient() *HttpRequestClient {
	return NewHttpRequestClient(true, false, false, 0, DefaultSocketTimeout, DefaultSocketTimeout, DefaultSocketTimeout, DefaultSocketTimeout)
}

func NewHttpRequestClient(	disableKeepAlives,
							disableCompression,
							skipSslVerify bool,
							maxIdleConnsPerHost,
							connectTimeoutInMs,
							responseHeaderTimeoutInMs,
							requestTimeoutInMs,
							readWriteTimeoutInMs int) *HttpRequestClient {

	return &HttpRequestClient{ 	DisableKeepAlives: disableKeepAlives,
								DisableCompression: disableCompression,
								SkipSslVerify: skipSslVerify,
								MaxIdleConnsPerHost: maxIdleConnsPerHost,
								ConnectTimeout: time.Duration(connectTimeoutInMs) * time.Millisecond,
								ResponseHeaderTimeout: time.Duration(responseHeaderTimeoutInMs) * time.Millisecond,
								RequestTimeout: time.Duration(requestTimeoutInMs) * time.Millisecond,
								ReadWriteTimeout: time.Duration(readWriteTimeoutInMs) * time.Millisecond,
	}
}

// Post the values to the url.
func (self *HttpRequestClient) Post(url string, values url.Values, headers map[string]string) (int, []byte, error) {

	client, transport := self.getClientAndTransport()
	defer transport.Close()

	request, err := http.NewRequest(HttpPostMethod, url,  strings.NewReader(values.Encode()))
	if err != nil { return NoStatusCode, nil, err }

	request.Header.Set(ContentTypeHeader, ContentTypePostForm)
	for key, value := range headers { request.Header.Set(key, value) }

	response, err := client.Do(request)
	if err != nil { return NoStatusCode, nil, err }

	statusCode := response.StatusCode

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil { return statusCode, nil, err
	} else { return statusCode, data, nil }
}

// Post the raw string to the url.
func (self *HttpRequestClient) PostStr(url string, value string, headers map[string]string) (int, []byte, error) {

	client, transport := self.getClientAndTransport()
	defer transport.Close()

	request, err := http.NewRequest(HttpPostMethod, url, bytes.NewReader([]byte(value)))
	if err != nil { return NoStatusCode, nil, err }

	request.Header.Set(ContentTypeHeader, ContentTypePostForm)
	for key, value := range headers { request.Header.Set(key, value) }

	response, err := client.Do(request)
	if err != nil { return NoStatusCode, nil, err }

	statusCode := response.StatusCode

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil { return statusCode, nil, err
	} else { return statusCode, data, nil }
}

// Post the json struct to the url.
func (self *HttpRequestClient) PostJson(url string, value interface{}, headers map[string]string) (int, []byte, error) {

	rawJson, err := json.Marshal(value)
	if err != nil { return NoStatusCode, nil, err }

	client, transport := self.getClientAndTransport()
	defer transport.Close()

	request, err := http.NewRequest(HttpPostMethod, url, bytes.NewReader(rawJson))
	if err != nil { return NoStatusCode, nil, err }

	request.Header.Set(ContentTypeHeader, ContentTypeJson)
	for key, value := range headers { request.Header.Set(key, value) }

	response, err := client.Do(request)
	if err != nil { return NoStatusCode, nil, err }

	statusCode := response.StatusCode

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil { return statusCode, nil, err
	} else { return statusCode, data, nil }
}

// Post the bson doc to the url.
func (self *HttpRequestClient) PostBson(url string, bsonDoc interface{}, headers map[string]string) (int, []byte, error) {

	rawBson, err := bson.Marshal(bsonDoc)
	if err != nil { return NoStatusCode, nil, err }

	client, transport := self.getClientAndTransport()
	defer transport.Close()

	request, err := http.NewRequest(HttpPostMethod, url, bytes.NewReader(rawBson))
	if err != nil { return NoStatusCode, nil, err }

	request.Header.Set(ContentTypeHeader, ContentTypePostForm)
	for key, value := range headers { request.Header.Set(key, value) }

	response, err := client.Do(request)
	if err != nil { return NoStatusCode, nil, err }

	statusCode := response.StatusCode

	defer response.Body.Close()

	// We do not return the response so don't report if there is an error.
	if data, err := ioutil.ReadAll(response.Body); err != nil { return statusCode, nil, err
	} else { return statusCode, data, nil }
}

// Issue a GET to retrieve a bson doc.
func (self *HttpRequestClient) GetBson(url string, headers map[string]string) (int, bson.M, error) {

	client, transport := self.getClientAndTransport()
	defer transport.Close()

	request, requestErr := http.NewRequest(HttpGetMethod, url, nil)
	if requestErr != nil { return NoStatusCode, nil, requestErr }

	for key, value := range headers { request.Header.Set(key, value) }

	response, err := client.Do(request)
	if err != nil { return NoStatusCode, nil, err }

	statusCode := response.StatusCode

	defer response.Body.Close()

	rawBson, err := ioutil.ReadAll(response.Body)
	if err != nil { return statusCode, nil, err }

	var bsonDoc bson.M
	if err := bson.Unmarshal(rawBson, &bsonDoc); err != nil { return statusCode, nil, err }

	return statusCode, bsonDoc, nil
}

// Use the clone method if you need to override some/all of the configured values.
func (self *HttpRequestClient) Clone() *HttpRequestClient {
	val := *self
	return &val
}

func (self *HttpRequestClient) getClientAndTransport() (*http.Client, *httpclient.Transport) {
	transport := self.getTransport()
	return &http.Client{ Transport: transport }, transport
}

func (self *HttpRequestClient) getTransport() *httpclient.Transport {
	return &httpclient.Transport {

		DisableKeepAlives: self.DisableKeepAlives,
		DisableCompression: self.DisableCompression,

		MaxIdleConnsPerHost: self.MaxIdleConnsPerHost,

		ConnectTimeout: self.ConnectTimeout,
		ResponseHeaderTimeout: self.ResponseHeaderTimeout,
		RequestTimeout: self.RequestTimeout,
		ReadWriteTimeout: self.ReadWriteTimeout,

		TLSClientConfig: &tls.Config{ InsecureSkipVerify: self.SkipSslVerify },
	}
}

func HttpPost(url string, values url.Values, headers map[string]string) (int, []byte, error) { return NewDefaultHttpRequestClient().Post(url, values, headers) }

func HttpPostStr(url string, value string, headers map[string]string) (int, []byte, error) { return NewDefaultHttpRequestClient().PostStr(url, value, headers) }

func HttpPostJson(url string, value interface{}, headers map[string]string) (int, []byte, error) { return NewDefaultHttpRequestClient().PostJson(url, value, headers) }

func HttpPostBson(url string, bsonDoc interface{}, headers map[string]string) (int, []byte, error) { return NewDefaultHttpRequestClient().PostBson(url, bsonDoc, headers) }

func HttpGetBson(url string, headers map[string]string) (int, bson.M, error) { return NewDefaultHttpRequestClient().GetBson(url, headers) }

<commit_msg>changed file name.<commit_after><|endoftext|>


<commit_before>package client

import (
	"bufio"
	"crypto/tls"
	"github.com/fluffle/goirc/event"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

const (
	second = int64(1e9)
)

// An IRC connection is represented by this struct. Once connected, any errors
// encountered are piped down *Conn.Err; this channel is closed on disconnect.
type Conn struct {
	// Connection Hostname and Nickname
	Host    string
	Me      *Nick
	Network string

	// Event handler registry and dispatcher
	Registry event.EventRegistry
	Dispatcher event.EventDispatcher

	// Map of channels we're on
	chans map[string]*Channel
	// Map of nicks we know about
	nicks map[string]*Nick

	// Use the State field to store external state that handlers might need.
	// Remember ... you might need locking for this ;-)
	State interface{}

	// I/O stuff to server
	sock      net.Conn
	io        *bufio.ReadWriter
	in        chan *Line
	out       chan string
	connected bool

	// Control channels to goroutines
	cSend, cLoop chan bool

	// Error channel to transmit any fail back to the user
	Err chan os.Error

	// Misc knobs to tweak client behaviour:
	// Are we connecting via SSL? Do we care about certificate validity?
	SSL       bool
	SSLConfig *tls.Config

	// Socket timeout, in seconds. Defaulted to 5m in New().
	Timeout int64

	// Set this to true to disable flood protection and false to re-enable
	Flood bool

	// Internal counters for flood protection
	badness, lastsent int64

	// Function which returns a *time.Time for use as a timestamp
	Timestamp func() *time.Time

	// Enable debugging? Set format for timestamps on debug output.
	Debug    bool
	TSFormat string
}

// Creates a new IRC connection object, but doesn't connect to anything so
// that you can add event handlers to it. See AddHandler() for details.
func New(nick, user, name string) *Conn {
	reg := event.NewRegistry()
	conn := &Conn{
		Registry: reg,
		Dispatcher: reg,
		in: make(chan *Line, 32),
		out: make(chan string, 32),
		Err: make(chan os.Error, 4),
		cSend: make(chan bool),
		cLoop: make(chan bool),
		SSL: false,
		SSLConfig: nil,
		Timeout: 300,
		Flood: false,
		badness: 0,
		lastsent: 0,
		Timestamp: time.LocalTime,
		TSFormat: "15:04:05",
	}
	conn.initialise()
	conn.SetupHandlers()
	conn.Me = conn.NewNick(nick, user, name, "")
	return conn
}

// Per-connection state initialisation.
func (conn *Conn) initialise() {
	conn.nicks = make(map[string]*Nick)
	conn.chans = make(map[string]*Channel)
	conn.io = nil
	conn.sock = nil

	// If this is being called because we are reconnecting, conn.Me
	// will still have all the old channels referenced -- nuke them!
	if conn.Me != nil {
		conn.Me = conn.NewNick(conn.Me.Nick, conn.Me.Ident, conn.Me.Name, "")
	}
}

// Connect the IRC connection object to "host[:port]" which should be either
// a hostname or an IP address, with an optional port. To enable explicit SSL
// on the connection to the IRC server, set Conn.SSL to true before calling
// Connect(). The port will default to 6697 if ssl is enabled, and 6667
// otherwise. You can also provide an optional connect password.
func (conn *Conn) Connect(host string, pass ...string) os.Error {
	if conn.connected {
		return os.NewError(fmt.Sprintf(
			"irc.Connect(): already connected to %s, cannot connect to %s",
			conn.Host, host))
	}

	if conn.SSL {
		if !hasPort(host) {
			host += ":6697"
		}
		if s, err := tls.Dial("tcp", host, conn.SSLConfig); err == nil {
			conn.sock = s
		} else {
			return err
		}
	} else {
		if !hasPort(host) {
			host += ":6667"
		}
		if s, err := net.Dial("tcp", host); err == nil {
			conn.sock = s
		} else {
			return err
		}
	}

	conn.Host = host
	conn.io = bufio.NewReadWriter(
		bufio.NewReader(conn.sock),
		bufio.NewWriter(conn.sock))
	conn.sock.SetTimeout(conn.Timeout * second)
	conn.connected = true
	go conn.send()
	go conn.recv()

	if len(pass) > 0 {
		conn.Pass(pass[0])
	}
	conn.Nick(conn.Me.Nick)
	conn.User(conn.Me.Ident, conn.Me.Name)

	go conn.runLoop()
	return nil
}

// dispatch a nicely formatted os.Error to the error channel
func (conn *Conn) error(s string, a ...interface{}) {
	conn.Err <- os.NewError(fmt.Sprintf(s, a...))
}

// copied from http.client for great justice
func hasPort(s string) bool {
	return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
}

// goroutine to pass data from output channel to write()
func (conn *Conn) send() {
	for {
		select {
		case line := <-conn.out:
			conn.write(line)
		case <-conn.cSend:
			// strobe on control channel, bail out
			return
		}
	}
}

// receive one \r\n terminated line from peer, parse and dispatch it
func (conn *Conn) recv() {
	for {
		s, err := conn.io.ReadString('\n')
		if err != nil {
			conn.error("irc.recv(): %s", err.String())
			conn.shutdown()
			break
		}
		s = strings.Trim(s, "\r\n")
		t := conn.Timestamp()
		if conn.Debug {
			fmt.Println(t.Format(conn.TSFormat) + " <- " + s)
		}

		if line := parseLine(s); line != nil {
			line.Time = t
			conn.in <- line
		}
	}
}

// goroutine to dispatch events for lines received on input channel
func (conn *Conn) runLoop() {
	for {
		select {
		case line := <-conn.in:
			conn.Dispatcher.Dispatch(line.Cmd, conn, line)
		case <-conn.cLoop:
			// strobe on control channel, bail out
			return
		}
	}
}

// Write a \r\n terminated line of output to the connected server,
// using Hybrid's algorithm to rate limit if conn.Flood is false.
func (conn *Conn) write(line string) {
	if !conn.Flood {
		conn.rateLimit(int64(len(line)))
	}

	if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
		conn.error("irc.send(): %s", err.String())
		conn.shutdown()
		return
	}
	conn.io.Flush()
	if conn.Debug {
		fmt.Println(conn.Timestamp().Format(conn.TSFormat) + " -> " + line)
	}
}

// Implement Hybrid's flood control algorithm to rate-limit outgoing lines.
func (conn *Conn) rateLimit(chars int64) {
	// Hybrid's algorithm allows for 2 seconds per line and an additional
	// 1/120 of a second per character on that line.
	linetime := 2*second + chars*second/120
	elapsed := time.Nanoseconds() - conn.lastsent
	if conn.badness += linetime - elapsed; conn.badness < 0 {
		// negative badness times are badness...
		conn.badness = int64(0)
	}
	conn.lastsent = time.Nanoseconds()
	// If we've sent more than 10 second's worth of lines according to the
	// calculation above, then we're at risk of "Excess Flood".
	if conn.badness > 10*second && !conn.Flood {
		// so sleep for the current line's time value before sending it
		time.Sleep(linetime)
	}
}

func (conn *Conn) shutdown() {
	// Guard against double-call of shutdown() if we get an error in send()
	// as calling sock.Close() will cause recv() to recieve EOF in readstring()
	if conn.connected {
		conn.connected = false
		conn.sock.Close()
		conn.cSend <- true
		conn.cLoop <- true
		conn.Dispatcher.Dispatch("disconnected", conn, nil)
		// reinit datastructures ready for next connection
		// do this here rather than after runLoop()'s for due to race
		conn.initialise()
	}
}

// Dumps a load of information about the current state of the connection to a
// string for debugging state tracking and other such things.
func (conn *Conn) String() string {
	str := "GoIRC Connection\n"
	str += "----------------\n\n"
	if conn.connected {
		str += "Connected to " + conn.Host + "\n\n"
	} else {
		str += "Not currently connected!\n\n"
	}
	str += conn.Me.String() + "\n"
	str += "GoIRC Channels\n"
	str += "--------------\n\n"
	for _, ch := range conn.chans {
		str += ch.String() + "\n"
	}
	str += "GoIRC NickNames\n"
	str += "---------------\n\n"
	for _, n := range conn.nicks {
		if n != conn.Me {
			str += n.String() + "\n"
		}
	}
	return str
}
<commit_msg>Impolite to break the 'IRC event provides Conn, Line' contract.<commit_after>package client

import (
	"bufio"
	"crypto/tls"
	"github.com/fluffle/goirc/event"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

const (
	second = int64(1e9)
)

// An IRC connection is represented by this struct. Once connected, any errors
// encountered are piped down *Conn.Err; this channel is closed on disconnect.
type Conn struct {
	// Connection Hostname and Nickname
	Host    string
	Me      *Nick
	Network string

	// Event handler registry and dispatcher
	Registry event.EventRegistry
	Dispatcher event.EventDispatcher

	// Map of channels we're on
	chans map[string]*Channel
	// Map of nicks we know about
	nicks map[string]*Nick

	// Use the State field to store external state that handlers might need.
	// Remember ... you might need locking for this ;-)
	State interface{}

	// I/O stuff to server
	sock      net.Conn
	io        *bufio.ReadWriter
	in        chan *Line
	out       chan string
	connected bool

	// Control channels to goroutines
	cSend, cLoop chan bool

	// Error channel to transmit any fail back to the user
	Err chan os.Error

	// Misc knobs to tweak client behaviour:
	// Are we connecting via SSL? Do we care about certificate validity?
	SSL       bool
	SSLConfig *tls.Config

	// Socket timeout, in seconds. Defaulted to 5m in New().
	Timeout int64

	// Set this to true to disable flood protection and false to re-enable
	Flood bool

	// Internal counters for flood protection
	badness, lastsent int64

	// Function which returns a *time.Time for use as a timestamp
	Timestamp func() *time.Time

	// Enable debugging? Set format for timestamps on debug output.
	Debug    bool
	TSFormat string
}

// Creates a new IRC connection object, but doesn't connect to anything so
// that you can add event handlers to it. See AddHandler() for details.
func New(nick, user, name string) *Conn {
	reg := event.NewRegistry()
	conn := &Conn{
		Registry: reg,
		Dispatcher: reg,
		in: make(chan *Line, 32),
		out: make(chan string, 32),
		Err: make(chan os.Error, 4),
		cSend: make(chan bool),
		cLoop: make(chan bool),
		SSL: false,
		SSLConfig: nil,
		Timeout: 300,
		Flood: false,
		badness: 0,
		lastsent: 0,
		Timestamp: time.LocalTime,
		TSFormat: "15:04:05",
	}
	conn.initialise()
	conn.SetupHandlers()
	conn.Me = conn.NewNick(nick, user, name, "")
	return conn
}

// Per-connection state initialisation.
func (conn *Conn) initialise() {
	conn.nicks = make(map[string]*Nick)
	conn.chans = make(map[string]*Channel)
	conn.io = nil
	conn.sock = nil

	// If this is being called because we are reconnecting, conn.Me
	// will still have all the old channels referenced -- nuke them!
	if conn.Me != nil {
		conn.Me = conn.NewNick(conn.Me.Nick, conn.Me.Ident, conn.Me.Name, "")
	}
}

// Connect the IRC connection object to "host[:port]" which should be either
// a hostname or an IP address, with an optional port. To enable explicit SSL
// on the connection to the IRC server, set Conn.SSL to true before calling
// Connect(). The port will default to 6697 if ssl is enabled, and 6667
// otherwise. You can also provide an optional connect password.
func (conn *Conn) Connect(host string, pass ...string) os.Error {
	if conn.connected {
		return os.NewError(fmt.Sprintf(
			"irc.Connect(): already connected to %s, cannot connect to %s",
			conn.Host, host))
	}

	if conn.SSL {
		if !hasPort(host) {
			host += ":6697"
		}
		if s, err := tls.Dial("tcp", host, conn.SSLConfig); err == nil {
			conn.sock = s
		} else {
			return err
		}
	} else {
		if !hasPort(host) {
			host += ":6667"
		}
		if s, err := net.Dial("tcp", host); err == nil {
			conn.sock = s
		} else {
			return err
		}
	}

	conn.Host = host
	conn.io = bufio.NewReadWriter(
		bufio.NewReader(conn.sock),
		bufio.NewWriter(conn.sock))
	conn.sock.SetTimeout(conn.Timeout * second)
	conn.connected = true
	go conn.send()
	go conn.recv()

	if len(pass) > 0 {
		conn.Pass(pass[0])
	}
	conn.Nick(conn.Me.Nick)
	conn.User(conn.Me.Ident, conn.Me.Name)

	go conn.runLoop()
	return nil
}

// dispatch a nicely formatted os.Error to the error channel
func (conn *Conn) error(s string, a ...interface{}) {
	conn.Err <- os.NewError(fmt.Sprintf(s, a...))
}

// copied from http.client for great justice
func hasPort(s string) bool {
	return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
}

// goroutine to pass data from output channel to write()
func (conn *Conn) send() {
	for {
		select {
		case line := <-conn.out:
			conn.write(line)
		case <-conn.cSend:
			// strobe on control channel, bail out
			return
		}
	}
}

// receive one \r\n terminated line from peer, parse and dispatch it
func (conn *Conn) recv() {
	for {
		s, err := conn.io.ReadString('\n')
		if err != nil {
			conn.error("irc.recv(): %s", err.String())
			conn.shutdown()
			break
		}
		s = strings.Trim(s, "\r\n")
		t := conn.Timestamp()
		if conn.Debug {
			fmt.Println(t.Format(conn.TSFormat) + " <- " + s)
		}

		if line := parseLine(s); line != nil {
			line.Time = t
			conn.in <- line
		}
	}
}

// goroutine to dispatch events for lines received on input channel
func (conn *Conn) runLoop() {
	for {
		select {
		case line := <-conn.in:
			conn.Dispatcher.Dispatch(line.Cmd, conn, line)
		case <-conn.cLoop:
			// strobe on control channel, bail out
			return
		}
	}
}

// Write a \r\n terminated line of output to the connected server,
// using Hybrid's algorithm to rate limit if conn.Flood is false.
func (conn *Conn) write(line string) {
	if !conn.Flood {
		conn.rateLimit(int64(len(line)))
	}

	if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
		conn.error("irc.send(): %s", err.String())
		conn.shutdown()
		return
	}
	conn.io.Flush()
	if conn.Debug {
		fmt.Println(conn.Timestamp().Format(conn.TSFormat) + " -> " + line)
	}
}

// Implement Hybrid's flood control algorithm to rate-limit outgoing lines.
func (conn *Conn) rateLimit(chars int64) {
	// Hybrid's algorithm allows for 2 seconds per line and an additional
	// 1/120 of a second per character on that line.
	linetime := 2*second + chars*second/120
	elapsed := time.Nanoseconds() - conn.lastsent
	if conn.badness += linetime - elapsed; conn.badness < 0 {
		// negative badness times are badness...
		conn.badness = int64(0)
	}
	conn.lastsent = time.Nanoseconds()
	// If we've sent more than 10 second's worth of lines according to the
	// calculation above, then we're at risk of "Excess Flood".
	if conn.badness > 10*second && !conn.Flood {
		// so sleep for the current line's time value before sending it
		time.Sleep(linetime)
	}
}

func (conn *Conn) shutdown() {
	// Guard against double-call of shutdown() if we get an error in send()
	// as calling sock.Close() will cause recv() to recieve EOF in readstring()
	if conn.connected {
		conn.connected = false
		conn.sock.Close()
		conn.cSend <- true
		conn.cLoop <- true
		conn.Dispatcher.Dispatch("disconnected", conn, &Line{})
		// reinit datastructures ready for next connection
		// do this here rather than after runLoop()'s for due to race
		conn.initialise()
	}
}

// Dumps a load of information about the current state of the connection to a
// string for debugging state tracking and other such things.
func (conn *Conn) String() string {
	str := "GoIRC Connection\n"
	str += "----------------\n\n"
	if conn.connected {
		str += "Connected to " + conn.Host + "\n\n"
	} else {
		str += "Not currently connected!\n\n"
	}
	str += conn.Me.String() + "\n"
	str += "GoIRC Channels\n"
	str += "--------------\n\n"
	for _, ch := range conn.chans {
		str += ch.String() + "\n"
	}
	str += "GoIRC NickNames\n"
	str += "---------------\n\n"
	for _, n := range conn.nicks {
		if n != conn.Me {
			str += n.String() + "\n"
		}
	}
	return str
}
<|endoftext|>


<commit_before>package grpc

import (
	"fmt"
	"strings"

	b "bytes"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	jsoniter "github.com/json-iterator/go"
	"github.com/micro/go-micro/codec"
	"github.com/micro/go-micro/codec/bytes"
	"github.com/micro/go-micro/codec/jsonrpc"
	"github.com/micro/go-micro/codec/protorpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/encoding"
)

type jsonCodec struct{}
type protoCodec struct{}
type bytesCodec struct{}
type wrapCodec struct{ encoding.Codec }

var jsonpbMarshaler = &jsonpb.Marshaler{}

var (
	defaultGRPCCodecs = map[string]encoding.Codec{
		"application/json":         jsonCodec{},
		"application/proto":        protoCodec{},
		"application/protobuf":     protoCodec{},
		"application/octet-stream": protoCodec{},
		"application/grpc+json":    jsonCodec{},
		"application/grpc+proto":   protoCodec{},
		"application/grpc+bytes":   bytesCodec{},
	}

	defaultRPCCodecs = map[string]codec.NewCodec{
		"application/json":         jsonrpc.NewCodec,
		"application/json-rpc":     jsonrpc.NewCodec,
		"application/protobuf":     protorpc.NewCodec,
		"application/proto-rpc":    protorpc.NewCodec,
		"application/octet-stream": protorpc.NewCodec,
	}

	json = jsoniter.ConfigCompatibleWithStandardLibrary
)

// UseNumber fix unmarshal Number(8234567890123456789) to interface(8.234567890123457e+18)
func UseNumber() {
	json = jsoniter.Config{
		UseNumber:              true,
		EscapeHTML:             true,
		SortMapKeys:            true,
		ValidateJsonRawMessage: true,
	}.Froze()
}

func (w wrapCodec) String() string {
	return w.Codec.Name()
}

func (w wrapCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*bytes.Frame)
	if ok {
		return b.Data, nil
	}
	return w.Codec.Marshal(v)
}

func (w wrapCodec) Unmarshal(data []byte, v interface{}) error {
	b, ok := v.(*bytes.Frame)
	if ok {
		b.Data = data
		return nil
	}
	return w.Codec.Unmarshal(data, v)
}

func (protoCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*bytes.Frame)
	if ok {
		return b.Data, nil
	}
	return proto.Marshal(v.(proto.Message))
}

func (protoCodec) Unmarshal(data []byte, v interface{}) error {
	return proto.Unmarshal(data, v.(proto.Message))
}

func (protoCodec) Name() string {
	return "proto"
}

func (bytesCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*[]byte)
	if !ok {
		return nil, fmt.Errorf("failed to marshal: %v is not type of *[]byte", v)
	}
	return *b, nil
}

func (bytesCodec) Unmarshal(data []byte, v interface{}) error {
	b, ok := v.(*[]byte)
	if !ok {
		return fmt.Errorf("failed to unmarshal: %v is not type of *[]byte", v)
	}
	*b = data
	return nil
}

func (bytesCodec) Name() string {
	return "bytes"
}

func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
	if pb, ok := v.(proto.Message); ok {
		s, err := jsonpbMarshaler.MarshalToString(pb)

		return []byte(s), err
	}

	return json.Marshal(v)
}

func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
	if pb, ok := v.(proto.Message); ok {
		return jsonpb.Unmarshal(b.NewReader(data), pb)
	}

	return json.Unmarshal(data, v)
}

func (jsonCodec) Name() string {
	return "json"
}

type grpcCodec struct {
	// headers
	id       string
	target   string
	method   string
	endpoint string

	s grpc.ClientStream
	c encoding.Codec
}

func (g *grpcCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
	md, err := g.s.Header()
	if err != nil {
		return err
	}
	if m == nil {
		m = new(codec.Message)
	}
	if m.Header == nil {
		m.Header = make(map[string]string)
	}
	for k, v := range md {
		m.Header[k] = strings.Join(v, ",")
	}
	m.Id = g.id
	m.Target = g.target
	m.Method = g.method
	m.Endpoint = g.endpoint
	return nil
}

func (g *grpcCodec) ReadBody(v interface{}) error {
	if f, ok := v.(*bytes.Frame); ok {
		return g.s.RecvMsg(f)
	}
	return g.s.RecvMsg(v)
}

func (g *grpcCodec) Write(m *codec.Message, v interface{}) error {
	// if we don't have a body
	if v != nil {
		return g.s.SendMsg(v)
	}
	// write the body using the framing codec
	return g.s.SendMsg(&bytes.Frame{m.Body})
}

func (g *grpcCodec) Close() error {
	return g.s.CloseSend()
}

func (g *grpcCodec) String() string {
	return g.c.Name()
}
<commit_msg>Add grpc content-type<commit_after>package grpc

import (
	"fmt"
	"strings"

	b "bytes"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	jsoniter "github.com/json-iterator/go"
	"github.com/micro/go-micro/codec"
	"github.com/micro/go-micro/codec/bytes"
	"github.com/micro/go-micro/codec/jsonrpc"
	"github.com/micro/go-micro/codec/protorpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/encoding"
)

type jsonCodec struct{}
type protoCodec struct{}
type bytesCodec struct{}
type wrapCodec struct{ encoding.Codec }

var jsonpbMarshaler = &jsonpb.Marshaler{}

var (
	defaultGRPCCodecs = map[string]encoding.Codec{
		"application/json":         jsonCodec{},
		"application/proto":        protoCodec{},
		"application/protobuf":     protoCodec{},
		"application/octet-stream": protoCodec{},
		"application/grpc":         protoCodec{},
		"application/grpc+json":    jsonCodec{},
		"application/grpc+proto":   protoCodec{},
		"application/grpc+bytes":   bytesCodec{},
	}

	defaultRPCCodecs = map[string]codec.NewCodec{
		"application/json":         jsonrpc.NewCodec,
		"application/json-rpc":     jsonrpc.NewCodec,
		"application/protobuf":     protorpc.NewCodec,
		"application/proto-rpc":    protorpc.NewCodec,
		"application/octet-stream": protorpc.NewCodec,
	}

	json = jsoniter.ConfigCompatibleWithStandardLibrary
)

// UseNumber fix unmarshal Number(8234567890123456789) to interface(8.234567890123457e+18)
func UseNumber() {
	json = jsoniter.Config{
		UseNumber:              true,
		EscapeHTML:             true,
		SortMapKeys:            true,
		ValidateJsonRawMessage: true,
	}.Froze()
}

func (w wrapCodec) String() string {
	return w.Codec.Name()
}

func (w wrapCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*bytes.Frame)
	if ok {
		return b.Data, nil
	}
	return w.Codec.Marshal(v)
}

func (w wrapCodec) Unmarshal(data []byte, v interface{}) error {
	b, ok := v.(*bytes.Frame)
	if ok {
		b.Data = data
		return nil
	}
	return w.Codec.Unmarshal(data, v)
}

func (protoCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*bytes.Frame)
	if ok {
		return b.Data, nil
	}
	return proto.Marshal(v.(proto.Message))
}

func (protoCodec) Unmarshal(data []byte, v interface{}) error {
	return proto.Unmarshal(data, v.(proto.Message))
}

func (protoCodec) Name() string {
	return "proto"
}

func (bytesCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*[]byte)
	if !ok {
		return nil, fmt.Errorf("failed to marshal: %v is not type of *[]byte", v)
	}
	return *b, nil
}

func (bytesCodec) Unmarshal(data []byte, v interface{}) error {
	b, ok := v.(*[]byte)
	if !ok {
		return fmt.Errorf("failed to unmarshal: %v is not type of *[]byte", v)
	}
	*b = data
	return nil
}

func (bytesCodec) Name() string {
	return "bytes"
}

func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
	if pb, ok := v.(proto.Message); ok {
		s, err := jsonpbMarshaler.MarshalToString(pb)

		return []byte(s), err
	}

	return json.Marshal(v)
}

func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
	if pb, ok := v.(proto.Message); ok {
		return jsonpb.Unmarshal(b.NewReader(data), pb)
	}

	return json.Unmarshal(data, v)
}

func (jsonCodec) Name() string {
	return "json"
}

type grpcCodec struct {
	// headers
	id       string
	target   string
	method   string
	endpoint string

	s grpc.ClientStream
	c encoding.Codec
}

func (g *grpcCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
	md, err := g.s.Header()
	if err != nil {
		return err
	}
	if m == nil {
		m = new(codec.Message)
	}
	if m.Header == nil {
		m.Header = make(map[string]string)
	}
	for k, v := range md {
		m.Header[k] = strings.Join(v, ",")
	}
	m.Id = g.id
	m.Target = g.target
	m.Method = g.method
	m.Endpoint = g.endpoint
	return nil
}

func (g *grpcCodec) ReadBody(v interface{}) error {
	if f, ok := v.(*bytes.Frame); ok {
		return g.s.RecvMsg(f)
	}
	return g.s.RecvMsg(v)
}

func (g *grpcCodec) Write(m *codec.Message, v interface{}) error {
	// if we don't have a body
	if v != nil {
		return g.s.SendMsg(v)
	}
	// write the body using the framing codec
	return g.s.SendMsg(&bytes.Frame{m.Body})
}

func (g *grpcCodec) Close() error {
	return g.s.CloseSend()
}

func (g *grpcCodec) String() string {
	return g.c.Name()
}
<|endoftext|>


<commit_before>package lxd

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Event handling functions

// GetEvents connects to the LXD monitoring interface
func (r *ProtocolLXD) GetEvents() (*EventListener, error) {
	// Prevent anything else from interacting with the listeners
	r.eventListenersLock.Lock()
	defer r.eventListenersLock.Unlock()

	// Setup a new listener
	listener := EventListener{
		r:        r,
		chActive: make(chan bool),
	}

	if r.eventListeners != nil {
		// There is an existing Go routine setup, so just add another target
		r.eventListeners = append(r.eventListeners, &listener)
		return &listener, nil
	}

	// Initialize the list if needed
	r.eventListeners = []*EventListener{}

	// Setup a new connection with LXD
	conn, err := r.websocket(fmt.Sprintf("/events?project=%s", r.project))
	if err != nil {
		return nil, err
	}

	// Add the listener
	r.eventListeners = append(r.eventListeners, &listener)

	// Spawn a watcher that will close the websocket connection after all
	// listeners are gone.
	stopCh := make(chan struct{}, 0)
	go func() {
		for {
			select {
			case <-time.After(time.Minute):
			case <-stopCh:
				break
			}

			r.eventListenersLock.Lock()
			if len(r.eventListeners) == 0 {
				// We don't need the connection anymore, disconnect
				conn.Close()

				r.eventListeners = nil
				r.eventListenersLock.Unlock()
				break
			}
			r.eventListenersLock.Unlock()
		}
	}()

	// Spawn the listener
	go func() {
		for {
			_, data, err := conn.ReadMessage()
			if err != nil {
				// Prevent anything else from interacting with the listeners
				r.eventListenersLock.Lock()
				defer r.eventListenersLock.Unlock()

				// Tell all the current listeners about the failure
				for _, listener := range r.eventListeners {
					listener.err = err
					listener.disconnected = true
					close(listener.chActive)
				}

				// And remove them all from the list
				r.eventListeners = []*EventListener{}

				conn.Close()
				close(stopCh)

				return
			}

			// Attempt to unpack the message
			event := api.Event{}
			err = json.Unmarshal(data, &event)
			if err != nil {
				continue
			}

			// Extract the message type
			if event.Type == "" {
				continue
			}

			// Send the message to all handlers
			r.eventListenersLock.Lock()
			for _, listener := range r.eventListeners {
				listener.targetsLock.Lock()
				for _, target := range listener.targets {
					if target.types != nil && !shared.StringInSlice(event.Type, target.types) {
						continue
					}

					go target.function(event)
				}
				listener.targetsLock.Unlock()
			}
			r.eventListenersLock.Unlock()
		}
	}()

	return &listener, nil
}
<commit_msg>client: Properly reset listener on error<commit_after>package lxd

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Event handling functions

// GetEvents connects to the LXD monitoring interface
func (r *ProtocolLXD) GetEvents() (*EventListener, error) {
	// Prevent anything else from interacting with the listeners
	r.eventListenersLock.Lock()
	defer r.eventListenersLock.Unlock()

	// Setup a new listener
	listener := EventListener{
		r:        r,
		chActive: make(chan bool),
	}

	if r.eventListeners != nil {
		// There is an existing Go routine setup, so just add another target
		r.eventListeners = append(r.eventListeners, &listener)
		return &listener, nil
	}

	// Initialize the list if needed
	r.eventListeners = []*EventListener{}

	// Setup a new connection with LXD
	conn, err := r.websocket(fmt.Sprintf("/events?project=%s", r.project))
	if err != nil {
		return nil, err
	}

	// Add the listener
	r.eventListeners = append(r.eventListeners, &listener)

	// Spawn a watcher that will close the websocket connection after all
	// listeners are gone.
	stopCh := make(chan struct{}, 0)
	go func() {
		for {
			select {
			case <-time.After(time.Minute):
			case <-stopCh:
				break
			}

			r.eventListenersLock.Lock()
			if len(r.eventListeners) == 0 {
				// We don't need the connection anymore, disconnect
				conn.Close()

				r.eventListeners = nil
				r.eventListenersLock.Unlock()
				break
			}
			r.eventListenersLock.Unlock()
		}
	}()

	// Spawn the listener
	go func() {
		for {
			_, data, err := conn.ReadMessage()
			if err != nil {
				// Prevent anything else from interacting with the listeners
				r.eventListenersLock.Lock()
				defer r.eventListenersLock.Unlock()

				// Tell all the current listeners about the failure
				for _, listener := range r.eventListeners {
					listener.err = err
					listener.disconnected = true
					close(listener.chActive)
				}

				// And remove them all from the list
				r.eventListeners = nil

				conn.Close()
				close(stopCh)

				return
			}

			// Attempt to unpack the message
			event := api.Event{}
			err = json.Unmarshal(data, &event)
			if err != nil {
				continue
			}

			// Extract the message type
			if event.Type == "" {
				continue
			}

			// Send the message to all handlers
			r.eventListenersLock.Lock()
			for _, listener := range r.eventListeners {
				listener.targetsLock.Lock()
				for _, target := range listener.targets {
					if target.types != nil && !shared.StringInSlice(event.Type, target.types) {
						continue
					}

					go target.function(event)
				}
				listener.targetsLock.Unlock()
			}
			r.eventListenersLock.Unlock()
		}
	}()

	return &listener, nil
}
<|endoftext|>


<commit_before>package lxd

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/shared/api"
)

// The Operation type represents an ongoing LXD operation (asynchronous processing)
type Operation struct {
	api.Operation

	r            *ProtocolLXD
	listener     *EventListener
	handlerReady bool
	handlerLock  sync.Mutex

	chActive chan bool
}

// AddHandler adds a function to be called whenever an event is received
func (op *Operation) AddHandler(function func(api.Operation)) (*EventTarget, error) {
	// Make sure we have a listener setup
	err := op.setupListener()
	if err != nil {
		return nil, err
	}

	// Make sure we're not racing with ourselves
	op.handlerLock.Lock()
	defer op.handlerLock.Unlock()

	// If we're done already, just return
	if op.StatusCode.IsFinal() {
		return nil, nil
	}

	// Wrap the function to filter unwanted messages
	wrapped := func(data interface{}) {
		newOp := op.extractOperation(data)
		if newOp == nil {
			return
		}

		function(*newOp)
	}

	return op.listener.AddHandler([]string{"operation"}, wrapped)
}

// Cancel will request that LXD cancels the operation (if supported)
func (op *Operation) Cancel() error {
	return op.r.DeleteOperation(op.ID)
}

// GetWebsocket returns a raw websocket connection from the operation
func (op *Operation) GetWebsocket(secret string) (*websocket.Conn, error) {
	return op.r.GetOperationWebsocket(op.ID, secret)
}

// RemoveHandler removes a function to be called whenever an event is received
func (op *Operation) RemoveHandler(target *EventTarget) error {
	// Make sure we're not racing with ourselves
	op.handlerLock.Lock()
	defer op.handlerLock.Unlock()

	// If the listener is gone, just return
	if op.listener == nil {
		return nil
	}

	return op.listener.RemoveHandler(target)
}

// Refresh pulls the current version of the operation and updates the struct
func (op *Operation) Refresh() error {
	// Don't bother with a manual update if we are listening for events
	if op.handlerReady {
		return nil
	}

	// Get the current version of the operation
	newOp, _, err := op.r.GetOperation(op.ID)
	if err != nil {
		return err
	}

	// Update the operation struct
	op.Operation = *newOp

	return nil
}

// Wait lets you wait until the operation reaches a final state
func (op *Operation) Wait() error {
	// Check if not done already
	if op.StatusCode.IsFinal() {
		if op.Err != "" {
			return fmt.Errorf(op.Err)
		}

		return nil
	}

	// Make sure we have a listener setup
	err := op.setupListener()
	if err != nil {
		return err
	}

	<-op.chActive

	// We're done, parse the result
	if op.Err != "" {
		return fmt.Errorf(op.Err)
	}

	return nil
}

func (op *Operation) setupListener() error {
	// Make sure we're not racing with ourselves
	op.handlerLock.Lock()
	defer op.handlerLock.Unlock()

	// We already have a listener setup
	if op.handlerReady {
		return nil
	}

	// Get a new listener
	if op.listener == nil {
		listener, err := op.r.GetEvents()
		if err != nil {
			return err
		}

		op.listener = listener
	}

	// Setup the handler
	chReady := make(chan bool)
	_, err := op.listener.AddHandler([]string{"operation"}, func(data interface{}) {
		<-chReady

		// Get an operation struct out of this data
		newOp := op.extractOperation(data)
		if newOp == nil {
			return
		}

		// We don't want concurrency while processing events
		op.handlerLock.Lock()
		defer op.handlerLock.Unlock()

		// Check if we're done already (because of another event)
		if op.listener == nil {
			return
		}

		// Update the struct
		op.Operation = *newOp

		// And check if we're done
		if op.StatusCode.IsFinal() {
			op.listener.Disconnect()
			op.listener = nil
			close(op.chActive)
			return
		}
	})
	if err != nil {
		op.listener.Disconnect()
		op.listener = nil
		close(op.chActive)
		close(chReady)

		return err
	}

	// Monitor event listener
	go func() {
		<-chReady

		// We don't want concurrency while accessing the listener
		op.handlerLock.Lock()

		// Check if we're done already (because of another event)
		if op.listener == nil {
			op.handlerLock.Unlock()
			return
		}
		op.handlerLock.Unlock()

		// Wait for the listener or operation to be done
		select {
		case <-op.listener.chActive:
			op.handlerLock.Lock()
			op.Err = fmt.Sprintf("%v", op.listener.err)
			close(op.chActive)
			op.handlerLock.Unlock()
		case <-op.chActive:
			return
		}
	}()

	// And do a manual refresh to avoid races
	err = op.Refresh()
	if err != nil {
		op.listener.Disconnect()
		op.listener = nil
		close(op.chActive)
		close(chReady)

		return err
	}

	// Check if not done already
	if op.StatusCode.IsFinal() {
		op.listener.Disconnect()
		op.listener = nil
		close(op.chActive)

		op.handlerReady = true
		close(chReady)

		if op.Err != "" {
			return fmt.Errorf(op.Err)
		}

		return nil
	}

	// Start processing background updates
	op.handlerReady = true
	close(chReady)

	return nil
}

func (op *Operation) extractOperation(data interface{}) *api.Operation {
	// Extract the metadata
	meta, ok := data.(map[string]interface{})["metadata"]
	if !ok {
		return nil
	}

	// And attempt to decode it as JSON operation data
	encoded, err := json.Marshal(meta)
	if err != nil {
		return nil
	}

	newOp := api.Operation{}
	err = json.Unmarshal(encoded, &newOp)
	if err != nil {
		return nil
	}

	// And now check that it's what we want
	if newOp.ID != op.ID {
		return nil
	}

	return &newOp
}

// The RemoteOperation type represents an ongoing LXD operation between two servers
type RemoteOperation struct {
	targetOp *Operation

	handlers []func(api.Operation)

	chDone chan bool
	chPost chan bool
	err    error
}

// AddHandler adds a function to be called whenever an event is received
func (op *RemoteOperation) AddHandler(function func(api.Operation)) (*EventTarget, error) {
	var err error
	var target *EventTarget

	// Attach to the existing target operation
	if op.targetOp != nil {
		target, err = op.targetOp.AddHandler(function)
		if err != nil {
			return nil, err
		}
	} else {
		// Generate a mock EventTarget
		target = &EventTarget{
			function: func(interface{}) { function(api.Operation{}) },
			types:    []string{"operation"},
		}
	}

	// Add the handler to our list
	op.handlers = append(op.handlers, function)

	return target, nil
}

// CancelTarget attempts to cancel the target operation
func (op *RemoteOperation) CancelTarget() error {
	if op.targetOp == nil {
		return fmt.Errorf("No associated target operation")
	}

	return op.targetOp.Cancel()
}

// GetTarget returns the target operation
func (op *RemoteOperation) GetTarget() (*api.Operation, error) {
	if op.targetOp == nil {
		return nil, fmt.Errorf("No associated target operation")
	}

	return &op.targetOp.Operation, nil
}

// Wait lets you wait until the operation reaches a final state
func (op *RemoteOperation) Wait() error {
	<-op.chDone

	if op.chPost != nil {
		<-op.chPost
	}

	return op.err
}
<commit_msg>client: Fix crash in operation handler<commit_after>package lxd

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/shared/api"
)

// The Operation type represents an ongoing LXD operation (asynchronous processing)
type Operation struct {
	api.Operation

	r            *ProtocolLXD
	listener     *EventListener
	handlerReady bool
	handlerLock  sync.Mutex

	chActive chan bool
}

// AddHandler adds a function to be called whenever an event is received
func (op *Operation) AddHandler(function func(api.Operation)) (*EventTarget, error) {
	// Make sure we have a listener setup
	err := op.setupListener()
	if err != nil {
		return nil, err
	}

	// Make sure we're not racing with ourselves
	op.handlerLock.Lock()
	defer op.handlerLock.Unlock()

	// If we're done already, just return
	if op.StatusCode.IsFinal() {
		return nil, nil
	}

	// Wrap the function to filter unwanted messages
	wrapped := func(data interface{}) {
		newOp := op.extractOperation(data)
		if newOp == nil {
			return
		}

		function(*newOp)
	}

	return op.listener.AddHandler([]string{"operation"}, wrapped)
}

// Cancel will request that LXD cancels the operation (if supported)
func (op *Operation) Cancel() error {
	return op.r.DeleteOperation(op.ID)
}

// GetWebsocket returns a raw websocket connection from the operation
func (op *Operation) GetWebsocket(secret string) (*websocket.Conn, error) {
	return op.r.GetOperationWebsocket(op.ID, secret)
}

// RemoveHandler removes a function to be called whenever an event is received
func (op *Operation) RemoveHandler(target *EventTarget) error {
	// Make sure we're not racing with ourselves
	op.handlerLock.Lock()
	defer op.handlerLock.Unlock()

	// If the listener is gone, just return
	if op.listener == nil {
		return nil
	}

	return op.listener.RemoveHandler(target)
}

// Refresh pulls the current version of the operation and updates the struct
func (op *Operation) Refresh() error {
	// Don't bother with a manual update if we are listening for events
	if op.handlerReady {
		return nil
	}

	// Get the current version of the operation
	newOp, _, err := op.r.GetOperation(op.ID)
	if err != nil {
		return err
	}

	// Update the operation struct
	op.Operation = *newOp

	return nil
}

// Wait lets you wait until the operation reaches a final state
func (op *Operation) Wait() error {
	// Check if not done already
	if op.StatusCode.IsFinal() {
		if op.Err != "" {
			return fmt.Errorf(op.Err)
		}

		return nil
	}

	// Make sure we have a listener setup
	err := op.setupListener()
	if err != nil {
		return err
	}

	<-op.chActive

	// We're done, parse the result
	if op.Err != "" {
		return fmt.Errorf(op.Err)
	}

	return nil
}

func (op *Operation) setupListener() error {
	// Make sure we're not racing with ourselves
	op.handlerLock.Lock()
	defer op.handlerLock.Unlock()

	// We already have a listener setup
	if op.handlerReady {
		return nil
	}

	// Get a new listener
	if op.listener == nil {
		listener, err := op.r.GetEvents()
		if err != nil {
			return err
		}

		op.listener = listener
	}

	// Setup the handler
	chReady := make(chan bool)
	_, err := op.listener.AddHandler([]string{"operation"}, func(data interface{}) {
		<-chReady

		// Get an operation struct out of this data
		newOp := op.extractOperation(data)
		if newOp == nil {
			return
		}

		// We don't want concurrency while processing events
		op.handlerLock.Lock()
		defer op.handlerLock.Unlock()

		// Check if we're done already (because of another event)
		if op.listener == nil {
			return
		}

		// Update the struct
		op.Operation = *newOp

		// And check if we're done
		if op.StatusCode.IsFinal() {
			op.listener.Disconnect()
			op.listener = nil
			close(op.chActive)
			return
		}
	})
	if err != nil {
		op.listener.Disconnect()
		op.listener = nil
		close(op.chActive)
		close(chReady)

		return err
	}

	// Monitor event listener
	go func() {
		<-chReady

		// We don't want concurrency while accessing the listener
		op.handlerLock.Lock()

		// Check if we're done already (because of another event)
		listener := op.listener
		if listener == nil {
			op.handlerLock.Unlock()
			return
		}
		op.handlerLock.Unlock()

		// Wait for the listener or operation to be done
		select {
		case <-listener.chActive:
			op.handlerLock.Lock()
			if op.listener != nil {
				op.Err = fmt.Sprintf("%v", listener.err)
				close(op.chActive)
			}
			op.handlerLock.Unlock()
		case <-op.chActive:
			return
		}
	}()

	// And do a manual refresh to avoid races
	err = op.Refresh()
	if err != nil {
		op.listener.Disconnect()
		op.listener = nil
		close(op.chActive)
		close(chReady)

		return err
	}

	// Check if not done already
	if op.StatusCode.IsFinal() {
		op.listener.Disconnect()
		op.listener = nil
		close(op.chActive)

		op.handlerReady = true
		close(chReady)

		if op.Err != "" {
			return fmt.Errorf(op.Err)
		}

		return nil
	}

	// Start processing background updates
	op.handlerReady = true
	close(chReady)

	return nil
}

func (op *Operation) extractOperation(data interface{}) *api.Operation {
	// Extract the metadata
	meta, ok := data.(map[string]interface{})["metadata"]
	if !ok {
		return nil
	}

	// And attempt to decode it as JSON operation data
	encoded, err := json.Marshal(meta)
	if err != nil {
		return nil
	}

	newOp := api.Operation{}
	err = json.Unmarshal(encoded, &newOp)
	if err != nil {
		return nil
	}

	// And now check that it's what we want
	if newOp.ID != op.ID {
		return nil
	}

	return &newOp
}

// The RemoteOperation type represents an ongoing LXD operation between two servers
type RemoteOperation struct {
	targetOp *Operation

	handlers []func(api.Operation)

	chDone chan bool
	chPost chan bool
	err    error
}

// AddHandler adds a function to be called whenever an event is received
func (op *RemoteOperation) AddHandler(function func(api.Operation)) (*EventTarget, error) {
	var err error
	var target *EventTarget

	// Attach to the existing target operation
	if op.targetOp != nil {
		target, err = op.targetOp.AddHandler(function)
		if err != nil {
			return nil, err
		}
	} else {
		// Generate a mock EventTarget
		target = &EventTarget{
			function: func(interface{}) { function(api.Operation{}) },
			types:    []string{"operation"},
		}
	}

	// Add the handler to our list
	op.handlers = append(op.handlers, function)

	return target, nil
}

// CancelTarget attempts to cancel the target operation
func (op *RemoteOperation) CancelTarget() error {
	if op.targetOp == nil {
		return fmt.Errorf("No associated target operation")
	}

	return op.targetOp.Cancel()
}

// GetTarget returns the target operation
func (op *RemoteOperation) GetTarget() (*api.Operation, error) {
	if op.targetOp == nil {
		return nil, fmt.Errorf("No associated target operation")
	}

	return &op.targetOp.Operation, nil
}

// Wait lets you wait until the operation reaches a final state
func (op *RemoteOperation) Wait() error {
	<-op.chDone

	if op.chPost != nil {
		<-op.chPost
	}

	return op.err
}
<|endoftext|>


<commit_before><commit_msg>Enable sync<commit_after><|endoftext|>


<commit_before>package contact

import (
	"math"

	"github.com/hueypark/physics/core/body"
	"github.com/hueypark/physics/core/closest_point"
	"github.com/hueypark/physics/core/math/vector"
	"github.com/hueypark/physics/core/shape"
	"github.com/hueypark/physics/core/shape/circle"
	"github.com/hueypark/physics/core/shape/convex"
)

func (c *Contact) DetectCollision() {
	lhsType := c.lhs.Shape.Type()
	rhsType := c.rhs.Shape.Type()

	switch lhsType {
	case shape.BULLET:
		switch rhsType {
		case shape.BULLET:
			break
		case shape.CIRCLE:
			c.bulletToCircle(c.rhs.Shape.(*circle.Circle))
			break
		case shape.CONVEX:
			c.normal, c.penetration, c.points = bulletToConvex(c.lhs, c.rhs)
			break
		}
		break
	case shape.CIRCLE:
		switch rhsType {
		case shape.BULLET:
			c.circleToBullet(c.lhs.Shape.(*circle.Circle))
			break
		case shape.CIRCLE:
			c.circleToCircle(c.lhs.Shape.(*circle.Circle), c.rhs.Shape.(*circle.Circle))
			break
		case shape.CONVEX:
			c.normal, c.penetration, c.points = circleToConvex(c.lhs, c.rhs)
			break
		}
		break
	case shape.CONVEX:
		switch rhsType {
		case shape.BULLET:
			c.swap()
			c.normal, c.penetration, c.points = bulletToConvex(c.lhs, c.rhs)
		case shape.CIRCLE:
			c.swap()
			c.normal, c.penetration, c.points = circleToConvex(c.lhs, c.rhs)
			break
		case shape.CONVEX:
			c.normal, c.penetration, c.points = convexToConvex(c.lhs, c.rhs)
			break
		}
		break
	}
}

func (c *Contact) swap() {
	c.lhs, c.rhs = c.rhs, c.lhs
}

func (c *Contact) bulletToCircle(rhs *circle.Circle) {
	c.normal = vector.Subtract(c.rhs.Position(), c.lhs.Position())

	distanceSquared := c.normal.SizeSquared()

	if distanceSquared >= rhs.Radius*rhs.Radius {
		return
	}

	distance := math.Sqrt(distanceSquared)

	c.penetration = rhs.Radius - distance
	c.normal.Normalize()
	c.points = append(c.points, c.lhs.Position())
}

func (c *Contact) circleToBullet(lhs *circle.Circle) {
	c.normal = vector.Subtract(c.rhs.Position(), c.lhs.Position())

	distanceSquared := c.normal.SizeSquared()

	if distanceSquared >= lhs.Radius*lhs.Radius {
		return
	}

	distance := math.Sqrt(distanceSquared)

	c.penetration = lhs.Radius - distance
	c.normal.Normalize()
	c.points = append(c.points, c.lhs.Position())
}

func bulletToConvex(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	rhsConvex := rhs.Shape.(*convex.Convex)

	penetration = math.MaxFloat64

	for _, edge := range rhsConvex.Edges() {
		worldStart := rhs.Rotation().RotateVector(edge.Start)
		worldStart = vector.Add(rhs.Position(), worldStart)
		worldEnd := rhs.Rotation().RotateVector(edge.End)
		worldEnd = vector.Add(rhs.Position(), worldEnd)
		edgeVector := vector.Subtract(worldEnd, worldStart)
		pointVector := vector.Subtract(lhs.Position(), worldStart)

		if !pointVector.OnTheRight(edgeVector) {
			normal = vector.Vector{}
			penetration = 0
			return normal, penetration, points
		}

		perpendicular := vector.Vector{-edgeVector.Y, edgeVector.X}
		perpendicular.Normalize()

		lhsVector := vector.Subtract(lhs.Position(), worldStart)

		proj := vector.Multiply(perpendicular, vector.Dot(lhsVector, perpendicular))

		if proj.Size() < penetration {
			normal = perpendicular
			penetration = proj.Size()
		}
	}

	points = append(points, lhs.Position())
	return normal, penetration, points
}

func (c *Contact) circleToCircle(lhsCircle, rhsCircle *circle.Circle) {
	c.normal = vector.Subtract(c.rhs.Position(), c.lhs.Position())

	distanceSquared := c.normal.SizeSquared()
	radius := lhsCircle.Radius + rhsCircle.Radius

	if distanceSquared >= radius*radius {
		return
	}

	distance := math.Sqrt(distanceSquared)

	c.penetration = radius - distance
	c.normal.Normalize()
	c.points = append(c.points, vector.Add(vector.Multiply(c.normal, lhsCircle.Radius), c.lhs.Position()))
}

func circleToConvex(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	lhsCircle := lhs.Shape.(*circle.Circle)
	rhsConvex := rhs.Shape.(*convex.Convex)

	penetration = math.MaxFloat64
	var selectedEdge convex.Edge

	for _, edge := range rhsConvex.Edges() {
		worldStart := rhs.Rotation().RotateVector(edge.Start)
		worldStart = vector.Add(rhs.Position(), worldStart)
		worldEnd := rhs.Rotation().RotateVector(edge.End)
		worldEnd = vector.Add(rhs.Position(), worldEnd)
		edgeVector := vector.Subtract(worldEnd, worldStart)

		perpendicular := vector.Vector{-edgeVector.Y, edgeVector.X}
		perpendicular.Normalize()

		lhsVector := vector.Subtract(lhs.Position(), worldStart)

		projSize := vector.Dot(lhsVector, perpendicular)

		if projSize < -lhsCircle.Radius {
			return vector.ZERO(), 0, points
		}

		if projSize < penetration {
			normal = perpendicular
			penetration = projSize
			selectedEdge = edge
		}
	}

	worldStart := rhs.Rotation().RotateVector(selectedEdge.Start)
	worldStart = vector.Add(rhs.Position(), worldStart)
	worldEnd := rhs.Rotation().RotateVector(selectedEdge.End)
	worldEnd = vector.Add(rhs.Position(), worldEnd)
	closestPoint := closest_point.LineSegmentToPoint(lhs.Position(), worldStart, worldEnd)
	if lhsCircle.Radius*lhsCircle.Radius < vector.Subtract(lhs.Position(), closestPoint).SizeSquared() {
		return vector.ZERO(), 0, points
	}

	points = append(points, closestPoint)

	return normal, penetration, points
}

func convexToConvex(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	lhsConvex := lhs.Shape.(*convex.Convex)
	rhsConvex := rhs.Shape.(*convex.Convex)

	minkowskiDifference := convex.MinkowskiDifference(*rhsConvex, rhs.Position(), rhs.Rotation(), *lhsConvex, lhs.Position(), lhs.Rotation())

	penetration = math.MaxFloat64

	for _, edge := range minkowskiDifference.Edges() {
		edgeVector := vector.Subtract(edge.End, edge.Start)
		pointVector := vector.Subtract(vector.ZERO(), edge.Start)

		if !pointVector.OnTheRight(edgeVector) {
			normal = vector.Vector{}
			penetration = 0
			return normal, penetration, points
		}

		perpendicular := vector.Vector{-edgeVector.Y, edgeVector.X}
		perpendicular.Normalize()

		lhsVector := vector.Subtract(vector.Vector{}, edge.Start)

		proj := vector.Multiply(perpendicular, vector.Dot(lhsVector, perpendicular))

		if proj.Size() < penetration {
			normal = perpendicular
			penetration = proj.Size()
		}
	}

	for _, point := range lhsConvex.Hull() {
		worldPoint := vector.Add(lhs.Position(), lhs.Rotation().RotateVector(point))
		if rhsConvex.InHull(rhs.Position(), rhs.Rotation(), worldPoint) {
			points = append(points, worldPoint)
		}
	}

	for _, point := range rhsConvex.Hull() {
		worldPoint := vector.Add(rhs.Position(), rhs.Rotation().RotateVector(point))
		if lhsConvex.InHull(lhs.Position(), lhs.Rotation(), worldPoint) {
			points = append(points, worldPoint)
		}
	}

	return normal, penetration, points
}
<commit_msg>Refactor detect_collision<commit_after>package contact

import (
	"math"

	"github.com/hueypark/physics/core/body"
	"github.com/hueypark/physics/core/closest_point"
	"github.com/hueypark/physics/core/math/vector"
	"github.com/hueypark/physics/core/shape"
	"github.com/hueypark/physics/core/shape/circle"
	"github.com/hueypark/physics/core/shape/convex"
)

func (c *Contact) DetectCollision() {
	lhsType := c.lhs.Shape.Type()
	rhsType := c.rhs.Shape.Type()

	switch lhsType {
	case shape.BULLET:
		switch rhsType {
		case shape.BULLET:
			break
		case shape.CIRCLE:
			c.normal, c.penetration, c.points = bulletToCircle(c.lhs, c.rhs)
			break
		case shape.CONVEX:
			c.normal, c.penetration, c.points = bulletToConvex(c.lhs, c.rhs)
			break
		}
		break
	case shape.CIRCLE:
		switch rhsType {
		case shape.BULLET:
			c.swap()
			c.normal, c.penetration, c.points = bulletToCircle(c.lhs, c.rhs)
			break
		case shape.CIRCLE:
			c.normal, c.penetration, c.points = circleToCircle(c.lhs, c.rhs)
			break
		case shape.CONVEX:
			c.normal, c.penetration, c.points = circleToConvex(c.lhs, c.rhs)
			break
		}
		break
	case shape.CONVEX:
		switch rhsType {
		case shape.BULLET:
			c.swap()
			c.normal, c.penetration, c.points = bulletToConvex(c.lhs, c.rhs)
		case shape.CIRCLE:
			c.swap()
			c.normal, c.penetration, c.points = circleToConvex(c.lhs, c.rhs)
			break
		case shape.CONVEX:
			c.normal, c.penetration, c.points = convexToConvex(c.lhs, c.rhs)
			break
		}
		break
	}
}

func (c *Contact) swap() {
	c.lhs, c.rhs = c.rhs, c.lhs
}

func bulletToCircle(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	rhsCircle := rhs.Shape.(*circle.Circle)

	normal = vector.Subtract(rhs.Position(), lhs.Position())

	distanceSquared := normal.SizeSquared()

	if distanceSquared >= rhsCircle.Radius*rhsCircle.Radius {
		return
	}

	distance := math.Sqrt(distanceSquared)

	normal.Normalize()
	penetration = rhsCircle.Radius - distance
	points = append(points, lhs.Position())

	return normal, penetration, points
}

func bulletToConvex(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	rhsConvex := rhs.Shape.(*convex.Convex)

	penetration = math.MaxFloat64

	for _, edge := range rhsConvex.Edges() {
		worldStart := rhs.Rotation().RotateVector(edge.Start)
		worldStart = vector.Add(rhs.Position(), worldStart)
		worldEnd := rhs.Rotation().RotateVector(edge.End)
		worldEnd = vector.Add(rhs.Position(), worldEnd)
		edgeVector := vector.Subtract(worldEnd, worldStart)
		pointVector := vector.Subtract(lhs.Position(), worldStart)

		if !pointVector.OnTheRight(edgeVector) {
			normal = vector.Vector{}
			penetration = 0
			return normal, penetration, points
		}

		perpendicular := vector.Vector{-edgeVector.Y, edgeVector.X}
		perpendicular.Normalize()

		lhsVector := vector.Subtract(lhs.Position(), worldStart)

		proj := vector.Multiply(perpendicular, vector.Dot(lhsVector, perpendicular))

		if proj.Size() < penetration {
			normal = perpendicular
			penetration = proj.Size()
		}
	}

	points = append(points, lhs.Position())

	return normal, penetration, points
}

func circleToCircle(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	lhsCircle := lhs.Shape.(*circle.Circle)
	rhsCircle := rhs.Shape.(*circle.Circle)

	normal = vector.Subtract(rhs.Position(), lhs.Position())

	distanceSquared := normal.SizeSquared()
	radius := lhsCircle.Radius + rhsCircle.Radius

	if distanceSquared >= radius*radius {
		return
	}

	distance := math.Sqrt(distanceSquared)

	normal.Normalize()
	penetration = radius - distance
	points = append(points, vector.Add(vector.Multiply(normal, lhsCircle.Radius), lhs.Position()))

	return normal, penetration, points
}

func circleToConvex(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	lhsCircle := lhs.Shape.(*circle.Circle)
	rhsConvex := rhs.Shape.(*convex.Convex)

	penetration = math.MaxFloat64
	var selectedEdge convex.Edge

	for _, edge := range rhsConvex.Edges() {
		worldStart := rhs.Rotation().RotateVector(edge.Start)
		worldStart = vector.Add(rhs.Position(), worldStart)
		worldEnd := rhs.Rotation().RotateVector(edge.End)
		worldEnd = vector.Add(rhs.Position(), worldEnd)
		edgeVector := vector.Subtract(worldEnd, worldStart)

		perpendicular := vector.Vector{-edgeVector.Y, edgeVector.X}
		perpendicular.Normalize()

		lhsVector := vector.Subtract(lhs.Position(), worldStart)

		projSize := vector.Dot(lhsVector, perpendicular)

		if projSize < -lhsCircle.Radius {
			return vector.ZERO(), 0, points
		}

		if projSize < penetration {
			normal = perpendicular
			penetration = projSize
			selectedEdge = edge
		}
	}

	worldStart := rhs.Rotation().RotateVector(selectedEdge.Start)
	worldStart = vector.Add(rhs.Position(), worldStart)
	worldEnd := rhs.Rotation().RotateVector(selectedEdge.End)
	worldEnd = vector.Add(rhs.Position(), worldEnd)
	closestPoint := closest_point.LineSegmentToPoint(lhs.Position(), worldStart, worldEnd)
	if lhsCircle.Radius*lhsCircle.Radius < vector.Subtract(lhs.Position(), closestPoint).SizeSquared() {
		return vector.ZERO(), 0, points
	}

	points = append(points, closestPoint)

	return normal, penetration, points
}

func convexToConvex(lhs, rhs *body.Body) (normal vector.Vector, penetration float64, points []vector.Vector) {
	lhsConvex := lhs.Shape.(*convex.Convex)
	rhsConvex := rhs.Shape.(*convex.Convex)

	minkowskiDifference := convex.MinkowskiDifference(*rhsConvex, rhs.Position(), rhs.Rotation(), *lhsConvex, lhs.Position(), lhs.Rotation())

	penetration = math.MaxFloat64

	for _, edge := range minkowskiDifference.Edges() {
		edgeVector := vector.Subtract(edge.End, edge.Start)
		pointVector := vector.Subtract(vector.ZERO(), edge.Start)

		if !pointVector.OnTheRight(edgeVector) {
			normal = vector.Vector{}
			penetration = 0
			return normal, penetration, points
		}

		perpendicular := vector.Vector{-edgeVector.Y, edgeVector.X}
		perpendicular.Normalize()

		lhsVector := vector.Subtract(vector.Vector{}, edge.Start)

		proj := vector.Multiply(perpendicular, vector.Dot(lhsVector, perpendicular))

		if proj.Size() < penetration {
			normal = perpendicular
			penetration = proj.Size()
		}
	}

	for _, point := range lhsConvex.Hull() {
		worldPoint := vector.Add(lhs.Position(), lhs.Rotation().RotateVector(point))
		if rhsConvex.InHull(rhs.Position(), rhs.Rotation(), worldPoint) {
			points = append(points, worldPoint)
		}
	}

	for _, point := range rhsConvex.Hull() {
		worldPoint := vector.Add(rhs.Position(), rhs.Rotation().RotateVector(point))
		if lhsConvex.InHull(lhs.Position(), lhs.Rotation(), worldPoint) {
			points = append(points, worldPoint)
		}
	}

	return normal, penetration, points
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"strings"
)

const (
	defQueryType  = "ANY"
	defQueryClass = "IN"
	defProtocol   = "UDP"
)

var (
	eDNS0 = false
)

// checkQueryParam checks against possible list of parameters
func checkQueryParam(arg string, list map[string]bool) bool {
	_, ok := list[strings.ToUpper(arg)]
	return ok
}

// init injects our "dns" related commands/options.
func init() {
	// Fill-in the various commands
	cliCommands = append(cliCommands, cli.Command{
		Name:        "dns",
		Usage:       "send dns queries",
		Description: "send DNS queries about an host/IP/domain\n   use: <Q> [<TYPE> [<CLASS>]]",
		Aliases: []string{
			"dig",
			"drill",
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:        "E, edns0",
				Usage:       "use EDNS0",
				Destination: &eDNS0,
			},
			cli.BoolFlag{
				Name:        "D, disable-dnssec",
				Usage:       "Do not try to validate DNSSEC RR",
				Destination: &fDisableDNSSEC,
			},
			cli.BoolFlag{
				Name:        "C, disable-dnssec-checks",
				Usage:       "Do not try to validate DNSSEC Check by probes",
				Destination: &fBitCD,
			},
			cli.StringFlag{
				Name:        "p, protocol",
				Usage:       "Select UDP or TCP",
				Destination: &fProtocol,
			},
		},
		Action: cmdDNS,
	})
}

func prepareDNS(proto, qa, qc, qt string, do, cd bool) (req *atlas.MeasurementRequest) {
	opts := map[string]string{
		"Type":          "dns",
		"Description":   fmt.Sprintf("DNS - %s", qa),
		"Protocol":      proto,
		"QueryArgument": qa,
		"QueryClass":    qc,
		"QueryType":     qt,
		"SetDOBit":      boolToString(do),
		"SetCDBit":      boolToString(cd),
	}

	if eDNS0 {
		opts["UDPPayloadSize"] = "4096"
		opts["Protocol"] = "UDP"
	} else {
		opts["UDPPayloadSize"] = "512"
	}

	req = client.NewMeasurement()
	if mycnf.WantAF == WantBoth {

		opts["AF"] = "4"
		req.AddDefinition(opts)

		opts["AF"] = "6"
		req.AddDefinition(opts)
	} else {
		opts["AF"] = mycnf.WantAF
		req.AddDefinition(opts)
	}

	// Check global parameters
	opts = checkGlobalFlags(opts)

	if fVerbose {
		displayOptions(opts)
	}

	return
}

func cmdDNS(c *cli.Context) error {
	var (
		bitDO  = true
		bitCD  = false
		qtype  = defQueryType
		qclass = defQueryClass
		proto  = defProtocol

		addr string
	)

	args := c.Args()
	if args == nil || len(args) == 0 {
		log.Fatal("Error: you must specify at least a name")
	}

	qtype = defQueryType
	qclass = defQueryClass
	proto = defProtocol

	if len(args) == 1 {
		addr = args[0]
	} else if len(args) == 2 {
		addr = args[0]
		qtype = args[1]
	} else if len(args) == 3 {
		addr = args[0]
		qtype = args[1]
		qclass = args[2]
	}

	if fProtocol != "" {
		log.Printf("Use %s", fProtocol)
		proto = fProtocol
	}

	if fDisableDNSSEC {
		bitDO = false
	}

	if fBitCD {
		bitCD = true
	}

	req := prepareDNS(proto, addr, qclass, qtype, bitDO, bitCD)

	log.Printf("req=%#v", req)
	m, err := client.DNS(req)
	if err != nil {
		fmt.Printf("err: %v", err)
		os.Exit(1)
	}

	//str := res.Result.Display()
	fmt.Printf("m: %v\n", m)

	return nil
}
<commit_msg>Comment out this, we'll revisit later.<commit_after>package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"strings"
)

const (
	defQueryType  = "ANY"
	defQueryClass = "IN"
	defProtocol   = "UDP"
)

var (
	eDNS0 = false
)

// checkQueryParam checks against possible list of parameters
func checkQueryParam(arg string, list map[string]bool) bool {
	_, ok := list[strings.ToUpper(arg)]
	return ok
}

// init injects our "dns" related commands/options.
func init() {
	// Fill-in the various commands
	cliCommands = append(cliCommands, cli.Command{
		Name:        "dns",
		Usage:       "send dns queries",
		Description: "send DNS queries about an host/IP/domain\n   use: <Q> [<TYPE> [<CLASS>]]",
		Aliases: []string{
			"dig",
			"drill",
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:        "E, edns0",
				Usage:       "use EDNS0",
				Destination: &eDNS0,
			},
			cli.BoolFlag{
				Name:        "D, disable-dnssec",
				Usage:       "Do not try to validate DNSSEC RR",
				Destination: &fDisableDNSSEC,
			},
			cli.BoolFlag{
				Name:        "C, disable-dnssec-checks",
				Usage:       "Do not try to validate DNSSEC Check by probes",
				Destination: &fBitCD,
			},
			cli.StringFlag{
				Name:        "p, protocol",
				Usage:       "Select UDP or TCP",
				Destination: &fProtocol,
			},
		},
		Action: cmdDNS,
	})
}

func prepareDNS(proto, qa, qc, qt string, do, cd bool) (req *atlas.MeasurementRequest) {
	opts := map[string]string{
		"Type":          "dns",
		"Description":   fmt.Sprintf("DNS - %s", qa),
		"Protocol":      proto,
		"QueryArgument": qa,
		"QueryClass":    qc,
		"QueryType":     qt,
		"SetDOBit":      boolToString(do),
		"SetCDBit":      boolToString(cd),
	}

	if eDNS0 {
		opts["UDPPayloadSize"] = "4096"
		opts["Protocol"] = "UDP"
	} else {
		opts["UDPPayloadSize"] = "512"
	}

	/*if mycnf.WantAF == WantBoth {

		opts["AF"] = "4"
		req.AddDefinition(opts)

		opts["AF"] = "6"
		req.AddDefinition(opts)
	} else {
		opts["AF"] = mycnf.WantAF
		req.AddDefinition(opts)
	}*/

	// Check global parameters
	opts = checkGlobalFlags(opts)

	if fVerbose {
		displayOptions(opts)
	}

	return
}

func cmdDNS(c *cli.Context) error {
	var (
		bitDO  = true
		bitCD  = false
		qtype  = defQueryType
		qclass = defQueryClass
		proto  = defProtocol

		addr string
	)

	args := c.Args()
	if args == nil || len(args) == 0 {
		log.Fatal("Error: you must specify at least a name")
	}

	qtype = defQueryType
	qclass = defQueryClass
	proto = defProtocol

	if len(args) == 1 {
		addr = args[0]
	} else if len(args) == 2 {
		addr = args[0]
		qtype = args[1]
	} else if len(args) == 3 {
		addr = args[0]
		qtype = args[1]
		qclass = args[2]
	}

	if fProtocol != "" {
		log.Printf("Use %s", fProtocol)
		proto = fProtocol
	}

	if fDisableDNSSEC {
		bitDO = false
	}

	if fBitCD {
		bitCD = true
	}

	req := prepareDNS(proto, addr, qclass, qtype, bitDO, bitCD)

	log.Printf("req=%#v", req)
	m, err := client.DNS(req)
	if err != nil {
		fmt.Printf("err: %v", err)
		os.Exit(1)
	}

	//str := res.Result.Display()
	fmt.Printf("m: %v\n", m)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	_ "image/jpeg"
	"image/png"
	"io/ioutil"
	"os"

	"github.com/golang/freetype/truetype"
	"github.com/nfnt/resize"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

func mustGetImage(path string) image.Image {
	image, err := getImage(path)
	if err != nil {
		panic(err)
	}
	return image
}

func getImage(path string) (image.Image, error) {
	imageFd, err := os.Open(path)
	if err != nil {
		return image.Black, err
	}
	defer imageFd.Close()

	img, _, err := image.Decode(imageFd)
	if err != nil {
		return image.Black, err
	}
	return img, nil
}

func generateBasicTemplate() draw.Image {
	templateImage := mustGetImage("template.png")
	destinationImage := image.NewNRGBA(templateImage.Bounds())

	// put base template into our destination
	draw.Draw(
		destinationImage,
		destinationImage.Bounds(),
		templateImage,
		image.ZP,
		draw.Src,
	)
	return destinationImage
}

type backgroundConf struct {
	Path      string
	Placement placement
}

func writeBackground(backgroundConfig backgroundConf, destinationImage draw.Image) draw.Image {
	templateMask := mustGetImage("template_mask.png")
	if backgroundConfig.Path == "" {
		backgroundConfig.Path = "background"
	}
	backgroundImage := mustGetImage(backgroundConfig.Path)

	// resize to the size of the template
	backgroundImage = resize.Resize(
		// scale to the width of the template
		comicWidth,
		0,
		backgroundImage,
		resize.Bilinear,
	)
	backgroundImageHeight := backgroundImage.Bounds().Dy()
	backgroundSegmentSize := backgroundImageHeight / 5
	backgroundStartingY := (int(backgroundConfig.Placement) - 1) * backgroundSegmentSize

	// if the placement makes the image not fully fit in the template, align the bottom edge with the bottom edge of the template
	if destinationImageHeight, pixelsInImage := destinationImage.Bounds().Dy(), backgroundImageHeight-backgroundStartingY; pixelsInImage < destinationImageHeight {
		backgroundStartingY = backgroundImageHeight - destinationImageHeight
	}

	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		backgroundImage,
		image.Pt(0, backgroundStartingY),
		templateMask,
		image.ZP,
		draw.Over,
	)

	return destinationImage
}

func getFont() *truetype.Font {
	fontFd, err := os.Open("Loveletter_TW.ttf")
	if err != nil {
		panic(err)
	}

	fontBytes, err := ioutil.ReadAll(fontFd)
	if err != nil {
		panic(err)
	}

	font, err := truetype.Parse(fontBytes)
	if err != nil {
		panic(err)
	}

	return font
}

const (
	comicWidth            = 720
	comicHeight           = 275
	fontSize              = 14.0
	textBackgroundPadding = 3
	numPlacements         = 5
	baselineX             = 30
)

func baselinePointForPlacement(place placement) image.Point {
	segmentSize := panelRectangle.Dy() / numPlacements

	// multiply the number of segments above (which corresponds to the number of the placement minus 1)
	// then add half a segment to put it in the middle of that (this helps put it not right next to edges)
	baselineY := (int(place)-1)*segmentSize + segmentSize/2
	return image.Pt(baselineX, baselineY)
}

func withPadding(rect image.Rectangle, padding int) image.Rectangle {
	return image.Rect(
		rect.Min.X-padding,
		rect.Min.Y-padding,
		rect.Max.X+padding,
		rect.Max.Y+padding,
	)
}

var panelToTopLeft = map[int]image.Point{
	0: image.Pt(13, 37),
	1: image.Pt(254, 37),
	2: image.Pt(493, 38),
}

var panelRectangle = image.Rect(
	0, 0,
	212, 216,
)

var panelToRectangle = func() map[int]image.Rectangle {
	m := make(map[int]image.Rectangle)
	for panelNumber, topLeft := range panelToTopLeft {
		m[panelNumber] = panelRectangle.Add(topLeft)
	}
	return m
}()

func copyImage(img image.Image) draw.Image {
	// create a new image
	copyTo := image.NewNRGBA(img.Bounds())

	// copy stuff to that image
	draw.Draw(
		copyTo,
		copyTo.Bounds(),
		img,
		image.ZP,
		draw.Src,
	)
	return copyTo
}

func writeTextList(textConfigList []textConf, destinationImage draw.Image) draw.Image {
	// copy for easier semantics
	destinationImage = copyImage(destinationImage)

	for i, textConfig := range textConfigList {
		// writing an empty string still does a background, so let's not do that
		if textConfig.Text == "" {
			continue
		}
		// create text image for panel
		textImage := writeSingleText(textConfig)
		// write text image on top of panel
		draw.DrawMask(
			destinationImage,
			panelToRectangle[i],
			textImage,
			image.ZP,
			image.Black,
			image.ZP,
			draw.Over,
		)
	}
	return destinationImage
}

// between -10 and 10 pixel offset
const offsetBound = 21

func hashString(text string, reduce func(left, right rune) rune) int {
	var accumulator rune
	for _, ch := range text {
		accumulator = reduce(accumulator, ch)
	}
	return int(accumulator)
}

func choosePlacement(text string) placement {
	hash := hashString(text, func(left, right rune) rune { return left | right })
	// mod by the number of placements and then add one to not get noPlacement
	return placement((hash % numPlacements) + 1)
}

func offset(text string, reduce func(left, right rune) rune) int {
	hash := hashString(text, reduce)
	return int(hash%offsetBound - (offsetBound / 2))
}

func offsetX(text string) int {
	return offset(text, func(left, right rune) rune { return left * right })
}

func offsetY(text string) int {
	return offset(text, func(left, right rune) rune { return left + right })
}

type placement int

const (
	noPlacement placement = iota
	topPlacement
	topMiddlePlacement
	middlePlacement
	bottomMiddlePlacement
	bottomPlacement
)

type textConf struct {
	Text      string    `json:"text"`
	Placement placement `json:"placement"`
}

func writeSingleText(textConfig textConf) draw.Image {
	// create a panel image to draw our text to
	destinationImage := image.NewNRGBA(panelRectangle)

	// create font face for our font
	fontFace := truetype.NewFace(
		getFont(),
		&truetype.Options{Size: fontSize},
	)

	// create a drawer to draw the text starting at the baseline point, in the font and measure the distance of the string
	drawDistance := (&font.Drawer{Face: fontFace}).MeasureString(textConfig.Text)

	// get the baseline start point based on the placement
	if textConfig.Placement == noPlacement {
		textConfig.Placement = choosePlacement(textConfig.Text)
	}
	baselineStartPoint := baselinePointForPlacement(textConfig.Placement)

	// add some variance to the starting baseline
	startPoint := image.Pt(
		baselineStartPoint.X+offsetX(textConfig.Text),
		baselineStartPoint.Y+offsetY(textConfig.Text),
	)

	borderRect := withPadding(
		// create a rectangle for the border
		image.Rect(
			// top left x is the same as the baseline
			startPoint.X,
			// top left y is the baseline y moved up by the ascent of the font (the distance between the baseline and the top of the font)
			startPoint.Y-fontFace.Metrics().Ascent.Round(),
			// bottom right x is the baseline start point x plus the calculated distance for drawing
			startPoint.X+drawDistance.Round(),
			// bottom right y is the same as the baseline
			startPoint.Y,
		),
		// pad that rectangle
		textBackgroundPadding,
	)

	// draw the background rectangle into the destination image in white
	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		image.White,
		image.ZP,
		borderRect,
		image.ZP,
		draw.Over,
	)

	// draw the text, in black to the return value
	drawer := &font.Drawer{
		Dst:  destinationImage,
		Src:  image.Black,
		Face: fontFace,
		Dot: fixed.P(
			startPoint.X,
			startPoint.Y,
		),
	}
	drawer.DrawString(textConfig.Text)

	return destinationImage
}

func writeImage(path string, image image.Image) error {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()

	return png.Encode(fd, image)
}

type panelConf struct {
	Text      string `json:"text"`
	Placement string `json:"placement"`
}

type comicBackgroundConf struct {
	Path      string `json:"path"`
	Placement string `json:"placement"`
}

type config struct {
	PanelConfigList  []panelConf         `json:"panels"`
	BackgroundConfig comicBackgroundConf `json:"background"`
}

func panelConfList2textConfList(panelConfigList []panelConf) []textConf {
	textConfigList := make([]textConf, 0, len(panelConfigList))
	for _, panelConfig := range panelConfigList {
		place := noPlacement

		switch panelConfig.Placement {
		case "top":
			place = topPlacement
		case "top-middle":
			place = topMiddlePlacement
		case "middle":
			place = middlePlacement
		case "bottom-middle":
			place = bottomMiddlePlacement
		case "bottom":
			place = bottomPlacement
		}

		textConfigList = append(
			textConfigList,
			textConf{
				Text:      panelConfig.Text,
				Placement: place,
			},
		)
	}
	return textConfigList
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println(r)
			os.Exit(1)
		}
	}()

	conf := config{}
	configFd, err := os.Open("config.json")
	if err != nil {
		panic(err)
	}
	err = json.NewDecoder(configFd).Decode(&conf)
	if err != nil {
		panic(err)
	}
	fmt.Println(conf)

	destinationImage := writeTextList(
		panelConfList2textConfList(conf.PanelConfigList),
		writeBackground(backgroundConf{Placement: topMiddlePlacement, Path: "background"}, generateBasicTemplate()),
	)

	err = writeImage("out.png", destinationImage)
	if err != nil {
		panic(err)
	}
}
<commit_msg>pull string2placement out into its own function<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	_ "image/jpeg"
	"image/png"
	"io/ioutil"
	"os"

	"github.com/golang/freetype/truetype"
	"github.com/nfnt/resize"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

func mustGetImage(path string) image.Image {
	image, err := getImage(path)
	if err != nil {
		panic(err)
	}
	return image
}

func getImage(path string) (image.Image, error) {
	imageFd, err := os.Open(path)
	if err != nil {
		return image.Black, err
	}
	defer imageFd.Close()

	img, _, err := image.Decode(imageFd)
	if err != nil {
		return image.Black, err
	}
	return img, nil
}

func generateBasicTemplate() draw.Image {
	templateImage := mustGetImage("template.png")
	destinationImage := image.NewNRGBA(templateImage.Bounds())

	// put base template into our destination
	draw.Draw(
		destinationImage,
		destinationImage.Bounds(),
		templateImage,
		image.ZP,
		draw.Src,
	)
	return destinationImage
}

type backgroundConf struct {
	Path      string
	Placement placement
}

func writeBackground(backgroundConfig backgroundConf, destinationImage draw.Image) draw.Image {
	templateMask := mustGetImage("template_mask.png")
	if backgroundConfig.Path == "" {
		backgroundConfig.Path = "background"
	}
	backgroundImage := mustGetImage(backgroundConfig.Path)

	// resize to the size of the template
	backgroundImage = resize.Resize(
		// scale to the width of the template
		comicWidth,
		0,
		backgroundImage,
		resize.Bilinear,
	)
	backgroundImageHeight := backgroundImage.Bounds().Dy()
	backgroundSegmentSize := backgroundImageHeight / 5
	backgroundStartingY := (int(backgroundConfig.Placement) - 1) * backgroundSegmentSize

	// if the placement makes the image not fully fit in the template, align the bottom edge with the bottom edge of the template
	if destinationImageHeight, pixelsInImage := destinationImage.Bounds().Dy(), backgroundImageHeight-backgroundStartingY; pixelsInImage < destinationImageHeight {
		backgroundStartingY = backgroundImageHeight - destinationImageHeight
	}

	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		backgroundImage,
		image.Pt(0, backgroundStartingY),
		templateMask,
		image.ZP,
		draw.Over,
	)

	return destinationImage
}

func getFont() *truetype.Font {
	fontFd, err := os.Open("Loveletter_TW.ttf")
	if err != nil {
		panic(err)
	}

	fontBytes, err := ioutil.ReadAll(fontFd)
	if err != nil {
		panic(err)
	}

	font, err := truetype.Parse(fontBytes)
	if err != nil {
		panic(err)
	}

	return font
}

const (
	comicWidth            = 720
	comicHeight           = 275
	fontSize              = 14.0
	textBackgroundPadding = 3
	numPlacements         = 5
	baselineX             = 30
)

func baselinePointForPlacement(place placement) image.Point {
	segmentSize := panelRectangle.Dy() / numPlacements

	// multiply the number of segments above (which corresponds to the number of the placement minus 1)
	// then add half a segment to put it in the middle of that (this helps put it not right next to edges)
	baselineY := (int(place)-1)*segmentSize + segmentSize/2
	return image.Pt(baselineX, baselineY)
}

func withPadding(rect image.Rectangle, padding int) image.Rectangle {
	return image.Rect(
		rect.Min.X-padding,
		rect.Min.Y-padding,
		rect.Max.X+padding,
		rect.Max.Y+padding,
	)
}

var panelToTopLeft = map[int]image.Point{
	0: image.Pt(13, 37),
	1: image.Pt(254, 37),
	2: image.Pt(493, 38),
}

var panelRectangle = image.Rect(
	0, 0,
	212, 216,
)

var panelToRectangle = func() map[int]image.Rectangle {
	m := make(map[int]image.Rectangle)
	for panelNumber, topLeft := range panelToTopLeft {
		m[panelNumber] = panelRectangle.Add(topLeft)
	}
	return m
}()

func copyImage(img image.Image) draw.Image {
	// create a new image
	copyTo := image.NewNRGBA(img.Bounds())

	// copy stuff to that image
	draw.Draw(
		copyTo,
		copyTo.Bounds(),
		img,
		image.ZP,
		draw.Src,
	)
	return copyTo
}

func writeTextList(textConfigList []textConf, destinationImage draw.Image) draw.Image {
	// copy for easier semantics
	destinationImage = copyImage(destinationImage)

	for i, textConfig := range textConfigList {
		// writing an empty string still does a background, so let's not do that
		if textConfig.Text == "" {
			continue
		}
		// create text image for panel
		textImage := writeSingleText(textConfig)
		// write text image on top of panel
		draw.DrawMask(
			destinationImage,
			panelToRectangle[i],
			textImage,
			image.ZP,
			image.Black,
			image.ZP,
			draw.Over,
		)
	}
	return destinationImage
}

// between -10 and 10 pixel offset
const offsetBound = 21

func hashString(text string, reduce func(left, right rune) rune) int {
	var accumulator rune
	for _, ch := range text {
		accumulator = reduce(accumulator, ch)
	}
	return int(accumulator)
}

func choosePlacement(text string) placement {
	hash := hashString(text, func(left, right rune) rune { return left | right })
	// mod by the number of placements and then add one to not get noPlacement
	return placement((hash % numPlacements) + 1)
}

func offset(text string, reduce func(left, right rune) rune) int {
	hash := hashString(text, reduce)
	return int(hash%offsetBound - (offsetBound / 2))
}

func offsetX(text string) int {
	return offset(text, func(left, right rune) rune { return left * right })
}

func offsetY(text string) int {
	return offset(text, func(left, right rune) rune { return left + right })
}

type placement int

const (
	noPlacement placement = iota
	topPlacement
	topMiddlePlacement
	middlePlacement
	bottomMiddlePlacement
	bottomPlacement
)

type textConf struct {
	Text      string    `json:"text"`
	Placement placement `json:"placement"`
}

func writeSingleText(textConfig textConf) draw.Image {
	// create a panel image to draw our text to
	destinationImage := image.NewNRGBA(panelRectangle)

	// create font face for our font
	fontFace := truetype.NewFace(
		getFont(),
		&truetype.Options{Size: fontSize},
	)

	// create a drawer to draw the text starting at the baseline point, in the font and measure the distance of the string
	drawDistance := (&font.Drawer{Face: fontFace}).MeasureString(textConfig.Text)

	// get the baseline start point based on the placement
	if textConfig.Placement == noPlacement {
		textConfig.Placement = choosePlacement(textConfig.Text)
	}
	baselineStartPoint := baselinePointForPlacement(textConfig.Placement)

	// add some variance to the starting baseline
	startPoint := image.Pt(
		baselineStartPoint.X+offsetX(textConfig.Text),
		baselineStartPoint.Y+offsetY(textConfig.Text),
	)

	borderRect := withPadding(
		// create a rectangle for the border
		image.Rect(
			// top left x is the same as the baseline
			startPoint.X,
			// top left y is the baseline y moved up by the ascent of the font (the distance between the baseline and the top of the font)
			startPoint.Y-fontFace.Metrics().Ascent.Round(),
			// bottom right x is the baseline start point x plus the calculated distance for drawing
			startPoint.X+drawDistance.Round(),
			// bottom right y is the same as the baseline
			startPoint.Y,
		),
		// pad that rectangle
		textBackgroundPadding,
	)

	// draw the background rectangle into the destination image in white
	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		image.White,
		image.ZP,
		borderRect,
		image.ZP,
		draw.Over,
	)

	// draw the text, in black to the return value
	drawer := &font.Drawer{
		Dst:  destinationImage,
		Src:  image.Black,
		Face: fontFace,
		Dot: fixed.P(
			startPoint.X,
			startPoint.Y,
		),
	}
	drawer.DrawString(textConfig.Text)

	return destinationImage
}

func writeImage(path string, image image.Image) error {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()

	return png.Encode(fd, image)
}

type panelConf struct {
	Text      string `json:"text"`
	Placement string `json:"placement"`
}

type comicBackgroundConf struct {
	Path      string `json:"path"`
	Placement string `json:"placement"`
}

type config struct {
	PanelConfigList  []panelConf         `json:"panels"`
	BackgroundConfig comicBackgroundConf `json:"background"`
}

func string2placement(str string) (place placement) {
	switch str {
	case "top":
		place = topPlacement
	case "top-middle":
		place = topMiddlePlacement
	case "middle":
		place = middlePlacement
	case "bottom-middle":
		place = bottomMiddlePlacement
	case "bottom":
		place = bottomPlacement
	}
	return
}

func panelConfList2textConfList(panelConfigList []panelConf) []textConf {
	textConfigList := make([]textConf, 0, len(panelConfigList))
	for _, panelConfig := range panelConfigList {
		place := string2placement(panelConfig.Placement)

		textConfigList = append(
			textConfigList,
			textConf{
				Text:      panelConfig.Text,
				Placement: place,
			},
		)
	}
	return textConfigList
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println(r)
			os.Exit(1)
		}
	}()

	conf := config{}
	configFd, err := os.Open("config.json")
	if err != nil {
		panic(err)
	}
	err = json.NewDecoder(configFd).Decode(&conf)
	if err != nil {
		panic(err)
	}
	fmt.Println(conf)

	destinationImage := writeTextList(
		panelConfList2textConfList(conf.PanelConfigList),
		writeBackground(backgroundConf{Placement: topMiddlePlacement, Path: "background"}, generateBasicTemplate()),
	)

	err = writeImage("out.png", destinationImage)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/mvdan/basedir"
)

const cmdName = "fdroidcl"

func subdir(dir, name string) string {
	p := filepath.Join(dir, name)
	if err := os.MkdirAll(p, 0755); err != nil {
		log.Fatalf("Could not create dir '%s': %v", p, err)
	}
	return p
}

func mustCache() string {
	dir := basedir.Cache()
	if dir == "" {
		log.Fatalf("Could not determine cache dir")
	}
	return subdir(dir, cmdName)
}

func mustData() string {
	dir := basedir.Data()
	if dir == "" {
		log.Fatalf("Could not determine data dir")
	}
	return subdir(dir, cmdName)
}

func configPath() string {
	return filepath.Join(mustData(), "config.json")
}

type repo struct {
	ID      string `json:"id"`
	URL     string `json:"url"`
	Enabled bool   `json:"enabled"`
}

type userConfig struct {
	Repos []repo `json:"repos"`
}

var config = userConfig{
	Repos: []repo{
		{
			ID:      "f-droid",
			URL:     "https://f-droid.org/repo",
			Enabled: true,
		},
		{
			ID:      "f-droid-archive",
			URL:     "https://f-droid.org/archive",
			Enabled: false,
		},
	},
}

func readConfig() error {
	f, err := os.Open(configPath())
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("could not open config file: %v", err)
	}
	defer f.Close()
	if err := json.NewDecoder(f).Decode(&config); err != nil {
		return fmt.Errorf("could not decode config file: %v", err)
	}
	return nil
}

// A Command is an implementation of a go command
// like go build or go fix.
type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(args []string)

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description.
	Short string

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s %s [-h]\n", cmdName, c.UsageLine)
	anyFlags := false
	c.Flag.VisitAll(func(f *flag.Flag) { anyFlags = true })
	if anyFlags {
		fmt.Fprintf(os.Stderr, "\nAvailable options:\n")
		c.Flag.PrintDefaults()
	}
	os.Exit(2)
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-h] <command> [<args>]\n\n", cmdName)
		fmt.Fprintf(os.Stderr, "Available commands:\n")
		maxUsageLen := 0
		for _, c := range commands {
			if len(c.UsageLine) > maxUsageLen {
				maxUsageLen = len(c.UsageLine)
			}
		}
		for _, c := range commands {
			fmt.Fprintf(os.Stderr, "   %s%s  %s\n", c.UsageLine,
				strings.Repeat(" ", maxUsageLen-len(c.UsageLine)), c.Short)
		}
		fmt.Fprintf(os.Stderr, "\nA specific version of an app can be selected by following the appid with an colon (:) and the version code of the app to select.\n")
		fmt.Fprintf(os.Stderr, "\nUse %s <command> -h for more info\n", cmdName)
	}
}

// Commands lists the available commands.
var commands = []*Command{
	cmdUpdate,
	cmdSearch,
	cmdShow,
	cmdDevices,
	cmdDownload,
	cmdInstall,
	cmdUpgrade,
	cmdUninstall,
	cmdDefaults,
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(2)
	}

	for _, cmd := range commands {
		if cmd.Name() != args[0] {
			continue
		}
		if err := readConfig(); err != nil {
			log.Printf("Could not load config: %v", err)
			log.Printf("Using default config")
		}
		cmd.Flag.Usage = func() { cmd.Usage() }
		cmd.Flag.Parse(args[1:])
		args = cmd.Flag.Args()
		cmd.Run(args)
		os.Exit(0)
	}

	switch args[0] {
	default:
		log.Printf("Unrecognised command '%s'\n\n", args[0])
		flag.Usage()
		os.Exit(2)
	}
}
<commit_msg>fdroidcl: return instead of using os.Exit(0)<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/mvdan/basedir"
)

const cmdName = "fdroidcl"

func subdir(dir, name string) string {
	p := filepath.Join(dir, name)
	if err := os.MkdirAll(p, 0755); err != nil {
		log.Fatalf("Could not create dir '%s': %v", p, err)
	}
	return p
}

func mustCache() string {
	dir := basedir.Cache()
	if dir == "" {
		log.Fatalf("Could not determine cache dir")
	}
	return subdir(dir, cmdName)
}

func mustData() string {
	dir := basedir.Data()
	if dir == "" {
		log.Fatalf("Could not determine data dir")
	}
	return subdir(dir, cmdName)
}

func configPath() string {
	return filepath.Join(mustData(), "config.json")
}

type repo struct {
	ID      string `json:"id"`
	URL     string `json:"url"`
	Enabled bool   `json:"enabled"`
}

type userConfig struct {
	Repos []repo `json:"repos"`
}

var config = userConfig{
	Repos: []repo{
		{
			ID:      "f-droid",
			URL:     "https://f-droid.org/repo",
			Enabled: true,
		},
		{
			ID:      "f-droid-archive",
			URL:     "https://f-droid.org/archive",
			Enabled: false,
		},
	},
}

func readConfig() error {
	f, err := os.Open(configPath())
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("could not open config file: %v", err)
	}
	defer f.Close()
	if err := json.NewDecoder(f).Decode(&config); err != nil {
		return fmt.Errorf("could not decode config file: %v", err)
	}
	return nil
}

// A Command is an implementation of a go command
// like go build or go fix.
type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(args []string)

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description.
	Short string

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s %s [-h]\n", cmdName, c.UsageLine)
	anyFlags := false
	c.Flag.VisitAll(func(f *flag.Flag) { anyFlags = true })
	if anyFlags {
		fmt.Fprintf(os.Stderr, "\nAvailable options:\n")
		c.Flag.PrintDefaults()
	}
	os.Exit(2)
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-h] <command> [<args>]\n\n", cmdName)
		fmt.Fprintf(os.Stderr, "Available commands:\n")
		maxUsageLen := 0
		for _, c := range commands {
			if len(c.UsageLine) > maxUsageLen {
				maxUsageLen = len(c.UsageLine)
			}
		}
		for _, c := range commands {
			fmt.Fprintf(os.Stderr, "   %s%s  %s\n", c.UsageLine,
				strings.Repeat(" ", maxUsageLen-len(c.UsageLine)), c.Short)
		}
		fmt.Fprintf(os.Stderr, "\nA specific version of an app can be selected by following the appid with an colon (:) and the version code of the app to select.\n")
		fmt.Fprintf(os.Stderr, "\nUse %s <command> -h for more info\n", cmdName)
	}
}

// Commands lists the available commands.
var commands = []*Command{
	cmdUpdate,
	cmdSearch,
	cmdShow,
	cmdDevices,
	cmdDownload,
	cmdInstall,
	cmdUpgrade,
	cmdUninstall,
	cmdDefaults,
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(2)
	}

	for _, cmd := range commands {
		if cmd.Name() != args[0] {
			continue
		}
		if err := readConfig(); err != nil {
			log.Printf("Could not load config: %v", err)
			log.Printf("Using default config")
		}
		cmd.Flag.Usage = func() { cmd.Usage() }
		cmd.Flag.Parse(args[1:])
		args = cmd.Flag.Args()
		cmd.Run(args)
		return
	}

	switch args[0] {
	default:
		log.Printf("Unrecognised command '%s'\n\n", args[0])
		flag.Usage()
		os.Exit(2)
	}
}
<|endoftext|>