<commit_before>package redis

import (
	"errors"
	"github.com/blacklightops/libbeat/common"
	"github.com/blacklightops/libbeat/logp"
	"github.com/blacklightops/turnbeat/inputs"
	"github.com/garyburd/redigo/redis"
	"net"
	"fmt"
	"time"
)

type RedisInput struct {
	Config inputs.MothershipConfig
	Host	string	/* the host to connect to */
	Port	int		/* the port to connect to */
	DB		int		/* the database to read from */
	Key		string	/* the key to POP from */
	Type	string	/* the type to add to events */
}

func (l *RedisInput) InputType() string {
	return "RedisInput"
}

func (l *RedisInput) InputVersion() string {
	return "0.0.1"
}

func (l *RedisInput) Init(config inputs.MothershipConfig) error {

	l.Config = config

	if config.Host == "" {
		return errors.New("No Input Host specified")
	}
	l.Host = config.Host

	if config.Port == 0 {
		return errors.New("No Input Port specified")
	}
	l.Port = config.Port

	l.DB = config.DB

	if config.Key == "" {
		return errors.New("No Input Key specified")
	}
	l.Key = config.Key

	if config.Type == "" {
		return errors.New("No Event Type specified")
	}
	l.Type = config.Type

	logp.Debug("redisinput", "Using Host %s", l.Host)
	logp.Debug("redisinput", "Using Port %d", l.Port)
	logp.Debug("redisinput", "Using Database %d", l.DB)
	logp.Debug("redisinput", "Using Key %s", l.Key)
	logp.Debug("redisinput", "Adding Event Type %s", l.Type)

	return nil
}

func (l *RedisInput) GetConfig() inputs.MothershipConfig {
	return l.Config
}

func (l *RedisInput) Run(output chan common.MapStr) error {
	logp.Info("[RedisInput] Running Redis Input")
	redisHostname := fmt.Sprintf("%s:%d", l.Host, l.Port)
	server, err := redis.Dial("tcp", redisHostname)
	if err != nil {
		logp.Err("couldn't start listening: " + err.Error())
		return nil
	}
	logp.Info("[RedisInput] Connected to Redis Server")

	// dispatch the master listen thread
	go func(server redis.Conn) {
		var args []interface{}
		for {
			exists, err := redis.Bool(server.Do("EXISTS", append(args, l.Key)))
			if err != nil {
				logp.Err("An error occured while executing EXISTS command")
				return
			}
			if exists != true {
				logp.Err("Key %s does not exist!", l.Key)
				return
			}
			handleConn(server, output)
		}
	}(server)
	return nil
}

func (l *RedisInput) handleConn(server redis.Conn, output chan common.MapStr) {
	var offset int64 = 0
	var line uint64 = 0
	var bytesread uint65 = 0
	var args = []interface{}

	logp.Debug("redisinput", "Reading events from %s", l.Key)

	now := func() time.Time {
		t := time.Now()
		return t
	}

	for {
		args = []interface{}
		reply, err := server.Do("LPOP", append(args, l.Key))
		text, err := redis.String(reply, err)
		bytesread += len(text)

		if err != nil {
			logp.Info("Unexpected state reading from %s; error: %s\n", l.Key, err)
			return
		}

		logp.Debug("redisinputlines", "New Line: %s", &text)

		line++

		event := common.MapStr{}
		event["source"] = l.Key
		event["offset"] = offset
		event["line"] = line
		event["message"] = text
		event["type"] = l.Type

		event.EnsureTimestampField(now)
		event.EnsureCountField()

		offset += int64(bytesread)

		logp.Debug("redisinput", "InputEvent: %v", event)
		output <- event // ship the new event downstream
		client.Write([]byte("OK"))
	}
	logp.Debug("redisinput", "Finished reading from %s", l.Key)
}
<commit_msg>Fixing errors<commit_after>package redis

import (
	"errors"
	"github.com/blacklightops/libbeat/common"
	"github.com/blacklightops/libbeat/logp"
	"github.com/blacklightops/turnbeat/inputs"
	"github.com/garyburd/redigo/redis"
	"fmt"
	"time"
)

type RedisInput struct {
	Config inputs.MothershipConfig
	Host	string	/* the host to connect to */
	Port	int		/* the port to connect to */
	DB		int		/* the database to read from */
	Key		string	/* the key to POP from */
	Type	string	/* the type to add to events */
}

func (l *RedisInput) InputType() string {
	return "RedisInput"
}

func (l *RedisInput) InputVersion() string {
	return "0.0.1"
}

func (l *RedisInput) Init(config inputs.MothershipConfig) error {

	l.Config = config

	if config.Host == "" {
		return errors.New("No Input Host specified")
	}
	l.Host = config.Host

	if config.Port == 0 {
		return errors.New("No Input Port specified")
	}
	l.Port = config.Port

	l.DB = config.DB

	if config.Key == "" {
		return errors.New("No Input Key specified")
	}
	l.Key = config.Key

	if config.Type == "" {
		return errors.New("No Event Type specified")
	}
	l.Type = config.Type

	logp.Debug("redisinput", "Using Host %s", l.Host)
	logp.Debug("redisinput", "Using Port %d", l.Port)
	logp.Debug("redisinput", "Using Database %d", l.DB)
	logp.Debug("redisinput", "Using Key %s", l.Key)
	logp.Debug("redisinput", "Adding Event Type %s", l.Type)

	return nil
}

func (l *RedisInput) GetConfig() inputs.MothershipConfig {
	return l.Config
}

func (l *RedisInput) Run(output chan common.MapStr) error {
	logp.Info("[RedisInput] Running Redis Input")
	redisHostname := fmt.Sprintf("%s:%d", l.Host, l.Port)
	server, err := redis.Dial("tcp", redisHostname)
	if err != nil {
		logp.Err("couldn't start listening: " + err.Error())
		return nil
	}
	logp.Info("[RedisInput] Connected to Redis Server")

	// dispatch the master listen thread
	go func(server redis.Conn) {
		for {
			exists, err := redis.Bool(server.Do("EXISTS", redis.Args{}.Add(l.Key)))
			if err != nil {
				logp.Err("An error occured while executing EXISTS command")
				return
			}
			if exists != true {
				logp.Err("Key %s does not exist!", l.Key)
				return
			}
			l.handleConn(server, output)
		}
	}(server)
	return nil
}

func (l *RedisInput) handleConn(server redis.Conn, output chan common.MapStr) {
	var offset int64 = 0
	var line uint64 = 0
	var bytesread uint64 = 0

	logp.Debug("redisinput", "Reading events from %s", l.Key)

	now := func() time.Time {
		t := time.Now()
		return t
	}

	for {
		reply, err := server.Do("LPOP", redis.Args{}.Add(l.Key))
		text, err := redis.String(reply, err)
		bytesread += uint64(len(text))

		if err != nil {
			logp.Info("Unexpected state reading from %s; error: %s\n", l.Key, err)
			return
		}

		logp.Debug("redisinputlines", "New Line: %s", &text)

		line++

		event := common.MapStr{}
		event["source"] = l.Key
		event["offset"] = offset
		event["line"] = line
		event["message"] = text
		event["type"] = l.Type

		event.EnsureTimestampField(now)
		event.EnsureCountField()

		offset += int64(bytesread)

		logp.Debug("redisinput", "InputEvent: %v", event)
		output <- event // ship the new event downstream
	}
	logp.Debug("redisinput", "Finished reading from %s", l.Key)
}
<|endoftext|>


<commit_before>package slack

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
)

// SecretsVerifier contains the information needed to verify that the request comes from Slack
type SecretsVerifier struct {
	slackSig    string
	timeStamp   string
	requestBody string
}

// NewSecretsVerifierFromHeader returns a new SecretsVerifier object in exchange for an http.Header object
func NewSecretsVerifierFromHeader(header http.Header) (SecretsVerifier, error) {
	if header["X-Slack-Signature"][0] == "" || header["X-Slack-Request-Timestamp"][0] == "" {
		return SecretsVerifier{}, errors.New("headers are empty, cannot create SecretsVerifier")
	}

	return SecretsVerifier{
		slackSig:  header["X-Slack-Signature"][0],
		timeStamp: header["X-Slack-Request-Timestamp"][0],
	}, nil
}

func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
	v.requestBody = string(body)
	return len(body), nil
}

// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
func (v SecretsVerifier) Ensure(signingSecret string) error {
	message := fmt.Sprintf("v0:%v:%v", v.timeStamp, v.requestBody)

	mac := hmac.New(sha256.New, []byte(signingSecret))
	mac.Write([]byte(message))

	actualSignature := "v0=" + string(hex.EncodeToString(mac.Sum(nil)))
	fmt.Printf("actual: %s expected: %s", actualSignature, v.slackSig)
	if actualSignature == v.slackSig {
		return nil
	}

	return errors.New("invalid request")
}
<commit_msg>reformat hashing process<commit_after>package slack

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"hash"
	"net/http"
)

// SecretsVerifier contains the information needed to verify that the request comes from Slack
type SecretsVerifier struct {
	slackSig  string
	timeStamp string
	hmac      hash.Hash
}

// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret
func NewSecretsVerifier(header http.Header, signingSecret string) (SecretsVerifier, error) {
	if header["X-Slack-Signature"][0] == "" || header["X-Slack-Request-Timestamp"][0] == "" {
		return SecretsVerifier{}, errors.New("headers are empty, cannot create SecretsVerifier")
	}

	hash := hmac.New(sha256.New, []byte(signingSecret))
	hash.Write([]byte(fmt.Sprintf("v0:%s:", header["X-Slack-Request-Timestamp"][0])))
	return SecretsVerifier{
		slackSig:  header["X-Slack-Signature"][0],
		timeStamp: header["X-Slack-Request-Timestamp"][0],
		hmac:      hash,
	}, nil
}

func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
	return v.hmac.Write(body)
}

// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
func (v SecretsVerifier) Ensure(signingSecret string) error {
	computed := "v0=" + string(hex.EncodeToString(v.hmac.Sum(nil)))
	if computed == v.slackSig {
		return nil
	}

	return fmt.Errorf("invalid request verification token %s, expected %s", v.slackSig, computed)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/dkerwin/gini-api-go"
	"github.com/fatih/color"
	"io/ioutil"
	"net/url"
	"os"
)

// getApiClient create a Gini API client from cli context
func getApiClient(c *cli.Context) *giniapi.APIClient {
	credentials := getClientCredentials(c)
	apiEndpoint := c.GlobalString("api")
	userEndpoint := c.GlobalString("usercenter")

	apiConfig := giniapi.Config{
		ClientID:       credentials[0],
		ClientSecret:   credentials[1],
		Authentication: giniapi.UseBasicAuth,
		Endpoints: giniapi.Endpoints{
			API: apiEndpoint,
			UserCenter: userEndpoint,
		},
	}

	if c.GlobalBool("debug") {
		apiConfig.HTTPDebug = true
		apiConfig.RequestDebug = request
		apiConfig.ResponseDebug = response
	}

	api, err := giniapi.NewClient(&apiConfig)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		cli.ShowCommandHelp(c, c.Command.FullName())
		os.Exit(1)
	}

	return api
}

func uploadDocument(c *cli.Context) {
	filename := c.String("filename")
	doctype := c.String("doctype")
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	if _, err := os.Stat(c.Args().First()); os.IsNotExist(err) {
		color.Red("\nError: cannot find %s\n\n", c.Args().First())
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	bodyBuf, err := os.Open(c.Args().First())
	if err != nil {
		color.Red("\nError: failed to read %s\n\n", c.Args().First())
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)

	doc, err := api.Upload(bodyBuf, giniapi.UploadOptions{
		FileName:       filename,
		DocType:        doctype,
		UserIdentifier: userid,
	})

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   fmt.Sprintf("--data-binary '@%s'", c.Args().First()),
			URL:    fmt.Sprintf("%s/documents", api.Endpoints.API),
			Method: "GET",
		}

		curl.render(c)
	}
}

func getDocument(c *cli.Context) {
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Document,
			Method: "GET",
		}

		curl.render(c)
	}
}

func getProcessed(c *cli.Context) {
	userid := getUserIdentifier(c)

	if len(c.Args()) != 2 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	body, err := doc.GetProcessed()

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	err = ioutil.WriteFile(c.Args()[1], body, 0644)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json,application/octet-stream",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Processed,
			Method: "GET",
		}

		curl.render(c)
	}
}

func deleteDocument(c *cli.Context) {
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	err = doc.Delete()

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults("empty response")

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Document,
			Method: "DELETE",
		}

		curl.render(c)
	}
}

func listDocuments(c *cli.Context) {
	limit := c.Int("limit")
	offset := c.Int("offset")
	userid := c.GlobalString("user-id")

	api := getApiClient(c)

	doc, err := api.List(giniapi.ListOptions{
		Limit:          limit,
		Offset:         offset,
		UserIdentifier: userid,
	})

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    fmt.Sprintf("%s/documents?limit=%d&offset=%d", api.Endpoints.API, limit, offset),
			Method: "GET",
		}

		curl.render(c)
	}
}

func getExtractions(c *cli.Context) {
	incubator := c.Bool("incubator")
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	ext, err := doc.GetExtractions(incubator)
	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(ext)

	if c.GlobalBool("curl") {
		accept := "application/vnd.gini.v1+json"
		if incubator {
			accept = "application/vnd.gini.incubator+json"
		}

		curl := curlData{
			Headers: map[string]string{
				"Accept":            accept,
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Extractions,
			Method: "GET",
		}

		curl.render(c)
	}
}

func reportError(c *cli.Context) {
	summary := c.String("summary")
	description := c.String("description")
	userid := c.GlobalString("user-id")

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	err = doc.ErrorReport(summary, description)
	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults("")

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   fmt.Sprintf("-d \"summary=%s&description=%s\"", url.QueryEscape(summary), url.QueryEscape(description)),
			URL:    fmt.Sprintf("%s/errorreport", doc.Links.Document),
			Method: "POST",
		}

		curl.render(c)
	}
}
<commit_msg>Fix upload method in curl rendering<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/dkerwin/gini-api-go"
	"github.com/fatih/color"
	"io/ioutil"
	"net/url"
	"os"
)

// getApiClient create a Gini API client from cli context
func getApiClient(c *cli.Context) *giniapi.APIClient {
	credentials := getClientCredentials(c)
	apiEndpoint := c.GlobalString("api")
	userEndpoint := c.GlobalString("usercenter")

	apiConfig := giniapi.Config{
		ClientID:       credentials[0],
		ClientSecret:   credentials[1],
		Authentication: giniapi.UseBasicAuth,
		Endpoints: giniapi.Endpoints{
			API: apiEndpoint,
			UserCenter: userEndpoint,
		},
	}

	if c.GlobalBool("debug") {
		apiConfig.HTTPDebug = true
		apiConfig.RequestDebug = request
		apiConfig.ResponseDebug = response
	}

	api, err := giniapi.NewClient(&apiConfig)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		cli.ShowCommandHelp(c, c.Command.FullName())
		os.Exit(1)
	}

	return api
}

func uploadDocument(c *cli.Context) {
	filename := c.String("filename")
	doctype := c.String("doctype")
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	if _, err := os.Stat(c.Args().First()); os.IsNotExist(err) {
		color.Red("\nError: cannot find %s\n\n", c.Args().First())
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	bodyBuf, err := os.Open(c.Args().First())
	if err != nil {
		color.Red("\nError: failed to read %s\n\n", c.Args().First())
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)

	doc, err := api.Upload(bodyBuf, giniapi.UploadOptions{
		FileName:       filename,
		DocType:        doctype,
		UserIdentifier: userid,
	})

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   fmt.Sprintf("--data-binary '@%s'", c.Args().First()),
			URL:    fmt.Sprintf("%s/documents", api.Endpoints.API),
			Method: "POST",
		}

		curl.render(c)
	}
}

func getDocument(c *cli.Context) {
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Document,
			Method: "GET",
		}

		curl.render(c)
	}
}

func getProcessed(c *cli.Context) {
	userid := getUserIdentifier(c)

	if len(c.Args()) != 2 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	body, err := doc.GetProcessed()

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	err = ioutil.WriteFile(c.Args()[1], body, 0644)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json,application/octet-stream",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Processed,
			Method: "GET",
		}

		curl.render(c)
	}
}

func deleteDocument(c *cli.Context) {
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	err = doc.Delete()

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults("empty response")

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Document,
			Method: "DELETE",
		}

		curl.render(c)
	}
}

func listDocuments(c *cli.Context) {
	limit := c.Int("limit")
	offset := c.Int("offset")
	userid := c.GlobalString("user-id")

	api := getApiClient(c)

	doc, err := api.List(giniapi.ListOptions{
		Limit:          limit,
		Offset:         offset,
		UserIdentifier: userid,
	})

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(doc)

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    fmt.Sprintf("%s/documents?limit=%d&offset=%d", api.Endpoints.API, limit, offset),
			Method: "GET",
		}

		curl.render(c)
	}
}

func getExtractions(c *cli.Context) {
	incubator := c.Bool("incubator")
	userid := getUserIdentifier(c)

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	ext, err := doc.GetExtractions(incubator)
	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults(ext)

	if c.GlobalBool("curl") {
		accept := "application/vnd.gini.v1+json"
		if incubator {
			accept = "application/vnd.gini.incubator+json"
		}

		curl := curlData{
			Headers: map[string]string{
				"Accept":            accept,
				"X-User-Identifier": userid,
			},
			Body:   "",
			URL:    doc.Links.Extractions,
			Method: "GET",
		}

		curl.render(c)
	}
}

func reportError(c *cli.Context) {
	summary := c.String("summary")
	description := c.String("description")
	userid := c.GlobalString("user-id")

	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, c.Command.FullName())
		return
	}

	api := getApiClient(c)
	u := fmt.Sprintf("%s/documents/%s", api.Endpoints.API, c.Args().First())

	doc, err := api.Get(u, userid)

	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	err = doc.ErrorReport(summary, description)
	if err != nil {
		color.Red("\nError: %s\n\n", err)
		return
	}

	done <- true
	wg.Wait()

	renderResults("")

	if c.GlobalBool("curl") {
		curl := curlData{
			Headers: map[string]string{
				"Accept":            "application/vnd.gini.v1+json",
				"X-User-Identifier": userid,
			},
			Body:   fmt.Sprintf("-d \"summary=%s&description=%s\"", url.QueryEscape(summary), url.QueryEscape(description)),
			URL:    fmt.Sprintf("%s/errorreport", doc.Links.Document),
			Method: "POST",
		}

		curl.render(c)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/nordligulv/go-flake"
)

var (
	max     = flag.Int("max", 1, "number of IDs to create")
	hex     = flag.Bool("hex", false, "Show hex representation")
	integer = flag.Bool("integer", false, "Show integer representation")
)

func main() {
	flag.Parse()
	f, err := flake.New(1)
	if err != nil {
		log.Fatal(err)
	}

	if !*hex && !*integer {
		*hex = true
	}

	for i := 0; i < *max; i++ {
		id := f.NextID()

		if *integer {
			fmt.Println(id)
		}

		if *hex {
			fmt.Println(id.String())
		}
	}
}
<commit_msg>update example<commit_after>package main

import (
	"flag"
	"fmt"

	"github.com/nordligulv/go-flake"
)

var (
	max     = flag.Int("max", 1, "number of IDs to create")
	hex     = flag.Bool("hex", false, "Show hex representation")
	integer = flag.Bool("integer", false, "Show integer representation")
)

func main() {
	flag.Parse()
	f := flake.New(1)

	if !*hex && !*integer {
		*hex = true
	}

	for i := 0; i < *max; i++ {
		id := f.NextID()

		if *integer {
			fmt.Println(id)
		}

		if *hex {
			fmt.Println(id.String())
		}
	}
}
<|endoftext|>


<commit_before>package build

const client = `
"use strict";

exports.createClient = function(args) {
    var events = require('events');
    var channel = new events.EventEmitter();
	var http = require('http');
    var server = http.createServer(function (request, response) {
	  if (request.method!='POST') {
	    response.writeHead(404);
        response.end('');
		return;
	  }

	  var body = '';
	  request.on('data', function(chunk) { body += chunk.toString(); });
	  request.on('end', function() {
		switch (request.url) {
		case '/redirect':
		  channel.emit('redirect', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		case '/error':
		  channel.emit('error', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		default:
		  response.writeHead(404);
		  response.end('');
		 };
	  });
    });

    server.listen(0, '127.0.0.1', 1, function() {
        var nodeWebkitAddr = 'http://127.0.0.1:'+server.address().port;
        console.log('Listening for golang-nw on '+nodeWebkitAddr);
        startClient(channel, nodeWebkitAddr, args);
    });

	return channel;
};

function logMessage(data, logger) {
    var lines = data.toString().split('\n');
    for (var i = 0; i < lines.length; i++) {
        if (lines[i]) {
            logger(lines[i]);
        }
    }
}

function startClient(channel, nodeWebkitAddr, args) {
    var path = require('path');
    var exe = '.'+path.sep+'{{ .Bin }}';
    console.log('Using client: ' + exe);

    // Now start the client process
    var childProcess = require('child_process');

	var env = process.env;
	env['{{ .EnvVar }}'] = nodeWebkitAddr;
    var p = childProcess.spawn(exe, args, {env: env});

    p.stdout.on('data', function(data) {
        logMessage(data, console.log);
    });

    p.stderr.on('data', function(data) {
        logMessage(data, console.error);
    });

    p.on('error', function(err) {
        console.error('child error: ' + err);
        channel.emit('error', err);
    });

    p.on('close', function(code) {
        console.log('child process closed with code ' + code);
        channel.emit('close', code);
    });

    p.on('exit', function(code) {
        console.log('child process exited with code ' + code);
        channel.emit('exit', code);
    });

    channel.kill = function() {
        p.kill();
    }
};
`
<commit_msg>Fix console invocation error<commit_after>package build

const client = `
"use strict";

exports.createClient = function(args) {
    var events = require('events');
    var channel = new events.EventEmitter();
	var http = require('http');
    var server = http.createServer(function (request, response) {
	  if (request.method!='POST') {
	    response.writeHead(404);
        response.end('');
		return;
	  }

	  var body = '';
	  request.on('data', function(chunk) { body += chunk.toString(); });
	  request.on('end', function() {
		switch (request.url) {
		case '/redirect':
		  channel.emit('redirect', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		case '/error':
		  channel.emit('error', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		default:
		  response.writeHead(404);
		  response.end('');
		 };
	  });
    });

    server.listen(0, '127.0.0.1', 1, function() {
        var nodeWebkitAddr = 'http://127.0.0.1:'+server.address().port;
        console.log('Listening for golang-nw on '+nodeWebkitAddr);
        startClient(channel, nodeWebkitAddr, args);
    });

	return channel;
};

function logMessage(data, logger) {
    var lines = data.toString().split('\n');
    for (var i = 0; i < lines.length; i++) {
        if (lines[i]) {
            logger.call(console, lines[i]);
        }
    }
}

function startClient(channel, nodeWebkitAddr, args) {
    var path = require('path');
    var exe = '.'+path.sep+'{{ .Bin }}';
    console.log('Using client: ' + exe);

    // Now start the client process
    var childProcess = require('child_process');

	var env = process.env;
	env['{{ .EnvVar }}'] = nodeWebkitAddr;
    var p = childProcess.spawn(exe, args, {env: env});

    p.stdout.on('data', function(data) {
        logMessage(data, console.log);
    });

    p.stderr.on('data', function(data) {
        logMessage(data, console.error);
    });

    p.on('error', function(err) {
        console.error('child error: ' + err);
        channel.emit('error', err);
    });

    p.on('close', function(code) {
        console.log('child process closed with code ' + code);
        channel.emit('close', code);
    });

    p.on('exit', function(code) {
        console.log('child process exited with code ' + code);
        channel.emit('exit', code);
    });

    channel.kill = function() {
        p.kill();
    }
};
`
<|endoftext|>


<commit_before>// Package sendgrid provides a simple interface to interact with the SendGrid API
// Special thanks to this gist -> https://gist.github.com/rmulley/6603544
package sendgrid

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

// SGClient will contain the credentials and default values
type SGClient struct {
	apiUser string
	apiPwd  string
	apiMail string
	Client  *http.Client
}

// NewSendGridClient will return a new SGClient.
func NewSendGridClient(apiUser, apiPwd string) SGClient {
	apiMail := "https://api.sendgrid.com/api/mail.send.json?"
	return SGClient{
		apiUser: apiUser,
		apiPwd:  apiPwd,
		apiMail: apiMail,
	}
}

func (sg *SGClient) buildUrl(m SGMail) (url.Values, error) {
	values := url.Values{}
	values.Set("api_user", sg.apiUser)
	values.Set("api_key", sg.apiPwd)
	values.Set("subject", m.Subject)
	values.Set("html", m.HTML)
	values.Set("text", m.Text)
	values.Set("from", m.From)
	values.Set("replyto", m.ReplyTo)
	apiHeaders, apiError := m.JsonString()
	if apiError != nil {
		return nil, fmt.Errorf("sendgrid.go: error:%v", apiError)
	}
	values.Set("x-smtpapi", apiHeaders)
	values.Set("headers", m.Headers)
	if len(m.FromName) != 0 {
		values.Set("fromname", m.FromName)
	}
	for i := 0; i < len(m.Mail.To); i++ {
		values.Add("to[]", m.Mail.To[i])
	}
	for i := 0; i < len(m.Bcc); i++ {
		values.Add("bcc[]", m.Bcc[i])
	}
	for i := 0; i < len(m.ToName); i++ {
		values.Add("toname[]", m.ToName[i])
	}
	for k, v := range m.Files {
		values.Set("files["+k+"]", v)
	}
	return values, nil
}

// SendAPI will send mail using SG web API
func (sg *SGClient) Send(m SGMail) error {
	if sg.Client == nil {
		sg.Client = http.DefaultClient
	}
	var e error
	values, e := sg.buildUrl(m)
	if e != nil {
		return e
	}
	r, e := sg.Client.PostForm(sg.apiMail, values)
	if e == nil { // errors can contain nil Body responses
		defer r.Body.Close()
	}
	if r.StatusCode == http.StatusOK && e == nil {
		return nil
	} else {
		body, _ := ioutil.ReadAll(r.Body)
		return fmt.Errorf("sendgrid.go: code:%d error:%v body:%s", r.StatusCode, e, body)
	}
}
<commit_msg>removed old msg<commit_after>// Package sendgrid provides a simple interface to interact with the SendGrid API
package sendgrid

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

// SGClient will contain the credentials and default values
type SGClient struct {
	apiUser string
	apiPwd  string
	apiMail string
	Client  *http.Client
}

// NewSendGridClient will return a new SGClient.
func NewSendGridClient(apiUser, apiPwd string) SGClient {
	apiMail := "https://api.sendgrid.com/api/mail.send.json?"
	return SGClient{
		apiUser: apiUser,
		apiPwd:  apiPwd,
		apiMail: apiMail,
	}
}

func (sg *SGClient) buildUrl(m SGMail) (url.Values, error) {
	values := url.Values{}
	values.Set("api_user", sg.apiUser)
	values.Set("api_key", sg.apiPwd)
	values.Set("subject", m.Subject)
	values.Set("html", m.HTML)
	values.Set("text", m.Text)
	values.Set("from", m.From)
	values.Set("replyto", m.ReplyTo)
	apiHeaders, apiError := m.JsonString()
	if apiError != nil {
		return nil, fmt.Errorf("sendgrid.go: error:%v", apiError)
	}
	values.Set("x-smtpapi", apiHeaders)
	values.Set("headers", m.Headers)
	if len(m.FromName) != 0 {
		values.Set("fromname", m.FromName)
	}
	for i := 0; i < len(m.Mail.To); i++ {
		values.Add("to[]", m.Mail.To[i])
	}
	for i := 0; i < len(m.Bcc); i++ {
		values.Add("bcc[]", m.Bcc[i])
	}
	for i := 0; i < len(m.ToName); i++ {
		values.Add("toname[]", m.ToName[i])
	}
	for k, v := range m.Files {
		values.Set("files["+k+"]", v)
	}
	return values, nil
}

// SendAPI will send mail using SG web API
func (sg *SGClient) Send(m SGMail) error {
	if sg.Client == nil {
		sg.Client = http.DefaultClient
	}
	var e error
	values, e := sg.buildUrl(m)
	if e != nil {
		return e
	}
	r, e := sg.Client.PostForm(sg.apiMail, values)
	if e == nil { // errors can contain nil Body responses
		defer r.Body.Close()
	}
	if r.StatusCode == http.StatusOK && e == nil {
		return nil
	} else {
		body, _ := ioutil.ReadAll(r.Body)
		return fmt.Errorf("sendgrid.go: code:%d error:%v body:%s", r.StatusCode, e, body)
	}
}
<|endoftext|>


<commit_before>// Package sendgrid provides a simple interface to interact with the SendGrid API
// Special thanks to this gist -> https://gist.github.com/rmulley/6603544
package sendgrid

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

// SGClient will contain the credentials and default values
type SGClient struct {
	apiUser string
	apiPwd  string
	apiMail string
	Client  *http.Client
}

// NewSendGridClient will return a new SGClient.
func NewSendGridClient(apiUser, apiPwd string) SGClient {
	apiMail := "https://api.sendgrid.com/api/mail.send.json?"
	return SGClient{
		apiUser: apiUser,
		apiPwd:  apiPwd,
		apiMail: apiMail,
	}
}

// SendAPI will send mail using SG web API
func (sg *SGClient) Send(m SGMail) error {
	values := url.Values{}
	values.Set("api_user", sg.apiUser)
	values.Set("api_key", sg.apiPwd)
	values.Set("subject", m.Subject)
	values.Set("html", m.HTML)
	values.Set("text", m.Text)
	values.Set("from", m.From)
	apiHeaders, apiError := m.GetHeaders()
	if apiError != nil {
		return fmt.Errorf("sendgrid.go: error:%v", apiError)
	}
	values.Set("x-smtpapi", apiHeaders)
	values.Set("headers", m.Headers)
	for i := 0; i < len(m.To); i++ {
		values.Add("to[]", m.To[i])
	}
	for i := 0; i < len(m.Bcc); i++ {
		values.Add("bcc[]", m.Bcc[i])
	}
	for i := 0; i < len(m.ToName); i++ {
		values.Add("toname[]", m.ToName[i])
	}
	for k, v := range m.Files {
		values.Set("files["+k+"]", v)
	}
	if sg.Client == nil {
		sg.Client = http.DefaultClient
	}
	r, e := sg.Client.PostForm(sg.apiMail, values)
	defer r.Body.Close()
	if r.StatusCode == 200 && e == nil {
		return nil
	} else {
		body, _ := ioutil.ReadAll(r.Body)
		return fmt.Errorf("sendgrid.go: code:%d error:%v body:%s", r.StatusCode, e, body)
	}
}
<commit_msg>add fromname<commit_after>// Package sendgrid provides a simple interface to interact with the SendGrid API
// Special thanks to this gist -> https://gist.github.com/rmulley/6603544
package sendgrid

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

// SGClient will contain the credentials and default values
type SGClient struct {
	apiUser string
	apiPwd  string
	apiMail string
	Client  *http.Client
}

// NewSendGridClient will return a new SGClient.
func NewSendGridClient(apiUser, apiPwd string) SGClient {
	apiMail := "https://api.sendgrid.com/api/mail.send.json?"
	return SGClient{
		apiUser: apiUser,
		apiPwd:  apiPwd,
		apiMail: apiMail,
	}
}

// SendAPI will send mail using SG web API
func (sg *SGClient) Send(m SGMail) error {
	values := url.Values{}
	values.Set("api_user", sg.apiUser)
	values.Set("api_key", sg.apiPwd)
	values.Set("subject", m.Subject)
	values.Set("html", m.HTML)
	values.Set("text", m.Text)
	values.Set("from", m.From)
	apiHeaders, apiError := m.GetHeaders()
	if apiError != nil {
		return fmt.Errorf("sendgrid.go: error:%v", apiError)
	}
	values.Set("x-smtpapi", apiHeaders)
	values.Set("headers", m.Headers)
	if len(m.FromName) != 0 {
		values.Set("fromname", m.FromName)
	}
	for i := 0; i < len(m.To); i++ {
		values.Add("to[]", m.To[i])
	}
	for i := 0; i < len(m.Bcc); i++ {
		values.Add("bcc[]", m.Bcc[i])
	}
	for i := 0; i < len(m.ToName); i++ {
		values.Add("toname[]", m.ToName[i])
	}
	for k, v := range m.Files {
		values.Set("files["+k+"]", v)
	}
	if sg.Client == nil {
		sg.Client = http.DefaultClient
	}
	r, e := sg.Client.PostForm(sg.apiMail, values)
	defer r.Body.Close()
	if r.StatusCode == 200 && e == nil {
		return nil
	} else {
		body, _ := ioutil.ReadAll(r.Body)
		return fmt.Errorf("sendgrid.go: code:%d error:%v body:%s", r.StatusCode, e, body)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	iniconf "code.google.com/p/goconf/conf"
	"crypto/sha512"
	"encoding/json"
	"errors"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{QuitChan: sigChan}
	glcd.init(configFile)

	// receiving quit shuts down
	<-glcd.QuitChan
}

type GLCClient struct {
	Name          string
	ClientId      string
	Authenticated bool
	State         *PlayerState
	Heartbeat     time.Time
}

// struct type for Bot3
type GLCD struct {
	Online     bool
	ConfigFile *iniconf.ConfigFile

	// NSQ input/output
	NSQWriter             *nsq.Writer
	GLCDaemonTopic        *nsq.Reader
	GLCGameStateTopicName string
	GLCDaemonTopicChannel string
	Clients               map[string]*GLCClient

	// game state channels
	HeartbeatChan   chan *Heartbeat
	KnockChan       chan *GLCClient
	AuthChan        chan *PlayerAuthInfo
	PlayerStateChan chan *PlayerState

	QuitChan chan os.Signal

	MongoSession *mgo.Session
	MongoDB      *mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	glcd.setupMongoDBConnection()

	// set up channels
	glcd.setupTopicChannels()

	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	glcd.GLCGameStateTopicName, _ = conf.GetString("nsq", "server-topic")

	glcdTopic, _ := conf.GetString("nsq", "glcd-topic")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	glcd.NSQWriter = nsq.NewWriter(nsqdAddress)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, []byte("{\"client\":\"server\"}"))

	// set up reader for glcdTopic
	reader, err := nsq.NewReader(glcdTopic, "main")
	if err != nil {
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCDaemonTopic = reader
	glcd.GLCDaemonTopic.AddHandler(glcd)
	glcd.GLCDaemonTopic.ConnectToLookupd(lookupdAddress)

	// goroutines to handle concurrent events
	go glcd.CleanupClients()
	go glcd.HandlePlayerAuthChannel()
	go glcd.HandleHeartbeatChannel()
	go glcd.HandleKnockChannel()
	go glcd.HandlePlayerStateChannel()

	return nil
}

func (glcd *GLCD) setupTopicChannels() {
	// set up channels
	glcd.HeartbeatChan = make(chan *Heartbeat)
	glcd.KnockChan = make(chan *GLCClient)
	glcd.AuthChan = make(chan *PlayerAuthInfo)
	glcd.PlayerStateChan = make(chan *PlayerState)
}

func (glcd *GLCD) setupMongoDBConnection() error {

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")
	if err != nil {
		return err
	}

	glcd.MongoSession, err = mgo.Dial(servers)
	if err != nil {
		return err
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")
	if err != nil {
		return err
	} else {
		fmt.Println("Successfully obtained config from mongo")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)
	return nil
}

func (glcd *GLCD) Publish(msg *Message) {
	encodedRequest, _ := json.Marshal(*msg)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, encodedRequest)
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		//fmt.Println("Doing client clean up")
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				fmt.Printf("Deleting client %s due to inactivity.\n", v.ClientId)
				delete(glcd.Clients, k)
				//glcd.Publish(&Message{Type: "playerPassport", Data: PlayerPassport{Action: "playerGone"}}) // somehow add k to this
			} else {
				//fmt.Printf("Client has not expired.")
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones() {
	fmt.Println("SendZones --")
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No zones found")})
	} else {
		fmt.Println("Publishing zones to clients")
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				fmt.Printf("Res: is %+v", res)
				glcd.Publish(&Message{Type: "updateZone", Data: res.(bson.M)}) // dump res as a JSON string
			}
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zones: %v", err)})
		}
	}
}

func (glcd *GLCD) SendZone(zone *Zone) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone": zone.Name}
	results := c.Find(query)

	if results == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No such zone '%s'", zone.Name)})
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			glcd.Publish(&Message{Type: "zone", Data: res.(string)})
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zone: %v", err)})
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(zone *Zone) {
	query := bson.M{"zone": zone.Name}
	zdata := ZoneInfo{}
	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to update zone: %v", err)})
	} else {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Updated zone '%s'", zone.Name)})
	}
}

func (glcd *GLCD) HandleMessage(nsqMessage *nsq.Message) error {

	// fmt.Println("-------")
	// fmt.Printf("Received message %s\n\n", nsqMessage.Body)
	// fmt.Println("-------")
	msg := &Message{}

	err := json.Unmarshal(nsqMessage.Body, &msg)

	if err != nil {
		fmt.Printf(err.Error())
	}

	var dataMap map[string]interface{}
	var ok bool

	if msg.Data != nil {
		dataMap, ok = msg.Data.(map[string]interface{})
	} else {
		dataMap = make(map[string]interface{})
		ok = true
	}

	if !ok {
		return nil
	}

	// UNIMPLEMENTED TYPES: playerPassport, sendZones, error
	// add new/future handler functions in glcd-handlers.go
	if msg.Type == "playerState" {
		glcd.HandlePlayerState(msg, dataMap)
	} else if msg.Type == "connected" {
		glcd.HandleConnected(msg, dataMap)
	} else if msg.Type == "chat" {
		glcd.HandleChat(msg, msg.Data)
	} else if msg.Type == "heartbeat" {
		glcd.HandleHeartbeat(msg, dataMap)
	} else if msg.Type == "knock" {
		glcd.HandleKnock(msg, dataMap)
	} else if msg.Type == "playerAuth" {
		glcd.HandlePlayerAuth(msg, dataMap)
	} else {
		fmt.Printf("Unable to determine handler for message: %+v\n", msg)
	}

	return nil
}

func (glcd *GLCD) isPasswordCorrect(name string, password string) (bool, error) {
	c := glcd.MongoDB.C("users")
	authInfo := PlayerAuthInfo{}
	query := bson.M{"user": name}
	err := c.Find(query).One(&authInfo)

	if err != nil {
		return false, err
	}

	return password == authInfo.Password, nil
}

func generateSaltedPasswordHash(password string, salt []byte) ([]byte, error) {
	hash := sha512.New()
	//hash.Write(server_salt)
	hash.Write(salt)
	hash.Write([]byte(password))
	return hash.Sum(salt), nil
}

func (glcd *GLCD) getUserPasswordHash(name string) ([]byte, error) {
	return nil, nil
}

func (glcd *GLCD) isPasswordCorrectWithHash(name string, password string, salt []byte) (bool, error) {
	expectedHash, err := glcd.getUserPasswordHash(name)

	if err != nil {
		return false, err
	}

	if len(expectedHash) != 32+sha512.Size {
		return false, errors.New("Wrong size")
	}

	actualHash := sha512.New()
	actualHash.Write(salt)
	actualHash.Write([]byte(password))

	return bytes.Equal(actualHash.Sum(nil), expectedHash[32:]), nil
}
<commit_msg>glcd: Don't process the message if it's not JSON<commit_after>package main

import (
	"bytes"
	iniconf "code.google.com/p/goconf/conf"
	"crypto/sha512"
	"encoding/json"
	"errors"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{QuitChan: sigChan}
	glcd.init(configFile)

	// receiving quit shuts down
	<-glcd.QuitChan
}

type GLCClient struct {
	Name          string
	ClientId      string
	Authenticated bool
	State         *PlayerState
	Heartbeat     time.Time
}

// struct type for Bot3
type GLCD struct {
	Online     bool
	ConfigFile *iniconf.ConfigFile

	// NSQ input/output
	NSQWriter             *nsq.Writer
	GLCDaemonTopic        *nsq.Reader
	GLCGameStateTopicName string
	GLCDaemonTopicChannel string
	Clients               map[string]*GLCClient

	// game state channels
	HeartbeatChan   chan *Heartbeat
	KnockChan       chan *GLCClient
	AuthChan        chan *PlayerAuthInfo
	PlayerStateChan chan *PlayerState

	QuitChan chan os.Signal

	MongoSession *mgo.Session
	MongoDB      *mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	glcd.setupMongoDBConnection()

	// set up channels
	glcd.setupTopicChannels()

	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	glcd.GLCGameStateTopicName, _ = conf.GetString("nsq", "server-topic")

	glcdTopic, _ := conf.GetString("nsq", "glcd-topic")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	glcd.NSQWriter = nsq.NewWriter(nsqdAddress)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, []byte("{\"client\":\"server\"}"))

	// set up reader for glcdTopic
	reader, err := nsq.NewReader(glcdTopic, "main")
	if err != nil {
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCDaemonTopic = reader
	glcd.GLCDaemonTopic.AddHandler(glcd)
	glcd.GLCDaemonTopic.ConnectToLookupd(lookupdAddress)

	// goroutines to handle concurrent events
	go glcd.CleanupClients()
	go glcd.HandlePlayerAuthChannel()
	go glcd.HandleHeartbeatChannel()
	go glcd.HandleKnockChannel()
	go glcd.HandlePlayerStateChannel()

	return nil
}

func (glcd *GLCD) setupTopicChannels() {
	// set up channels
	glcd.HeartbeatChan = make(chan *Heartbeat)
	glcd.KnockChan = make(chan *GLCClient)
	glcd.AuthChan = make(chan *PlayerAuthInfo)
	glcd.PlayerStateChan = make(chan *PlayerState)
}

func (glcd *GLCD) setupMongoDBConnection() error {

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")
	if err != nil {
		return err
	}

	glcd.MongoSession, err = mgo.Dial(servers)
	if err != nil {
		return err
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")
	if err != nil {
		return err
	} else {
		fmt.Println("Successfully obtained config from mongo")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)
	return nil
}

func (glcd *GLCD) Publish(msg *Message) {
	encodedRequest, _ := json.Marshal(*msg)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, encodedRequest)
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		//fmt.Println("Doing client clean up")
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				fmt.Printf("Deleting client %s due to inactivity.\n", v.ClientId)
				delete(glcd.Clients, k)
				//glcd.Publish(&Message{Type: "playerPassport", Data: PlayerPassport{Action: "playerGone"}}) // somehow add k to this
			} else {
				//fmt.Printf("Client has not expired.")
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones() {
	fmt.Println("SendZones --")
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No zones found")})
	} else {
		fmt.Println("Publishing zones to clients")
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				fmt.Printf("Res: is %+v", res)
				glcd.Publish(&Message{Type: "updateZone", Data: res.(bson.M)}) // dump res as a JSON string
			}
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zones: %v", err)})
		}
	}
}

func (glcd *GLCD) SendZone(zone *Zone) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone": zone.Name}
	results := c.Find(query)

	if results == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No such zone '%s'", zone.Name)})
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			glcd.Publish(&Message{Type: "zone", Data: res.(string)})
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zone: %v", err)})
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(zone *Zone) {
	query := bson.M{"zone": zone.Name}
	zdata := ZoneInfo{}
	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to update zone: %v", err)})
	} else {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Updated zone '%s'", zone.Name)})
	}
}

func (glcd *GLCD) HandleMessage(nsqMessage *nsq.Message) error {

	// fmt.Println("-------")
	// fmt.Printf("Received message %s\n\n", nsqMessage.Body)
	// fmt.Println("-------")
	msg := &Message{}

	err := json.Unmarshal(nsqMessage.Body, &msg)

	if err != nil {
		fmt.Printf(err.Error())
		return err
	}

	var dataMap map[string]interface{}
	var ok bool

	if msg.Data != nil {
		dataMap, ok = msg.Data.(map[string]interface{})
	} else {
		dataMap = make(map[string]interface{})
		ok = true
	}

	if !ok {
		return nil
	}

	// UNIMPLEMENTED TYPES: playerPassport, sendZones, error
	// add new/future handler functions in glcd-handlers.go
	if msg.Type == "playerState" {
		glcd.HandlePlayerState(msg, dataMap)
	} else if msg.Type == "connected" {
		glcd.HandleConnected(msg, dataMap)
	} else if msg.Type == "chat" {
		glcd.HandleChat(msg, msg.Data)
	} else if msg.Type == "heartbeat" {
		glcd.HandleHeartbeat(msg, dataMap)
	} else if msg.Type == "knock" {
		glcd.HandleKnock(msg, dataMap)
	} else if msg.Type == "playerAuth" {
		glcd.HandlePlayerAuth(msg, dataMap)
	} else {
		fmt.Printf("Unable to determine handler for message: %+v\n", msg)
	}

	return nil
}

func (glcd *GLCD) isPasswordCorrect(name string, password string) (bool, error) {
	c := glcd.MongoDB.C("users")
	authInfo := PlayerAuthInfo{}
	query := bson.M{"user": name}
	err := c.Find(query).One(&authInfo)

	if err != nil {
		return false, err
	}

	return password == authInfo.Password, nil
}

func generateSaltedPasswordHash(password string, salt []byte) ([]byte, error) {
	hash := sha512.New()
	//hash.Write(server_salt)
	hash.Write(salt)
	hash.Write([]byte(password))
	return hash.Sum(salt), nil
}

func (glcd *GLCD) getUserPasswordHash(name string) ([]byte, error) {
	return nil, nil
}

func (glcd *GLCD) isPasswordCorrectWithHash(name string, password string, salt []byte) (bool, error) {
	expectedHash, err := glcd.getUserPasswordHash(name)

	if err != nil {
		return false, err
	}

	if len(expectedHash) != 32+sha512.Size {
		return false, errors.New("Wrong size")
	}

	actualHash := sha512.New()
	actualHash.Write(salt)
	actualHash.Write([]byte(password))

	return bytes.Equal(actualHash.Sum(nil), expectedHash[32:]), nil
}
<|endoftext|>


<commit_before>package main

import (
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	ms "github.com/mitchellh/mapstructure"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

var gamestateTopic = ""

type Message struct {
	Name       string
	PlayerName string
	ClientId   string
	Type       string // better way to persist type info?
	Command    string
	Data       interface{}
}

type ZoneInfo struct {
	x int
	y int
}

type Zone struct {
	Id    int
	Name  string
	State *ZoneInfo
}

type PlayerInfo struct {
	Name     string
	ClientId string
}

type Players []PlayerInfo

type PlayerState struct {
	ClientId string
	X        float64
	Y        float64
	AvatarId string `json:",omitempty"`
}

type Heartbeat struct {
	ClientId  string
	Timestamp time.Time
}

/* Players coming in and out */
type PlayerPassport struct {
	Action string
	Avatar string
}

type ErrorMessage string

type WallMessage string

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{QuitChan: sigChan}
	glcd.init(configFile)

	// receiving quit shuts down
	<-glcd.QuitChan
}

type GLCClient struct {
	Name      string
	ClientId  string
	State     *PlayerState
	Heartbeat time.Time
}

// struct type for Bot3
type GLCD struct {
	Online     bool
	ConfigFile *iniconf.ConfigFile

	// NSQ input/output
	NSQWriter             *nsq.Writer
	GLCDaemonTopic        *nsq.Reader
	GLCGameStateTopicName string
	GLCDaemonTopicChannel string
	Clients               map[string]*GLCClient

	// game state channels
	HeartbeatChan   chan *Heartbeat
	KnockChan       chan *GLCClient
	PlayerStateChan chan *PlayerState

	QuitChan chan os.Signal

	MongoSession *mgo.Session
	MongoDB      *mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")

	if err != nil {
		return fmt.Errorf("Mongo: No server configured.")
	}

	glcd.MongoSession, err = mgo.Dial(servers)

	if err != nil {
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")

	if err != nil {
		return fmt.Errorf("Mongo: No database configured.")
	} else {
		fmt.Println("Successfully obtained config from mongo")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)

	// set up channels
	glcd.HeartbeatChan = make(chan *Heartbeat)
	glcd.KnockChan = make(chan *GLCClient)
	glcd.PlayerStateChan = make(chan *PlayerState)

	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	glcd.GLCGameStateTopicName, _ = conf.GetString("nsq", "server-topic")

	glcdTopic, _ := conf.GetString("nsq", "glcd-topic")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	glcd.NSQWriter = nsq.NewWriter(nsqdAddress)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, []byte("{\"client\":\"server\"}"))

	// set up reader for glcdTopic
	reader, err := nsq.NewReader(glcdTopic, "main")
	if err != nil {
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCDaemonTopic = reader
	glcd.GLCDaemonTopic.AddHandler(glcd)
	glcd.GLCDaemonTopic.ConnectToLookupd(lookupdAddress)

	// goroutines to handle concurrent events
	go glcd.CleanupClients()
	go glcd.HandleHeartbeatChannel()
	go glcd.HandleKnockChannel()
	go glcd.HandlePlayerStateChannel()

	return nil
}

func (glcd *GLCD) Publish(msg *Message) {
	encodedRequest, _ := json.Marshal(*msg)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, encodedRequest)
}

func (glcd *GLCD) HandlePlayerStateChannel() {
	for {
		ps := <-glcd.PlayerStateChan
		glcd.Publish(&Message{Type: "playerState", Data: ps})
	}
}

func (glcd *GLCD) HandleHeartbeatChannel() {
	for {
		hb := <-glcd.HeartbeatChan
		//fmt.Printf("HandleHeartbeatChannel: Received heartbeat: %+v\n", hb)

		// see if key and client exists in the map
		c, exists := glcd.Clients[hb.ClientId]

		if exists {
			//fmt.Printf("Client %s exists.  Updating heartbeat.\n", hb.ClientId)
			c.Heartbeat = time.Now()
		} else {
			//fmt.Printf("Adding client %s to client list\n", hb.ClientId)
			client := &GLCClient{ClientId: hb.ClientId, Heartbeat: time.Now()}
			glcd.Clients[hb.ClientId] = client
		}
	}
}

func (glcd *GLCD) HandleKnockChannel() error {
	for {
		client := <-glcd.KnockChan
		fmt.Printf("Received knock from %s @ %s", client.Name, client.ClientId)
		players := make(Players, len(glcd.Clients))

		i := 0
		for n, c := range glcd.Clients {
			players[i] = PlayerInfo{Name: n, ClientId: c.ClientId}
			i++
		}

		glcd.Publish(&Message{Name: client.Name, ClientId: client.ClientId, Type: "knock", Data: players})
	}
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		//fmt.Println("Doing client clean up")
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				fmt.Printf("Deleting client %s due to inactivity.\n", v.ClientId)
				delete(glcd.Clients, k)
				//glcd.Publish(&Message{Type: "playerPassport", Data: PlayerPassport{Action: "playerGone"}}) // somehow add k to this
			} else {
				//fmt.Printf("Client has not expired.")
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones() {
	fmt.Println("SendZones --")
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No zones found")})
	} else {
		fmt.Println("Publishing zones to clients")
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				fmt.Printf("Res: is %+v", res)
				glcd.Publish(&Message{Type: "updateZone", Data: res.(bson.M)}) // dump res as a JSON string
			}
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zones: %v", err)})
		}
	}
}

func (glcd *GLCD) SendZone(zone *Zone) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone": zone.Name}
	results := c.Find(query)

	if results == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No such zone '%s'", zone.Name)})
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			glcd.Publish(&Message{Type: "zone", Data: res.(string)})
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zone: %v", err)})
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(zone *Zone) {
	query := bson.M{"zone": zone.Name}
	zdata := ZoneInfo{}
	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to update zone: %v", err)})
	} else {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Updated zone '%s'", zone.Name)})
	}
}

func (glcd *GLCD) HandleMessage(nsqMessage *nsq.Message) error {

	// fmt.Println("-------")
	// fmt.Printf("Received message %s\n\n", nsqMessage.Body)
	// fmt.Println("-------")
	msg := &Message{}

	err := json.Unmarshal(nsqMessage.Body, &msg)

	if err != nil {
		fmt.Printf(err.Error())
	}

	var dataMap map[string]interface{}
	var ok bool

	if msg.Data != nil {
		dataMap, ok = msg.Data.(map[string]interface{})
	} else {
		dataMap = make(map[string]interface{})
		ok = true
	}

	if !ok {
		return nil
	}

	if msg.Command == "playerPassport" {
		//		HandlePassport(msg.Data)
	} else if msg.Command == "playerState" {
		var ps PlayerState
		err := ms.Decode(dataMap, &ps)
		if err != nil {
			fmt.Println(err.Error())
		}
		ps.ClientId = msg.Name
		fmt.Printf("Player state: %+v\n", ps)
		glcd.PlayerStateChan <- &ps
	} else if msg.Command == "connected" {
		fmt.Println("Received connected from client")
		glcd.SendZones()
	} else if msg.Command == "sendZones" {
		fmt.Println("Received sendZones from client")
		//		HandleZoneUpdate(msg.Data)
	} else if msg.Command == "wall" {
		//		HandleWallMessage(msg.Data)
	} else if msg.Command == "heartbeat" {
		hb := &Heartbeat{}
		hb.ClientId = msg.Name
		glcd.HeartbeatChan <- hb
	} else if msg.Command == "knock" {
		glcd.KnockChan <- glcd.Clients[msg.Name]
	} else if msg.Command == "error" {
		//		HandleError(msg.Data)
	} else {
		// log.Printf("Unknown Message Type: %s", msg.Type)
	}

	return nil
}
<commit_msg>Standardizing Message between glcd and client<commit_after>package main

import (
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	ms "github.com/mitchellh/mapstructure"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

var gamestateTopic = ""

type Message struct {
	ClientId   string
	Type       string // better way to persist type info?
	Data       interface{}
}

type ZoneInfo struct {
	x int
	y int
}

type Zone struct {
	Id    int
	Name  string
	State *ZoneInfo
}

type PlayerInfo struct {
	Name     string
	ClientId string
}

type Players []PlayerInfo

type PlayerState struct {
	ClientId string
	X        float64
	Y        float64
	AvatarId string `json:",omitempty"`
}

type Heartbeat struct {
	ClientId  string
	Timestamp time.Time
}

/* Players coming in and out */
type PlayerPassport struct {
	Action string
	Avatar string
}

type ErrorMessage string

type ChatMessage struct {
	Sender string
	Message string
}

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{QuitChan: sigChan}
	glcd.init(configFile)

	// receiving quit shuts down
	<-glcd.QuitChan
}

type GLCClient struct {
	ClientId  string
	State     *PlayerState
	Heartbeat time.Time
}

// struct type for Bot3
type GLCD struct {
	Online     bool
	ConfigFile *iniconf.ConfigFile

	// NSQ input/output
	NSQWriter             *nsq.Writer
	GLCDaemonTopic        *nsq.Reader
	GLCGameStateTopicName string
	GLCDaemonTopicChannel string
	Clients               map[string]*GLCClient

	// game state channels
	HeartbeatChan   chan *Heartbeat
	KnockChan       chan *GLCClient
	PlayerStateChan chan *PlayerState

	QuitChan chan os.Signal

	MongoSession *mgo.Session
	MongoDB      *mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")

	if err != nil {
		return fmt.Errorf("Mongo: No server configured.")
	}

	glcd.MongoSession, err = mgo.Dial(servers)

	if err != nil {
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")

	if err != nil {
		return fmt.Errorf("Mongo: No database configured.")
	} else {
		fmt.Println("Successfully obtained config from mongo")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)

	// set up channels
	glcd.HeartbeatChan = make(chan *Heartbeat)
	glcd.KnockChan = make(chan *GLCClient)
	glcd.PlayerStateChan = make(chan *PlayerState)

	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	glcd.GLCGameStateTopicName, _ = conf.GetString("nsq", "server-topic")

	glcdTopic, _ := conf.GetString("nsq", "glcd-topic")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	glcd.NSQWriter = nsq.NewWriter(nsqdAddress)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, []byte("{\"client\":\"server\"}"))

	// set up reader for glcdTopic
	reader, err := nsq.NewReader(glcdTopic, "main")
	if err != nil {
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCDaemonTopic = reader
	glcd.GLCDaemonTopic.AddHandler(glcd)
	glcd.GLCDaemonTopic.ConnectToLookupd(lookupdAddress)

	// goroutines to handle concurrent events
	go glcd.CleanupClients()
	go glcd.HandleHeartbeatChannel()
	go glcd.HandleKnockChannel()
	go glcd.HandlePlayerStateChannel()

	return nil
}

func (glcd *GLCD) Publish(msg *Message) {
	encodedRequest, _ := json.Marshal(*msg)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, encodedRequest)
}

func (glcd *GLCD) HandlePlayerStateChannel() {
	for {
		ps := <-glcd.PlayerStateChan
		glcd.Publish(&Message{Type: "playerState", Data: ps})
	}
}

func (glcd *GLCD) HandleHeartbeatChannel() {
	for {
		hb := <-glcd.HeartbeatChan
		//fmt.Printf("HandleHeartbeatChannel: Received heartbeat: %+v\n", hb)

		// see if key and client exists in the map
		c, exists := glcd.Clients[hb.ClientId]

		if exists {
			//fmt.Printf("Client %s exists.  Updating heartbeat.\n", hb.ClientId)
			c.Heartbeat = time.Now()
		} else {
			//fmt.Printf("Adding client %s to client list\n", hb.ClientId)
			client := &GLCClient{ClientId: hb.ClientId, Heartbeat: time.Now()}
			glcd.Clients[hb.ClientId] = client
		}
	}
}

func (glcd *GLCD) HandleKnockChannel() error {
	for {
		client := <-glcd.KnockChan
		fmt.Printf("Received knock from %s", client.ClientId)
		players := make(Players, len(glcd.Clients))

		i := 0
		for _, c := range glcd.Clients {
			players[i] = PlayerInfo{ClientId: c.ClientId}
			i++
		}

		glcd.Publish(&Message{ClientId: client.ClientId, Type: "knock", Data: players})
	}
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		//fmt.Println("Doing client clean up")
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				fmt.Printf("Deleting client %s due to inactivity.\n", v.ClientId)
				delete(glcd.Clients, k)
				//glcd.Publish(&Message{Type: "playerPassport", Data: PlayerPassport{Action: "playerGone"}}) // somehow add k to this
			} else {
				//fmt.Printf("Client has not expired.")
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones() {
	fmt.Println("SendZones --")
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No zones found")})
	} else {
		fmt.Println("Publishing zones to clients")
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				fmt.Printf("Res: is %+v", res)
				glcd.Publish(&Message{Type: "updateZone", Data: res.(bson.M)}) // dump res as a JSON string
			}
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zones: %v", err)})
		}
	}
}

func (glcd *GLCD) HandleChatMessage(msg *Message, data interface{}) {
	glcd.Publish(msg)
}

func (glcd *GLCD) SendZone(zone *Zone) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone": zone.Name}
	results := c.Find(query)

	if results == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No such zone '%s'", zone.Name)})
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			glcd.Publish(&Message{Type: "zone", Data: res.(string)})
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zone: %v", err)})
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(zone *Zone) {
	query := bson.M{"zone": zone.Name}
	zdata := ZoneInfo{}
	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to update zone: %v", err)})
	} else {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Updated zone '%s'", zone.Name)})
	}
}

func (glcd *GLCD) HandleMessage(nsqMessage *nsq.Message) error {

	// fmt.Println("-------")
	// fmt.Printf("Received message %s\n\n", nsqMessage.Body)
	// fmt.Println("-------")
	msg := &Message{}

	err := json.Unmarshal(nsqMessage.Body, &msg)

	if err != nil {
		fmt.Printf(err.Error())
	}

	var dataMap map[string]interface{}
	var ok bool

	if msg.Data != nil {
		dataMap, ok = msg.Data.(map[string]interface{})
	} else {
		dataMap = make(map[string]interface{})
		ok = true
	}

	if !ok {
		return nil
	}

	if msg.Type == "playerPassport" {
		//		HandlePassport(msg.Data)
	} else if msg.Type == "playerState" {
		var ps PlayerState
		err := ms.Decode(dataMap, &ps)
		if err != nil {
			fmt.Println(err.Error())
		} else {
			ps.ClientId = msg.ClientId
			log.Printf("Player state: %+v\n", ps)
			glcd.PlayerStateChan <- &ps
		}
	} else if msg.Type == "connected" {
		fmt.Println("Received connected from client")
		glcd.SendZones()
	} else if msg.Type == "sendZones" {
		fmt.Println("Received sendZones from client")
		//		HandleZoneUpdate(msg.Data)
	} else if msg.Type == "chat" {
		glcd.HandleChatMessage(msg, msg.Data)
	} else if msg.Type == "heartbeat" {
		hb := &Heartbeat{}
		hb.ClientId = msg.ClientId
		glcd.HeartbeatChan <- hb
	} else if msg.Type == "knock" {
		glcd.KnockChan <- glcd.Clients[msg.ClientId]
	} else if msg.Type == "error" {
		//		HandleError(msg.Data)
	} else {
		// log.Printf("Unknown Message Type: %s", msg.Type)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/dmulholl/argo"
	"github.com/dmulholl/ironclad/irondb"
)

var listHelp = fmt.Sprintf(`
Usage: %s list [entries]

  Prints a list of entries from a database, showing only the entry title.

  Entries to list can be specified by ID or by title. (Titles are checked for
  a case-insensitive substring match.)

  If no arguments are specified, all the entries in the database will be
  listed.

Arguments:
  [entries]                 Entries to list by ID or title.

Options:
  -f, --file <str>          Database file. Defaults to the last used file.
  -t, --tag <str>           Filter entries using the specified tag.

Flags:
  -h, --help                Print this command's help text and exit.
  -i, --inactive            List inactive entries.
`, filepath.Base(os.Args[0]))

func registerListCmd(parser *argo.ArgParser) {
	cmdParser := parser.NewCommand("list show")
	cmdParser.Helptext = listHelp
	cmdParser.Callback = listCallback
	cmdParser.NewStringOption("file f", "")
	cmdParser.NewStringOption("tag t", "")
	cmdParser.NewFlag("inactive i")
}

func listCallback(cmdName string, cmdParser *argo.ArgParser) {
	filename, _, db := loadDB(cmdParser)

	// Default to displaying all active entries.
	var list irondb.EntryList
	var totalCount int
	if cmdParser.Found("inactive") {
		list = db.Inactive()
		totalCount = len(list)
	} else {
		list = db.Active()
		totalCount = len(list)
	}

	// Do we have query strings to filter on?
	if cmdParser.HasArgs() {
		list = list.FilterByAny(cmdParser.Args()...)
	}

	// Are we filtering by tag?
	if cmdParser.StringValue("tag") != "" {
		list = list.FilterByTag(cmdParser.StringValue("tag"))
	}

	// Print the list of entries.
	printCompact(list, totalCount, filepath.Base(filename))
}
<commit_msg>Housekeeping<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/dmulholl/argo"
	"github.com/dmulholl/ironclad/irondb"
)

var listHelp = fmt.Sprintf(`
Usage: %s list [entries]

  Prints a list of entries from a database, showing only the entry title.

  Entries to list can be specified by ID or by title. (Titles are checked for
  a case-insensitive substring match.)

  If no arguments are specified, all the entries in the database will be
  listed.

Arguments:
  [entries]                 Entries to list by ID or title.

Options:
  -f, --file <str>          Database file. Defaults to the last used file.
  -t, --tag <str>           Filter entries using the specified tag.

Flags:
  -h, --help                Print this command's help text and exit.
  -i, --inactive            List inactive entries.
`, filepath.Base(os.Args[0]))

func registerListCmd(parser *argo.ArgParser) {
	cmdParser := parser.NewCommand("list")
	cmdParser.Helptext = listHelp
	cmdParser.Callback = listCallback
	cmdParser.NewStringOption("file f", "")
	cmdParser.NewStringOption("tag t", "")
	cmdParser.NewFlag("inactive i")
}

func listCallback(cmdName string, cmdParser *argo.ArgParser) {
	filename, _, db := loadDB(cmdParser)

	// Default to displaying all active entries.
	var list irondb.EntryList
	var totalCount int
	if cmdParser.Found("inactive") {
		list = db.Inactive()
		totalCount = len(list)
	} else {
		list = db.Active()
		totalCount = len(list)
	}

	// Do we have query strings to filter on?
	if cmdParser.HasArgs() {
		list = list.FilterByAny(cmdParser.Args()...)
	}

	// Are we filtering by tag?
	if cmdParser.StringValue("tag") != "" {
		list = list.FilterByTag(cmdParser.StringValue("tag"))
	}

	// Print the list of entries.
	printCompact(list, totalCount, filepath.Base(filename))
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Package glob provides equivalent functionality to filepath.Glob while
// meeting different performance requirements.
package glob

import (
	"context"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// Glob is similar to filepath.Glob but with different performance concerns.
//
// Firstly, It can be canceled via the context. Secondly, it makes no guarantees
// about the order of returned matches. This change allows it to run in O(d+m)
// memory and O(n) time, where m is the number of match results, d is the depth
// of the directory tree the pattern is concerned with, and n is the number of
// files in that tree.
func Glob(ctx context.Context, pattern string) ([]string, error) {
	gr := Stream(pattern)
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-ctx.Done()
		gr.Close()
	}()
	defer cancel()

	var ret []string
	for {
		match, err := gr.Next()
		if err != nil {
			return nil, err
		}
		if match == "" {
			break
		}
		ret = append(ret, match)
	}
	return ret, nil
}

// Result is a stream of results from globbing against a pattern.
type Result struct {
	errors  chan error
	results chan string
	cancel  context.CancelFunc
}

// Stream Returns a Result from which glob matches can be streamed.
//
// Stream supports the same pattern syntax and produces the same matches as Go's
// filepath.Glob, but makes no ordering guarantees.
func Stream(pattern string) Result {
	ctx, cancel := context.WithCancel(context.Background())
	g := Result{
		errors:  make(chan error),
		results: make(chan string),
		cancel:  cancel,
	}
	go func() {
		defer close(g.results)
		defer close(g.errors)
		if err := stream(pattern, g.results, ctx.Done()); err != nil {
			g.errors <- err
		}
	}()
	return g
}

// Next returns the next match from the pattern. It returns an empty string when
// the matches are exhausted.
func (g *Result) Next() (string, error) {
	// Note: Next never returns filepath.ErrBadPattern if it has previously
	// returned a match. This isn't specified but it's highly desirable in
	// terms of least-surprise. I don't think there's a concise way for this
	// comment to justify this claim; you have to just read `stream` and
	// `filepath.Match` to convince yourself.
	select {
	case err := <-g.errors:
		g.Close()
		return "", err
	case r := <-g.results:
		return r, nil
	}
}

// Close cancels the in-progress globbing and cleans up. You can call this any
// time, including concurrently with Next. You don't need to call it if Next has
// returned an empty string.
func (g *Result) Close() error {
	g.cancel()
	for _ = range g.errors {
	}
	for _ = range g.results {
	}
	return nil
}

// stream finds files matching pattern and sends their paths on the results
// channel. It stops (returning nil) if the cancel channel is closed.
// The caller must drain the results channel.
func stream(pattern string, results chan<- string, cancel <-chan struct{}) error {
	if !hasMeta(pattern) {
		if _, err := os.Lstat(pattern); err != nil {
			return nil
		}
		results <- pattern
		return nil
	}

	dir, file := filepath.Split(pattern)
	volumeLen := 0
	if runtime.GOOS == "windows" {
		volumeLen, dir = cleanGlobPathWindows(dir)
	} else {
		dir = cleanGlobPath(dir)
	}

	if !hasMeta(dir[volumeLen:]) {
		return glob(dir, file, results, cancel)
	}

	// Prevent infinite recursion. See Go issue 15879.
	if dir == pattern {
		return filepath.ErrBadPattern
	}

	dirMatches := make(chan string)
	var streamErr error
	go func() {
		streamErr = stream(dir, dirMatches, cancel)
		close(dirMatches)
	}()

	for d := range dirMatches {
		if err := glob(d, file, results, cancel); err != nil {
			return err
		}
	}

	return streamErr
}

// cleanGlobPath prepares path for glob matching.
func cleanGlobPath(path string) string {
	switch path {
	case "":
		return "."
	case string(filepath.Separator):
		// do nothing to the path
		return path
	default:
		return path[0 : len(path)-1] // chop off trailing separator
	}
}

// cleanGlobPathWindows is windows version of cleanGlobPath.
func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
	vollen := len(filepath.VolumeName(path))
	switch {
	case path == "":
		return 0, "."
	case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
		// do nothing to the path
		return vollen + 1, path
	case vollen == len(path) && len(path) == 2: // C:
		return vollen, path + "." // convert C: into C:.
	default:
		if vollen >= len(path) {
			vollen = len(path) - 1
		}
		return vollen, path[0 : len(path)-1] // chop off trailing separator
	}
}

// glob searches for files matching pattern in the directory dir
// and sends them down the results channel. It stops if the chancel channel is
// closed.
func glob(dir, pattern string, results chan<- string, cancel <-chan struct{}) error {
	fi, err := os.Stat(dir)
	if err != nil {
		return nil
	}
	if !fi.IsDir() {
		return nil
	}
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()

	for {
		select {
		case <-cancel:
			return nil
		default:
		}

		names, err := d.Readdirnames(1)
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		n := names[0]

		matched, err := filepath.Match(pattern, n)
		if err != nil {
			return err
		}
		if matched {
			select {
			case results <- filepath.Join(dir, n):
			case <-cancel:
				return nil
			}
		}
	}
}

// hasMeta reports whether path contains any of the magic characters
// recognized by filepath.Match.
func hasMeta(path string) bool {
	magicChars := `*?[`
	if runtime.GOOS != "windows" {
		magicChars = `*?[\`
	}
	return strings.ContainsAny(path, magicChars)
}
<commit_msg>Drain channel from recursive stream call when glob fails<commit_after>// Copyright 2020 Google LLC

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Package glob provides equivalent functionality to filepath.Glob while
// meeting different performance requirements.
package glob

import (
	"context"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// Glob is similar to filepath.Glob but with different performance concerns.
//
// Firstly, It can be canceled via the context. Secondly, it makes no guarantees
// about the order of returned matches. This change allows it to run in O(d+m)
// memory and O(n) time, where m is the number of match results, d is the depth
// of the directory tree the pattern is concerned with, and n is the number of
// files in that tree.
func Glob(ctx context.Context, pattern string) ([]string, error) {
	gr := Stream(pattern)
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-ctx.Done()
		gr.Close()
	}()
	defer cancel()

	var ret []string
	for {
		match, err := gr.Next()
		if err != nil {
			return nil, err
		}
		if match == "" {
			break
		}
		ret = append(ret, match)
	}
	return ret, nil
}

// Result is a stream of results from globbing against a pattern.
type Result struct {
	errors  chan error
	results chan string
	cancel  context.CancelFunc
}

// Stream Returns a Result from which glob matches can be streamed.
//
// Stream supports the same pattern syntax and produces the same matches as Go's
// filepath.Glob, but makes no ordering guarantees.
func Stream(pattern string) Result {
	ctx, cancel := context.WithCancel(context.Background())
	g := Result{
		errors:  make(chan error),
		results: make(chan string),
		cancel:  cancel,
	}
	go func() {
		defer close(g.results)
		defer close(g.errors)
		if err := stream(pattern, g.results, ctx.Done()); err != nil {
			g.errors <- err
		}
	}()
	return g
}

// Next returns the next match from the pattern. It returns an empty string when
// the matches are exhausted.
func (g *Result) Next() (string, error) {
	// Note: Next never returns filepath.ErrBadPattern if it has previously
	// returned a match. This isn't specified but it's highly desirable in
	// terms of least-surprise. I don't think there's a concise way for this
	// comment to justify this claim; you have to just read `stream` and
	// `filepath.Match` to convince yourself.
	select {
	case err := <-g.errors:
		g.Close()
		return "", err
	case r := <-g.results:
		return r, nil
	}
}

// Close cancels the in-progress globbing and cleans up. You can call this any
// time, including concurrently with Next. You don't need to call it if Next has
// returned an empty string.
func (g *Result) Close() error {
	g.cancel()
	for _ = range g.errors {
	}
	for _ = range g.results {
	}
	return nil
}

// stream finds files matching pattern and sends their paths on the results
// channel. It stops (returning nil) if the cancel channel is closed.
// The caller must drain the results channel.
func stream(pattern string, results chan<- string, cancel <-chan struct{}) error {
	if !hasMeta(pattern) {
		if _, err := os.Lstat(pattern); err != nil {
			return nil
		}
		results <- pattern
		return nil
	}

	dir, file := filepath.Split(pattern)
	volumeLen := 0
	if runtime.GOOS == "windows" {
		volumeLen, dir = cleanGlobPathWindows(dir)
	} else {
		dir = cleanGlobPath(dir)
	}

	if !hasMeta(dir[volumeLen:]) {
		return glob(dir, file, results, cancel)
	}

	// Prevent infinite recursion. See Go issue 15879.
	if dir == pattern {
		return filepath.ErrBadPattern
	}

	dirMatches := make(chan string)
	var streamErr error
	go func() {
		streamErr = stream(dir, dirMatches, cancel)
		close(dirMatches)
	}()

	for d := range dirMatches {
		if err := glob(d, file, results, cancel); err != nil {
			// Drain channel before returning
			for range dirMatches {
			}
			return err
		}
	}

	return streamErr
}

// cleanGlobPath prepares path for glob matching.
func cleanGlobPath(path string) string {
	switch path {
	case "":
		return "."
	case string(filepath.Separator):
		// do nothing to the path
		return path
	default:
		return path[0 : len(path)-1] // chop off trailing separator
	}
}

// cleanGlobPathWindows is windows version of cleanGlobPath.
func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
	vollen := len(filepath.VolumeName(path))
	switch {
	case path == "":
		return 0, "."
	case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
		// do nothing to the path
		return vollen + 1, path
	case vollen == len(path) && len(path) == 2: // C:
		return vollen, path + "." // convert C: into C:.
	default:
		if vollen >= len(path) {
			vollen = len(path) - 1
		}
		return vollen, path[0 : len(path)-1] // chop off trailing separator
	}
}

// glob searches for files matching pattern in the directory dir
// and sends them down the results channel. It stops if the chancel channel is
// closed.
func glob(dir, pattern string, results chan<- string, cancel <-chan struct{}) error {
	fi, err := os.Stat(dir)
	if err != nil {
		return nil
	}
	if !fi.IsDir() {
		return nil
	}
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()

	for {
		select {
		case <-cancel:
			return nil
		default:
		}

		names, err := d.Readdirnames(1)
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		n := names[0]

		matched, err := filepath.Match(pattern, n)
		if err != nil {
			return err
		}
		if matched {
			select {
			case results <- filepath.Join(dir, n):
			case <-cancel:
				return nil
			}
		}
	}
}

// hasMeta reports whether path contains any of the magic characters
// recognized by filepath.Match.
func hasMeta(path string) bool {
	magicChars := `*?[`
	if runtime.GOOS != "windows" {
		magicChars = `*?[\`
	}
	return strings.ContainsAny(path, magicChars)
}
<|endoftext|>


<commit_before>package builder

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/cubicdaiya/nginx-build/command"
	"github.com/cubicdaiya/nginx-build/openresty"
)

type Builder struct {
	Version           string
	DownloadURLPrefix string
	Component         int
}

var (
	nginxVersionRe     *regexp.Regexp
	pcreVersionRe      *regexp.Regexp
	zlibVersionRe      *regexp.Regexp
	openSSLVersionRe   *regexp.Regexp
	openrestyVersionRe *regexp.Regexp
	tengineVersionRe   *regexp.Regexp
)

func init() {
	nginxVersionRe = regexp.MustCompile(`nginx version: nginx.(\d+\.\d+\.\d+)`)
	pcreVersionRe = regexp.MustCompile(`--with-pcre=.+/pcre-(\d+\.\d+)`)
	zlibVersionRe = regexp.MustCompile(`--with-zlib=.+/zlib-(\d+\.\d+\.\d+)`)
	openSSLVersionRe = regexp.MustCompile(`--with-openssl=.+/openssl-(\d+\.\d+\.\d+[a-z]+)`)
	openrestyVersionRe = regexp.MustCompile(`nginx version: openresty/(\d+\.\d+\.\d+\.\d+)`)
	tengineVersionRe = regexp.MustCompile(`Tengine version: Tengine/(\d+\.\d+\.\d+)`)
}

func (builder *Builder) name() string {
	var name string
	switch builder.Component {
	case ComponentNginx:
		name = "nginx"
	case ComponentPcre:
		name = "pcre"
	case ComponentOpenSSL:
		name = "openssl"
	case ComponentZlib:
		name = "zlib"
	case ComponentOpenResty:
		name = openresty.Name(builder.Version)
	case ComponentTengine:
		name = "tengine"
	default:
		panic("invalid component")
	}
	return name
}

func (builder *Builder) option() string {
	return fmt.Sprintf("--with-%s", builder.name())
}

func (builder *Builder) DownloadURL() string {
	return fmt.Sprintf("%s/%s", builder.DownloadURLPrefix, builder.ArchivePath())
}

func (builder *Builder) SourcePath() string {
	return fmt.Sprintf("%s-%s", builder.name(), builder.Version)
}

func (builder *Builder) ArchivePath() string {
	return fmt.Sprintf("%s.tar.gz", builder.SourcePath())
}

func (builder *Builder) LogPath() string {
	return fmt.Sprintf("%s-%s.log", builder.name(), builder.Version)
}

func (builder *Builder) IsIncludeWithOption(nginxConfigure string) bool {
	if strings.Contains(nginxConfigure, builder.option()+"=") {
		return true
	}
	return false
}

func (builder *Builder) WarnMsgWithLibrary() string {
	return fmt.Sprintf("[warn]Using '%s' is discouraged. Instead give '-%s' and '-%sversion' to 'nginx-build'",
		builder.option(), builder.name(), builder.name())
}

func (builder *Builder) InstalledVersion() (string, error) {
	args := []string{"/usr/local/sbin/nginx", "-V"}
	cmd, err := command.Make(args)
	if err != nil {
		return "", err
	}

	result, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}

	openRestyName := openresty.Name(builder.Version)

	switch builder.name() {
	case "nginx":
		m := nginxVersionRe.FindSubmatch(result)
		if len(m) < 2 {
			return "", nil
		}
		return string(m[1]), nil
	case openRestyName:
		m := openrestyVersionRe.FindSubmatch(result)
		if len(m) < 2 {
			return "", nil
		}
		return string(m[1]), nil
	case "zlib":
		m := zlibVersionRe.FindSubmatch(result)
		if len(m) < 2 {
			return "", nil
		}
		return string(m[1]), nil
	case "pcre":
		m := pcreVersionRe.FindSubmatch(result)
		if len(m) < 2 {
			return "", nil
		}
		return string(m[1]), nil
	case "openssl":
		m := openSSLVersionRe.FindSubmatch(result)
		if len(m) < 2 {
			return "", nil
		}
		return string(m[1]), nil
	case "tengine":
		m := tengineVersionRe.FindSubmatch(result)
		if len(m) < 2 {
			return "", nil
		}
		return string(m[1]), nil
	}

	return "", nil
}

func MakeBuilder(component int, version string) Builder {
	var builder Builder
	builder.Component = component
	builder.Version = version
	switch component {
	case ComponentNginx:
		builder.DownloadURLPrefix = NginxDownloadURLPrefix
	case ComponentPcre:
		builder.DownloadURLPrefix = PcreDownloadURLPrefix
	case ComponentOpenSSL:
		builder.DownloadURLPrefix = OpenSSLDownloadURLPrefix
	case ComponentZlib:
		builder.DownloadURLPrefix = ZlibDownloadURLPrefix
	case ComponentOpenResty:
		builder.DownloadURLPrefix = OpenRestyDownloadURLPrefix
	case ComponentTengine:
		builder.DownloadURLPrefix = TengineDownloadURLPrefix
	default:
		panic("invalid component")
	}
	return builder
}
<commit_msg>simplified.<commit_after>package builder

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/cubicdaiya/nginx-build/command"
	"github.com/cubicdaiya/nginx-build/openresty"
)

type Builder struct {
	Version           string
	DownloadURLPrefix string
	Component         int
}

var (
	nginxVersionRe     *regexp.Regexp
	pcreVersionRe      *regexp.Regexp
	zlibVersionRe      *regexp.Regexp
	opensslVersionRe   *regexp.Regexp
	openrestyVersionRe *regexp.Regexp
	tengineVersionRe   *regexp.Regexp
)

func init() {
	nginxVersionRe = regexp.MustCompile(`nginx version: nginx.(\d+\.\d+\.\d+)`)
	pcreVersionRe = regexp.MustCompile(`--with-pcre=.+/pcre-(\d+\.\d+)`)
	zlibVersionRe = regexp.MustCompile(`--with-zlib=.+/zlib-(\d+\.\d+\.\d+)`)
	opensslVersionRe = regexp.MustCompile(`--with-openssl=.+/openssl-(\d+\.\d+\.\d+[a-z]+)`)
	openrestyVersionRe = regexp.MustCompile(`nginx version: openresty/(\d+\.\d+\.\d+\.\d+)`)
	tengineVersionRe = regexp.MustCompile(`Tengine version: Tengine/(\d+\.\d+\.\d+)`)
}

func (builder *Builder) name() string {
	var name string
	switch builder.Component {
	case ComponentNginx:
		name = "nginx"
	case ComponentPcre:
		name = "pcre"
	case ComponentOpenSSL:
		name = "openssl"
	case ComponentZlib:
		name = "zlib"
	case ComponentOpenResty:
		name = openresty.Name(builder.Version)
	case ComponentTengine:
		name = "tengine"
	default:
		panic("invalid component")
	}
	return name
}

func (builder *Builder) option() string {
	return fmt.Sprintf("--with-%s", builder.name())
}

func (builder *Builder) DownloadURL() string {
	return fmt.Sprintf("%s/%s", builder.DownloadURLPrefix, builder.ArchivePath())
}

func (builder *Builder) SourcePath() string {
	return fmt.Sprintf("%s-%s", builder.name(), builder.Version)
}

func (builder *Builder) ArchivePath() string {
	return fmt.Sprintf("%s.tar.gz", builder.SourcePath())
}

func (builder *Builder) LogPath() string {
	return fmt.Sprintf("%s-%s.log", builder.name(), builder.Version)
}

func (builder *Builder) IsIncludeWithOption(nginxConfigure string) bool {
	if strings.Contains(nginxConfigure, builder.option()+"=") {
		return true
	}
	return false
}

func (builder *Builder) WarnMsgWithLibrary() string {
	return fmt.Sprintf("[warn]Using '%s' is discouraged. Instead give '-%s' and '-%sversion' to 'nginx-build'",
		builder.option(), builder.name(), builder.name())
}

func (builder *Builder) InstalledVersion() (string, error) {
	args := []string{"/usr/local/sbin/nginx", "-V"}
	cmd, err := command.Make(args)
	if err != nil {
		return "", err
	}

	result, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}

	openRestyName := openresty.Name(builder.Version)
	var versionRe *regexp.Regexp

	switch builder.name() {
	case "nginx":
		versionRe = nginxVersionRe
	case openRestyName:
		versionRe = openrestyVersionRe
	case "zlib":
		versionRe = zlibVersionRe
	case "pcre":
		versionRe = pcreVersionRe
	case "openssl":
		versionRe = opensslVersionRe
	case "tengine":
		versionRe = tengineVersionRe
	}

	m := versionRe.FindSubmatch(result)
	if len(m) < 2 {
		return "", nil
	}
	return string(m[1]), nil
}

func MakeBuilder(component int, version string) Builder {
	var builder Builder
	builder.Component = component
	builder.Version = version
	switch component {
	case ComponentNginx:
		builder.DownloadURLPrefix = NginxDownloadURLPrefix
	case ComponentPcre:
		builder.DownloadURLPrefix = PcreDownloadURLPrefix
	case ComponentOpenSSL:
		builder.DownloadURLPrefix = OpenSSLDownloadURLPrefix
	case ComponentZlib:
		builder.DownloadURLPrefix = ZlibDownloadURLPrefix
	case ComponentOpenResty:
		builder.DownloadURLPrefix = OpenRestyDownloadURLPrefix
	case ComponentTengine:
		builder.DownloadURLPrefix = TengineDownloadURLPrefix
	default:
		panic("invalid component")
	}
	return builder
}
<|endoftext|>


<commit_before>package jsonconsul

import (
	"fmt"
	"testing"
)

func ExampleJsonSet_RunBadJson() {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/blah", "\"a"})
	err := ji.Run()
	fmt.Println(err)

	// Output:
	// Can't set the key blah/blah invalid value: "a
}

func ExampleJsonSet_RunBadExpectedType() {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/blah", "true"})
	ji.setExpectedType("int")
	err := ji.Run()
	fmt.Println(err)

	// Output:
	// Invalid type. Value is a bool. Expected number
}

func TestJsonSet_RunGoodExpectedType(t *testing.T) {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/blah", "true"})
	ji.setExpectedType("bool")
	err := ji.Run()

	if err != nil {
		t.Error(err)
	}
}
<commit_msg>Need additonal quotes when passing strings<commit_after>package jsonconsul

import (
	"fmt"
	"testing"
)

func ExampleJsonSet_RunBadJson() {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/blah", "\"a"})
	err := ji.Run()
	fmt.Println(err)

	// Output:
	// Can't set the key blah/blah invalid value: "a
}

func ExampleJsonSet_RunBadExpectedType() {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/blah", "true"})
	ji.setExpectedType("int")
	err := ji.Run()
	fmt.Println(err)

	// Output:
	// Invalid type. Value is a bool. Expected number
}

func TestJsonSet_RunGoodExpectedTypeBool(t *testing.T) {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/blah", "true"})
	ji.setExpectedType("bool")
	err := ji.Run()

	if err != nil {
		t.Error(err)
	}
}

func TestJsonSet_RunGoodExpectedTypeString(t *testing.T) {
	ji := &JsonSet{}
	ji.ParseFlags([]string{"blah/str", "\"this is a string\""})
	ji.setExpectedType("string")
	err := ji.Run()

	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"os/user"
	"io/ioutil"
	"fmt"
	"os"
)

type Settings struct {
	Projects map[string]string `json:"projects"`
}

func LoadSettings() (settings *Settings, err error) {
	homedir := HomeDir()
	content, err := ioutil.ReadFile(homedir + "/.hack/config")

	// Lazily create hack config dir and retry
	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
		err = nil
		settings = &Settings{make(map[string]string)}
	}else{
		err = json.Unmarshal(content, &settings)
		if err != nil {
			return
		}
	}

	return
}

func (s *Settings) Write() (err error) {
	homedir := HomeDir()

	// Convert to json
	b, err := json.Marshal(s)
	if err != nil {
		return
	}

	// Create the config dir (no-op)
	err = os.MkdirAll(homedir + "/.hack", 0700)
	if err != nil {
		return
	}

	// Write to the file
    err = ioutil.WriteFile(homedir + "/.hack/config", b, 0700)
	if err != nil {
		return
	}
	return
}

func HomeDir() (string) {
	usr, err := user.Current()
    if err != nil {
        fmt.Println( err )
    }
    return usr.HomeDir
}
<commit_msg>That comment no longer applies...<commit_after>package main

import (
	"encoding/json"
	"os/user"
	"io/ioutil"
	"fmt"
	"os"
)

type Settings struct {
	Projects map[string]string `json:"projects"`
}

func LoadSettings() (settings *Settings, err error) {
	homedir := HomeDir()
	content, err := ioutil.ReadFile(homedir + "/.hack/config")

	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
		err = nil
		settings = &Settings{make(map[string]string)}
	}else{
		err = json.Unmarshal(content, &settings)
		if err != nil {
			return
		}
	}

	return
}

func (s *Settings) Write() (err error) {
	homedir := HomeDir()

	// Convert to json
	b, err := json.Marshal(s)
	if err != nil {
		return
	}

	// Create the config dir (no-op)
	err = os.MkdirAll(homedir + "/.hack", 0700)
	if err != nil {
		return
	}

	// Write to the file
    err = ioutil.WriteFile(homedir + "/.hack/config", b, 0700)
	if err != nil {
		return
	}
	return
}

func HomeDir() (string) {
	usr, err := user.Current()
    if err != nil {
        fmt.Println( err )
    }
    return usr.HomeDir
}
<|endoftext|>