<commit_before>package store

import (
	"encoding/json"
	"errors"
	"net/http"
	"sort"
	"time"

	"github.com/manishrjain/gocrud/x"
)

var (
	ErrNoParent = errors.New("No parent found")
)

// Query stores the read instrutions, storing the instruction set
// for the entities Query relates to.
type Query struct {
	kind       string
	id         string
	filterOut  map[string]bool
	maxDepth   int
	children   []*Query
	parent     *Query
	getDeleted bool
}

type Object struct {
	Value  interface{}
	Source string
	NanoTs int64
}

type Versions struct {
	versions []Object
}

// Result stores the final entity state retrieved from Store upon running
// the instructions provided by Query.
type Result struct {
	Id       string
	Kind     string
	Columns  map[string]*Versions
	Children []*Result
}

type runResult struct {
	Result *Result
	Err    error
}

func (v *Versions) add(o Object) {
	if len(v.versions) > 0 {
		i := len(v.versions) - 1
		if v.versions[i].NanoTs > o.NanoTs {
			// unsorted list. Gocrud code is doing something wrong.
			log.Fatal("Appending an object with lower ts to a sorted list")
		}
	}
	v.versions = append(v.versions, o)
}

func (v Versions) Latest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	i := len(v.versions) - 1
	return v.versions[i]
}

func (v Versions) Oldest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	return v.versions[0]
}

func (v Versions) Count() int {
	return len(v.versions)
}

// Retrieve the parent id for given entity id. Return ErrNoParent if parent is
// not present. Otherwise, if an error occurs during retrieval, returns that.
func Parent(id string) (parentid string, rerr error) {
	its, err := Get().GetEntity(id)
	if err != nil {
		x.LogErr(log, err).WithField("id", id).Error("While retrieving entity")
		return "", err
	}
	for _, it := range its {
		if it.Predicate == "_parent_" {
			return it.ObjectId, nil
		}
	}
	return "", ErrNoParent
}

// NewQuery is the main entrypoint to data store queries. Returns back a Query
// object pointer, to run read instructions on.
func NewQuery(id string) *Query {
	q := new(Query)
	q.id = id
	return q
}

// UptoDepth specifies the number of levels of descendants that would be
// retrieved for the entity Query points to.
//
// You can think of this in terms of a tree structure, where the Entity pointed
// to by Query points to other Entities, which in turn point to more Entities,
// and so on. For e.g. Post -> Comments -> Likes.
func (q *Query) UptoDepth(level int) *Query {
	q.maxDepth = level
	return q
}

func (q *Query) AllowDeleted() *Query {
	q.getDeleted = true
	return q
}

// Collect specifies the kind of child entities to retrieve. Returns back
// a new Query pointer pointing to those children entities as a collective.
//
// Any further operations on this returned pointer would attribute to those
// children entities, and not the caller query entity.
func (q *Query) Collect(kind string) *Query {
	for _, child := range q.children {
		if child.kind == kind {
			return child
		}
	}
	child := new(Query)
	child.parent = q
	child.kind = kind
	child.getDeleted = q.getDeleted
	q.children = append(q.children, child)
	return child
}

// FilterOut provides a way to well, filter out, any entities which have
// the given property.
func (q *Query) FilterOut(property string) *Query {
	if len(q.filterOut) == 0 {
		q.filterOut = make(map[string]bool)
	}
	q.filterOut[property] = true
	return q
}

func (q *Query) root() *Query {
	for q.parent != nil {
		q = q.parent
	}
	return q
}

func (q *Query) doRun(level, max int, ch chan runResult) {
	log.Debugf("Query: %+v", q)
	its, err := Get().GetEntity(q.id)
	if err != nil {
		x.LogErr(log, err).Error("While retrieving: ", q.id)
		ch <- runResult{Result: nil, Err: err}
		return
	}
	if len(its) == 0 {
		ch <- runResult{Result: new(Result), Err: nil}
		return
	}
	sort.Sort(x.Its(its))

	follow := make(map[string]*Query)
	for _, child := range q.children {
		follow[child.kind] = child
		log.WithField("kind", child.kind).WithField("child", child).Debug("Following")
	}

	result := new(Result)
	result.Columns = make(map[string]*Versions)
	it := its[0]
	result.Id = it.SubjectId
	result.Kind = it.SubjectType

	waitTimes := 0
	childChan := make(chan runResult)
	for _, it := range its {
		if it.Predicate == "_delete_" && !q.getDeleted {
			// If marked as deleted, don't return this node.
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("_delete_", true).
				Debug("Discarding due to delete bit")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if it.Predicate == "_parent_" {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("parent", it.ObjectId).
				Debug("Not following edge back to parent")
			continue
		}

		if _, fout := q.filterOut[it.Predicate]; fout {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("predicate", it.Predicate).
				Debug("Discarding due to predicate filter")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if len(it.ObjectId) == 0 {
			o := Object{NanoTs: it.NanoTs, Source: it.Source}
			if err := json.Unmarshal(it.Object, &o.Value); err != nil {
				x.LogErr(log, err).Error("While unmarshal")
				ch <- runResult{Result: nil, Err: err}
				return
			}

			if _, vok := result.Columns[it.Predicate]; !vok {
				result.Columns[it.Predicate] = new(Versions)
			}
			result.Columns[it.Predicate].add(o)
			continue
		}

		if childq, fw := follow[it.Predicate]; fw {
			nchildq := new(Query)
			*nchildq = *childq // This is important, otherwise id gets overwritten
			nchildq.id = it.ObjectId
			nchildq.getDeleted = q.getDeleted

			// Use child's maxDepth here, instead of parent's.
			waitTimes += 1
			log.WithField("child_id", nchildq.id).
				WithField("child_kind", nchildq.kind).Debug("Go routine for child")
			go nchildq.doRun(0, nchildq.maxDepth, childChan)
			continue
		}

		if len(it.ObjectId) > 0 && level < max {
			child := new(Query)
			child.id = it.ObjectId
			child.getDeleted = q.getDeleted

			waitTimes += 1
			log.WithField("child_id", child.id).WithField("level", level+1).
				Debug("Go routine for child one level deeper")
			go child.doRun(level+1, max, childChan)
		}
	}

	// Wait for all those subroutines
	for i := 0; i < waitTimes; i++ {
		log.Debugf("Waiting for children subroutines: %v/%v", i, waitTimes-1)
		rr := <-childChan
		log.Debugf("Waiting done")
		if rr.Err != nil {
			x.LogErr(log, err).Error("While child doRun")
		} else {
			if len(rr.Result.Id) > 0 && len(rr.Result.Kind) > 0 {
				log.WithField("result", *rr.Result).Debug("Appending child")
				result.Children = append(result.Children, rr.Result)
			}
		}
	}

	ch <- runResult{Result: result, Err: nil}
	return
}

// Run finds the root from the given Query pointer, recursively executes
// the read operations, and returns back pointer to Result object.
// Any errors encountered during these stpeps is returned as well.
func (q *Query) Run() (result *Result, rerr error) {
	q = q.root()

	ch := make(chan runResult)
	go q.doRun(0, q.maxDepth, ch)
	rr := <-ch // Blocking wait
	return rr.Result, rr.Err
}

func (r *Result) Drop(pred string) {
	delete(r.Columns, pred)
}

func (r *Result) Debug(level int) {
	log.Debugf("Result level %v: %+v", level, r)
	for _, child := range r.Children {
		child.Debug(level + 1)
	}
}

func (r *Result) ToMap() (data map[string]interface{}) {
	data = make(map[string]interface{})
	data["id"] = r.Id
	data["kind"] = r.Kind
	var ts_latest int64
	ts_oldest := time.Now().UnixNano()
	for pred, versions := range r.Columns {
		// During conversion to JSON, to keep things simple,
		// we're dropping older versions of predicates, and
		// source and ts information across all the predicates,
		// keeping only the latest one.
		data[pred] = versions.Latest().Value
		if versions.Latest().NanoTs > ts_latest {
			ts_latest = versions.Latest().NanoTs
			data["modifier"] = versions.Latest().Source // Loss of information.
		}
		if versions.Oldest().NanoTs < ts_oldest {
			ts_oldest = versions.Oldest().NanoTs
			data["creator"] = versions.Oldest().Source
		}
	}
	data["creation_ms"] = int(ts_oldest / 1000000)
	data["modification_ms"] = int(ts_latest / 1000000) // Loss of information. Picking up latest mod time.
	kinds := make(map[string]bool)
	for _, child := range r.Children {
		kinds[child.Kind] = true
	}

	for kind := range kinds {
		var l []map[string]interface{}
		for _, child := range r.Children {
			if kind != child.Kind {
				continue
			}
			l = append(l, child.ToMap())
		}
		data[kind] = l
	}

	return
}

// ToJson creates the JSON for the data pointed by the Result pointer.
//
// Note that this doesn't find the "root" from the Result pointer, instead
// doing the processing only from the current Result pointer.
func (r *Result) ToJson() ([]byte, error) {
	data := r.ToMap()
	return json.Marshal(data)
}

// WriteJsonResponse does the same as ToJson. But also writes the JSON
// generated to http.ResponseWriter. In case of error, writes that error
// instead, in this format:
//
//  {
//   "code":    "E_ERROR",
//   "message": "error message"
//  }
func (r *Result) WriteJsonResponse(w http.ResponseWriter) {
	data, err := r.ToJson()
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
	_, err = w.Write(data)
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
}
<commit_msg>Document AllowDeleted()<commit_after>package store

import (
	"encoding/json"
	"errors"
	"net/http"
	"sort"
	"time"

	"github.com/manishrjain/gocrud/x"
)

var (
	ErrNoParent = errors.New("No parent found")
)

// Query stores the read instrutions, storing the instruction set
// for the entities Query relates to.
type Query struct {
	kind       string
	id         string
	filterOut  map[string]bool
	maxDepth   int
	children   []*Query
	parent     *Query
	getDeleted bool
}

type Object struct {
	Value  interface{}
	Source string
	NanoTs int64
}

type Versions struct {
	versions []Object
}

// Result stores the final entity state retrieved from Store upon running
// the instructions provided by Query.
type Result struct {
	Id       string
	Kind     string
	Columns  map[string]*Versions
	Children []*Result
}

type runResult struct {
	Result *Result
	Err    error
}

func (v *Versions) add(o Object) {
	if len(v.versions) > 0 {
		i := len(v.versions) - 1
		if v.versions[i].NanoTs > o.NanoTs {
			// unsorted list. Gocrud code is doing something wrong.
			log.Fatal("Appending an object with lower ts to a sorted list")
		}
	}
	v.versions = append(v.versions, o)
}

func (v Versions) Latest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	i := len(v.versions) - 1
	return v.versions[i]
}

func (v Versions) Oldest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	return v.versions[0]
}

func (v Versions) Count() int {
	return len(v.versions)
}

// Retrieve the parent id for given entity id. Return ErrNoParent if parent is
// not present. Otherwise, if an error occurs during retrieval, returns that.
func Parent(id string) (parentid string, rerr error) {
	its, err := Get().GetEntity(id)
	if err != nil {
		x.LogErr(log, err).WithField("id", id).Error("While retrieving entity")
		return "", err
	}
	for _, it := range its {
		if it.Predicate == "_parent_" {
			return it.ObjectId, nil
		}
	}
	return "", ErrNoParent
}

// NewQuery is the main entrypoint to data store queries. Returns back a Query
// object pointer, to run read instructions on.
func NewQuery(id string) *Query {
	q := new(Query)
	q.id = id
	return q
}

// UptoDepth specifies the number of levels of descendants that would be
// retrieved for the entity Query points to.
//
// You can think of this in terms of a tree structure, where the Entity pointed
// to by Query points to other Entities, which in turn point to more Entities,
// and so on. For e.g. Post -> Comments -> Likes.
func (q *Query) UptoDepth(level int) *Query {
	q.maxDepth = level
	return q
}

// AllowDeleted will make this query not ignore entities with the _delete_ flag
// set. Use this to retrieve deleted entities if required.

func (q *Query) AllowDeleted() *Query {
	q.getDeleted = true
	return q
}

// Collect specifies the kind of child entities to retrieve. Returns back
// a new Query pointer pointing to those children entities as a collective.
//
// Any further operations on this returned pointer would attribute to those
// children entities, and not the caller query entity.
func (q *Query) Collect(kind string) *Query {
	for _, child := range q.children {
		if child.kind == kind {
			return child
		}
	}
	child := new(Query)
	child.parent = q
	child.kind = kind
	child.getDeleted = q.getDeleted
	q.children = append(q.children, child)
	return child
}

// FilterOut provides a way to well, filter out, any entities which have
// the given property.
func (q *Query) FilterOut(property string) *Query {
	if len(q.filterOut) == 0 {
		q.filterOut = make(map[string]bool)
	}
	q.filterOut[property] = true
	return q
}

func (q *Query) root() *Query {
	for q.parent != nil {
		q = q.parent
	}
	return q
}

func (q *Query) doRun(level, max int, ch chan runResult) {
	log.Debugf("Query: %+v", q)
	its, err := Get().GetEntity(q.id)
	if err != nil {
		x.LogErr(log, err).Error("While retrieving: ", q.id)
		ch <- runResult{Result: nil, Err: err}
		return
	}
	if len(its) == 0 {
		ch <- runResult{Result: new(Result), Err: nil}
		return
	}
	sort.Sort(x.Its(its))

	follow := make(map[string]*Query)
	for _, child := range q.children {
		follow[child.kind] = child
		log.WithField("kind", child.kind).WithField("child", child).Debug("Following")
	}

	result := new(Result)
	result.Columns = make(map[string]*Versions)
	it := its[0]
	result.Id = it.SubjectId
	result.Kind = it.SubjectType

	waitTimes := 0
	childChan := make(chan runResult)
	for _, it := range its {
		if it.Predicate == "_delete_" && !q.getDeleted {
			// If marked as deleted, don't return this node.
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("_delete_", true).
				Debug("Discarding due to delete bit")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if it.Predicate == "_parent_" {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("parent", it.ObjectId).
				Debug("Not following edge back to parent")
			continue
		}

		if _, fout := q.filterOut[it.Predicate]; fout {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("predicate", it.Predicate).
				Debug("Discarding due to predicate filter")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if len(it.ObjectId) == 0 {
			o := Object{NanoTs: it.NanoTs, Source: it.Source}
			if err := json.Unmarshal(it.Object, &o.Value); err != nil {
				x.LogErr(log, err).Error("While unmarshal")
				ch <- runResult{Result: nil, Err: err}
				return
			}

			if _, vok := result.Columns[it.Predicate]; !vok {
				result.Columns[it.Predicate] = new(Versions)
			}
			result.Columns[it.Predicate].add(o)
			continue
		}

		if childq, fw := follow[it.Predicate]; fw {
			nchildq := new(Query)
			*nchildq = *childq // This is important, otherwise id gets overwritten
			nchildq.id = it.ObjectId
			nchildq.getDeleted = q.getDeleted

			// Use child's maxDepth here, instead of parent's.
			waitTimes += 1
			log.WithField("child_id", nchildq.id).
				WithField("child_kind", nchildq.kind).Debug("Go routine for child")
			go nchildq.doRun(0, nchildq.maxDepth, childChan)
			continue
		}

		if len(it.ObjectId) > 0 && level < max {
			child := new(Query)
			child.id = it.ObjectId
			child.getDeleted = q.getDeleted

			waitTimes += 1
			log.WithField("child_id", child.id).WithField("level", level+1).
				Debug("Go routine for child one level deeper")
			go child.doRun(level+1, max, childChan)
		}
	}

	// Wait for all those subroutines
	for i := 0; i < waitTimes; i++ {
		log.Debugf("Waiting for children subroutines: %v/%v", i, waitTimes-1)
		rr := <-childChan
		log.Debugf("Waiting done")
		if rr.Err != nil {
			x.LogErr(log, err).Error("While child doRun")
		} else {
			if len(rr.Result.Id) > 0 && len(rr.Result.Kind) > 0 {
				log.WithField("result", *rr.Result).Debug("Appending child")
				result.Children = append(result.Children, rr.Result)
			}
		}
	}

	ch <- runResult{Result: result, Err: nil}
	return
}

// Run finds the root from the given Query pointer, recursively executes
// the read operations, and returns back pointer to Result object.
// Any errors encountered during these stpeps is returned as well.
func (q *Query) Run() (result *Result, rerr error) {
	q = q.root()

	ch := make(chan runResult)
	go q.doRun(0, q.maxDepth, ch)
	rr := <-ch // Blocking wait
	return rr.Result, rr.Err
}

func (r *Result) Drop(pred string) {
	delete(r.Columns, pred)
}

func (r *Result) Debug(level int) {
	log.Debugf("Result level %v: %+v", level, r)
	for _, child := range r.Children {
		child.Debug(level + 1)
	}
}

func (r *Result) ToMap() (data map[string]interface{}) {
	data = make(map[string]interface{})
	data["id"] = r.Id
	data["kind"] = r.Kind
	var ts_latest int64
	ts_oldest := time.Now().UnixNano()
	for pred, versions := range r.Columns {
		// During conversion to JSON, to keep things simple,
		// we're dropping older versions of predicates, and
		// source and ts information across all the predicates,
		// keeping only the latest one.
		data[pred] = versions.Latest().Value
		if versions.Latest().NanoTs > ts_latest {
			ts_latest = versions.Latest().NanoTs
			data["modifier"] = versions.Latest().Source // Loss of information.
		}
		if versions.Oldest().NanoTs < ts_oldest {
			ts_oldest = versions.Oldest().NanoTs
			data["creator"] = versions.Oldest().Source
		}
	}
	data["creation_ms"] = int(ts_oldest / 1000000)
	data["modification_ms"] = int(ts_latest / 1000000) // Loss of information. Picking up latest mod time.
	kinds := make(map[string]bool)
	for _, child := range r.Children {
		kinds[child.Kind] = true
	}

	for kind := range kinds {
		var l []map[string]interface{}
		for _, child := range r.Children {
			if kind != child.Kind {
				continue
			}
			l = append(l, child.ToMap())
		}
		data[kind] = l
	}

	return
}

// ToJson creates the JSON for the data pointed by the Result pointer.
//
// Note that this doesn't find the "root" from the Result pointer, instead
// doing the processing only from the current Result pointer.
func (r *Result) ToJson() ([]byte, error) {
	data := r.ToMap()
	return json.Marshal(data)
}

// WriteJsonResponse does the same as ToJson. But also writes the JSON
// generated to http.ResponseWriter. In case of error, writes that error
// instead, in this format:
//
//  {
//   "code":    "E_ERROR",
//   "message": "error message"
//  }
func (r *Result) WriteJsonResponse(w http.ResponseWriter) {
	data, err := r.ToJson()
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
	_, err = w.Write(data)
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
}
<|endoftext|>


<commit_before>package store

import (
	"crypto/x509"
	"errors"
	"fmt"
	"strings"
)

)

// Store represents a certificate store (often called 'pool') and has
// operations on it which mutate the underlying state (e.g. a file or
// directory).
type Store interface {
	// List returns the currently trusted X509 certificates contained
	// within the cert store
	List() ([]*x509.Certificate, error)
}

// Platform returns a new instance of Store for the running os/platform
func Platform() Store {
	return platform()
}

// ForApp returns a `Store` instance for the given app
func ForApp(app string) (Store, error) {
	switch strings.ToLower(app) {
	case "chrome":
		return NssStore(), nil
	case "firefox":
		return NssStore(), nil
	case "java":
		return JavaStore(), nil
	default:
		return nil, fmt.Errorf("application '%s' not found", app)
	}
}
<commit_msg>fix compile<commit_after>package store

import (
	"crypto/x509"
	"errors"
	"fmt"
	"strings"
)

// Store represents a certificate store (often called 'pool') and has
// operations on it which mutate the underlying state (e.g. a file or
// directory).
type Store interface {
	// List returns the currently trusted X509 certificates contained
	// within the cert store
	List() ([]*x509.Certificate, error)
}

// Platform returns a new instance of Store for the running os/platform
func Platform() Store {
	return platform()
}

// ForApp returns a `Store` instance for the given app
func ForApp(app string) (Store, error) {
	switch strings.ToLower(app) {
	case "chrome":
		return NssStore(), nil
	case "firefox":
		return NssStore(), nil
	case "java":
		return JavaStore(), nil
	default:
		return nil, fmt.Errorf("application '%s' not found", app)
	}
}
<|endoftext|>


<commit_before>package trousseau

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
)

type ScpStorage struct {
	host      string
	port      string
	connexion *ssh.ClientConn

	Keychain *Keychain
	User     string
	Endpoint string
}

type Keychain struct {
	key *rsa.PrivateKey
}

func NewScpStorage(host, port, user string, keychain *Keychain) *ScpStorage {
	return &ScpStorage{
		Keychain: keychain,
		User:     user,
		Endpoint: strings.Join([]string{host, port}, ":"),
		host:     host,
		port:     port,
	}
}

func NewKeychain(key *rsa.PrivateKey) *Keychain {
	return &Keychain{
		key: key,
	}
}

func DecodePrivateKeyFromFile(privateKeyPath string) (*rsa.PrivateKey, error) {
	keyContent, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode([]byte(keyContent))
	rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return rsakey, nil
}

func (k *Keychain) Key(i int) (key ssh.PublicKey, err error) {
	if i != 0 {
		return nil, nil
	}

    // Transform the rsa key into an ssh key
    ssh_publickey, _ := ssh.NewPublicKey(k.key.PublicKey)

	return ssh_publickey, nil
}

func (k *Keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
	hashFunc := crypto.SHA1
	h := hashFunc.New()
	h.Write(data)
	digest := h.Sum(nil)
	return rsa.SignPKCS1v15(rand, k.key, hashFunc, digest)
}

func (ss *ScpStorage) Connect() error {
	var err error

	clientConfig := &ssh.ClientConfig{
		User: ss.User,
		Auth: []ssh.ClientAuth{
			ssh.ClientAuthKeyring(ss.Keychain),
		},
	}

	ss.connexion, err = ssh.Dial("tcp", ss.Endpoint, clientConfig)
	if err != nil {
		return fmt.Errorf("Failed to dial: %s", err.Error())
	}

	return nil
}

func (ss *ScpStorage) Push(remoteName string) error {
	session, err := ss.connexion.NewSession()
	if err != nil {
		return fmt.Errorf("Failed to create session: %s", err.Error())
	}
	defer session.Close()

	go func() {
		w, _ := session.StdinPipe()
		defer w.Close()

		content, _ := ioutil.ReadFile(gStorePath)

		fmt.Fprintln(w, "C0755", len(content), remoteName)
		fmt.Fprint(w, string(content))
		fmt.Fprint(w, "\x00")
	}()

	if err := session.Run("/usr/bin/scp -qrt ./"); err != nil {
		return fmt.Errorf("Failed to run: %s", err.Error())
	}

	return nil
}

func (ss *ScpStorage) Pull(remoteName string) error {
	session, err := ss.connexion.NewSession()
	if err != nil {
		return fmt.Errorf("Failed to create session: %s", err.Error())
	}
	defer session.Close()

	var remoteFileBuffer bytes.Buffer
	session.Stdout = &remoteFileBuffer

	if err := session.Run(fmt.Sprintf("cat %s", remoteName)); err != nil {
		return fmt.Errorf("Failed to run: %s", err.Error())
	}

	err = ioutil.WriteFile(gStorePath, remoteFileBuffer.Bytes(), 0744)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix go fmt<commit_after>package trousseau

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
)

type ScpStorage struct {
	host      string
	port      string
	connexion *ssh.ClientConn

	Keychain *Keychain
	User     string
	Endpoint string
}

type Keychain struct {
	key *rsa.PrivateKey
}

func NewScpStorage(host, port, user string, keychain *Keychain) *ScpStorage {
	return &ScpStorage{
		Keychain: keychain,
		User:     user,
		Endpoint: strings.Join([]string{host, port}, ":"),
		host:     host,
		port:     port,
	}
}

func NewKeychain(key *rsa.PrivateKey) *Keychain {
	return &Keychain{
		key: key,
	}
}

func DecodePrivateKeyFromFile(privateKeyPath string) (*rsa.PrivateKey, error) {
	keyContent, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode([]byte(keyContent))
	rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return rsakey, nil
}

func (k *Keychain) Key(i int) (key ssh.PublicKey, err error) {
	if i != 0 {
		return nil, nil
	}

	// Transform the rsa key into an ssh key
	ssh_publickey, _ := ssh.NewPublicKey(k.key.PublicKey)

	return ssh_publickey, nil
}

func (k *Keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
	hashFunc := crypto.SHA1
	h := hashFunc.New()
	h.Write(data)
	digest := h.Sum(nil)
	return rsa.SignPKCS1v15(rand, k.key, hashFunc, digest)
}

func (ss *ScpStorage) Connect() error {
	var err error

	clientConfig := &ssh.ClientConfig{
		User: ss.User,
		Auth: []ssh.ClientAuth{
			ssh.ClientAuthKeyring(ss.Keychain),
		},
	}

	ss.connexion, err = ssh.Dial("tcp", ss.Endpoint, clientConfig)
	if err != nil {
		return fmt.Errorf("Failed to dial: %s", err.Error())
	}

	return nil
}

func (ss *ScpStorage) Push(remoteName string) error {
	session, err := ss.connexion.NewSession()
	if err != nil {
		return fmt.Errorf("Failed to create session: %s", err.Error())
	}
	defer session.Close()

	go func() {
		w, _ := session.StdinPipe()
		defer w.Close()

		content, _ := ioutil.ReadFile(gStorePath)

		fmt.Fprintln(w, "C0755", len(content), remoteName)
		fmt.Fprint(w, string(content))
		fmt.Fprint(w, "\x00")
	}()

	if err := session.Run("/usr/bin/scp -qrt ./"); err != nil {
		return fmt.Errorf("Failed to run: %s", err.Error())
	}

	return nil
}

func (ss *ScpStorage) Pull(remoteName string) error {
	session, err := ss.connexion.NewSession()
	if err != nil {
		return fmt.Errorf("Failed to create session: %s", err.Error())
	}
	defer session.Close()

	var remoteFileBuffer bytes.Buffer
	session.Stdout = &remoteFileBuffer

	if err := session.Run(fmt.Sprintf("cat %s", remoteName)); err != nil {
		return fmt.Errorf("Failed to run: %s", err.Error())
	}

	err = ioutil.WriteFile(gStorePath, remoteFileBuffer.Bytes(), 0744)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"runtime/pprof"
	"time"
)

const (
	KB          = 1024
	MB          = 1024 * KB
	GB          = 1024 * MB
	BLOCK_SIZE  = 4 * KB
	BUFFER_SIZE = GB / BLOCK_SIZE
)

func main() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v", err)
			os.Exit(3)
		}
	}()

	cpuprofile := flag.String("cpuprofile", "", "write cpu profiling data")
	flag.Parse()

	if *cpuprofile != "" {
		cpu, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		pprof.StartCPUProfile(cpu)
		defer pprof.StopCPUProfile()
	}

	if flag.NArg() != 2 {
		fmt.Fprintf(os.Stderr, "%s source destination", os.Args[0])
		os.Exit(1)
	}
	src := flag.Arg(0)
	dst := flag.Arg(1)

	start := time.Now()
	reads, writes, err := Sync(src, dst)
	duration := time.Since(start)

	if err != nil {
		fmt.Fprintf(os.Stderr, "%s", err.Error())
		os.Exit(2)
	}

	ratio := 0.0
	if reads > 0 {
		ratio = float64(writes) / float64(reads) * 100
	}
	fmt.Printf("reads\t%d\nwrites\t%d\nratio\t%3.2f%%\ntime\t%v\n", reads, writes, ratio, duration)
}

type Op struct {
	Data   []byte
	Offset int64
}

func Sync(src, dst string) (reads, writes int64, err error) {

	sf, err := os.Open(src)
	if err != nil {
		return
	}
	defer sf.Close()

	df, err := os.OpenFile(dst, os.O_RDWR, 0)
	if err != nil {
		return
	}
	defer df.Close()

	si, err := sf.Stat()
	if err != nil {
		return
	}
	size := si.Size()
	blocks := size / BLOCK_SIZE
	if size%BLOCK_SIZE > 0 {
		blocks += 1
	}

	err = df.Truncate(size)
	if err != nil {
		return
	}

	err = df.Sync()
	if err != nil {
		return
	}

	sr := make(chan Op, BUFFER_SIZE)
	dr := make(chan Op, BUFFER_SIZE)

	sw := make(chan Op, BUFFER_SIZE)
	dw := make(chan Op, BUFFER_SIZE)

	go ReadWrite(sf, sr, sw)
	go ReadWrite(df, dr, dw)

	for reads = int64(0); reads < blocks; reads++ {
		s, d := <-sr, <-dr
		if !Compare(s.Data, d.Data) {
			dw <- Op{s.Data, s.Offset}
			writes++
		}
	}

	close(sw)
	close(dw)

	<-dr
	<-sr

	return

}

func ReadWrite(file *os.File, read, write chan Op) {

	defer close(read)

	for offset := int64(0); ; {
		select {
		case w, ok := <-write:
			if !ok {
				return
			}
			_, err := file.WriteAt(w.Data, w.Offset)
			if err != nil {
				panic(err)
			}
			err = file.Sync()
			if err != nil {
				panic(err)
			}
		default:
			data := make([]byte, BLOCK_SIZE)
			n, err := file.Read(data)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n != 0 {
				read <- Op{data[:n], offset}
				offset += int64(n)
			}
		}
	}

}

func Compare(b1, b2 []byte) bool {
	if len(b1) != len(b2) {
		return false
	}
	for i := 0; i < len(b1); i++ {
		if b1[i] != b2[i] {
			return false
		}
	}
	return true
}
<commit_msg>moved sync<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"runtime/pprof"
	"time"
)

const (
	KB          = 1024
	MB          = 1024 * KB
	GB          = 1024 * MB
	BLOCK_SIZE  = 4 * KB
	BUFFER_SIZE = GB / BLOCK_SIZE
)

func main() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v", err)
			os.Exit(3)
		}
	}()

	cpuprofile := flag.String("cpuprofile", "", "write cpu profiling data")
	flag.Parse()

	if *cpuprofile != "" {
		cpu, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		pprof.StartCPUProfile(cpu)
		defer pprof.StopCPUProfile()
	}

	if flag.NArg() != 2 {
		fmt.Fprintf(os.Stderr, "%s source destination", os.Args[0])
		os.Exit(1)
	}
	src := flag.Arg(0)
	dst := flag.Arg(1)

	start := time.Now()
	reads, writes, err := Sync(src, dst)
	duration := time.Since(start)

	if err != nil {
		fmt.Fprintf(os.Stderr, "%s", err.Error())
		os.Exit(2)
	}

	ratio := 0.0
	if reads > 0 {
		ratio = float64(writes) / float64(reads) * 100
	}
	fmt.Printf("reads\t%d\nwrites\t%d\nratio\t%3.2f%%\ntime\t%v\n", reads, writes, ratio, duration)
}

type Op struct {
	Data   []byte
	Offset int64
}

func Sync(src, dst string) (reads, writes int64, err error) {

	sf, err := os.Open(src)
	if err != nil {
		return
	}
	defer sf.Close()

	df, err := os.OpenFile(dst, os.O_RDWR, 0)
	if err != nil {
		return
	}
	defer df.Close()

	si, err := sf.Stat()
	if err != nil {
		return
	}
	size := si.Size()
	blocks := size / BLOCK_SIZE
	if size%BLOCK_SIZE > 0 {
		blocks += 1
	}

	err = df.Truncate(size)
	if err != nil {
		return
	}

	sr := make(chan Op, BUFFER_SIZE)
	dr := make(chan Op, BUFFER_SIZE)

	sw := make(chan Op, BUFFER_SIZE)
	dw := make(chan Op, BUFFER_SIZE)

	go ReadWrite(sf, sr, sw)
	go ReadWrite(df, dr, dw)

	for reads = int64(0); reads < blocks; reads++ {
		s, d := <-sr, <-dr
		if !Compare(s.Data, d.Data) {
			dw <- Op{s.Data, s.Offset}
			writes++
		}
	}

	close(sw)
	close(dw)

	<-dr
	<-sr

	err = df.Sync()
	if err != nil {
		panic(err)
	}

	return

}

func ReadWrite(file *os.File, read, write chan Op) {

	defer close(read)

	for offset := int64(0); ; {
		select {
		case w, ok := <-write:
			if !ok {
				return
			}
			_, err := file.WriteAt(w.Data, w.Offset)
			if err != nil {
				panic(err)
			}
		default:
			data := make([]byte, BLOCK_SIZE)
			n, err := file.Read(data)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n != 0 {
				read <- Op{data[:n], offset}
				offset += int64(n)
			}
		}
	}

}

func Compare(b1, b2 []byte) bool {
	if len(b1) != len(b2) {
		return false
	}
	for i := 0; i < len(b1); i++ {
		if b1[i] != b2[i] {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"

	"github.com/codegangsta/cli"
)

type generate struct {
	Name        string
	UpperName   string
	FirstLetter string
}

func Generate() cli.Command {
	return cli.Command{
		Name:  "generate",
		Usage: "Generate new Gobot skeleton project",
		Action: func(c *cli.Context) {
			args := c.Args()
			if len(args) == 0 || len(args) > 1 {
				fmt.Println("Please provide a one word package name.")
				return
			}
			pwd, _ := os.Getwd()
			dir := fmt.Sprintf("%s/%s", pwd, "gobot-"+args[0])
			fmt.Println("Creating", dir)
			err := os.MkdirAll(dir, 0700)
			if err != nil {
				fmt.Println(err)
				err = nil
			}

			examplesDir := dir + "/examples"
			fmt.Println("Creating", examplesDir)
			err = os.MkdirAll(examplesDir, 0700)
			if err != nil {
				fmt.Println(err)
				err = nil
			}

			upperName := fmt.Sprintf("%s%s",
				strings.ToUpper(string(args[0][0])),
				string(args[0][1:]))

			name := generate{
				UpperName:   upperName,
				Name:        string(args[0]),
				FirstLetter: string(args[0][0]),
			}

			adaptor, _ := template.New("").Parse(adaptor())
			fileLocation := fmt.Sprintf("%s/%s_adaptor.go", dir, args[0])
			fmt.Println("Creating", fileLocation)
			f, err := os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}
			adaptor.Execute(f, name)
			f.Close()

			driver, _ := template.New("").Parse(driver())
			fileLocation = fmt.Sprintf("%s/%s_driver.go", dir, args[0])
			fmt.Println("Creating", fileLocation)
			f, err = os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}
			driver.Execute(f, name)
			f.Close()

			fileLocation = fmt.Sprintf("%s/LICENSE", dir)
			fmt.Println("Creating", fileLocation)
			f, err = os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}
			f.Close()

			driverTest, _ := template.New("").Parse(driverTest())
			fileLocation = fmt.Sprintf("%s/%s_driver_test.go", dir, args[0])
			fmt.Println("Creating", fileLocation)
			f, err = os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}
			driverTest.Execute(f, name)
			f.Close()

			adaptorTest, _ := template.New("").Parse(adaptorTest())
			fileLocation = fmt.Sprintf("%s/%s_adaptor_test.go", dir, args[0])
			fmt.Println("Creating", fileLocation)
			f, err = os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}
			adaptorTest.Execute(f, name)
			f.Close()

			example, _ := template.New("").Parse(example())
			fileLocation = fmt.Sprintf("%s/main.go", examplesDir)
			fmt.Println("Creating", fileLocation)
			f, err = os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}
			example.Execute(f, name)
			f.Close()

			readme, _ := template.New("").Parse(readme())
			fileLocation = fmt.Sprintf("%s/README.md", dir)
			fmt.Println("Creating", fileLocation)
			f, err = os.Create(fileLocation)
			if err != nil {
				fmt.Println(err)
				err = nil
			}

			ex, _ := ioutil.ReadFile(examplesDir + "/main.go")
			data := struct {
				Name    string
				Example string
			}{
				name.Name,
				string(ex),
			}
			readme.Execute(f, data)
			f.Close()
		},
	}
}

func adaptor() string {
	return `package {{.Name}}

import (
	"github.com/hybridgroup/gobot"
)

var _ gobot.Adaptor = (*{{.UpperName}}Adaptor)(nil)

type {{.UpperName}}Adaptor struct {
	name string
}

func New{{.UpperName}}Adaptor(name string) *{{.UpperName}}Adaptor {
	return &{{.UpperName}}Adaptor{
		name: name,
	}
}

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Name() string { return {{.FirstLetter}}.name }

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Connect() []error { return nil }

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Finalize() []error { return nil }

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Ping() string { return "pong" }
`
}

func driver() string {
	return `package {{.Name }}

import (
	"time"

	"github.com/hybridgroup/gobot"
)

var _ gobot.Driver = (*{{.UpperName}}Driver)(nil)

const Hello string = "hello"

type {{.UpperName}}Driver struct {
	name string
	connection gobot.Connection
	interval time.Duration
	halt chan bool
	gobot.Eventer
	gobot.Commander
}

func New{{.UpperName}}Driver(a *{{.UpperName}}Adaptor, name string) *{{.UpperName}}Driver {
	{{.FirstLetter}} := &{{.UpperName}}Driver{
		name: name,
		connection: a,
		interval: 500*time.Millisecond,
		halt: make(chan bool, 0),
    Eventer:    gobot.NewEventer(),
    Commander:  gobot.NewCommander(),
	}

	{{.FirstLetter}}.AddEvent(Hello)

	{{.FirstLetter}}.AddCommand(Hello, func(params map[string]interface{}) interface{} {
		return {{.FirstLetter}}.Hello()
	})

	return {{.FirstLetter}}
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Name() string { return {{.FirstLetter}}.name }

func ({{.FirstLetter}} *{{.UpperName}}Driver) Connection() gobot.Connection {
	return {{.FirstLetter}}.connection
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) adaptor() *{{.UpperName}}Adaptor {
	return {{.FirstLetter}}.Connection().(*{{.UpperName}}Adaptor)
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Hello() string {
	return "hello from " + {{.FirstLetter}}.Name() + "!"
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Ping() string {
	return {{.FirstLetter}}.adaptor().Ping()
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Start() []error {
	go func() {
		for {
			gobot.Publish({{.FirstLetter}}.Event(Hello), {{.FirstLetter}}.Hello())

			select {
			case <- time.After({{.FirstLetter}}.interval):
			case <- {{.FirstLetter}}.halt:
				return
			}
		}
	}()
	return nil
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Halt() []error {
	{{.FirstLetter}}.halt <- true
	return nil
}

`
}

func example() string {
	return `
package main

import (
  "../"
  "fmt"
  "time"

  "github.com/hybridgroup/gobot"
)

func main() {
  gbot := gobot.NewGobot()

  conn := {{.Name}}.New{{.UpperName}}Adaptor("conn")
  dev := {{.Name}}.New{{.UpperName}}Driver(conn, "dev")

  work := func() {
    gobot.On(dev.Event({{.Name}}.Hello), func(data interface{}) {
      fmt.Println(data)
    })

    gobot.Every(1200*time.Millisecond, func() {
      fmt.Println(dev.Ping())
    })
  }

  robot := gobot.NewRobot(
    "robot",
    []gobot.Connection{conn},
    []gobot.Device{dev},
    work,
  )

  gbot.AddRobot(robot)
  gbot.Start()
}
`
}

func driverTest() string {
	return `package {{.Name}}

import (
	"testing"
	"time"

	"github.com/hybridgroup/gobot"
)

func Test{{.UpperName}}Driver(t *testing.T) {
	d := New{{.UpperName}}Driver(New{{.UpperName}}Adaptor("conn"), "dev")

	gobot.Assert(t, d.Name(), "dev")
	gobot.Assert(t, d.Connection().Name(), "conn")

	ret := d.Command(Hello)(nil)
	gobot.Assert(t, ret.(string), "hello from dev!")

	gobot.Assert(t, d.Ping(), "pong")

	gobot.Assert(t, len(d.Start()), 0)

	<-time.After(d.interval)

	sem := make(chan bool, 0)

	gobot.On(d.Event(Hello), func(data interface{}) {
		sem <- true
	})

	select {
	case <-sem:
	case <-time.After(600 * time.Millisecond):
		t.Errorf("Hello Event was not published")
	}

	gobot.Assert(t, len(d.Halt()), 0)

	gobot.On(d.Event(Hello), func(data interface{}) {
		sem <- true
	})

	select {
	case <-sem:
		t.Errorf("Hello Event should not publish after Halt")
	case <-time.After(600 * time.Millisecond):
	}
}

`
}

func adaptorTest() string {
	return `package {{.Name}}

import (
	"testing"

	"github.com/hybridgroup/gobot"
)

func Test{{.UpperName}}Adaptor(t *testing.T) {
	a := New{{.UpperName}}Adaptor("tester")

	gobot.Assert(t, a.Name(), "tester")

	gobot.Assert(t, len(a.Connect()), 0)

	gobot.Assert(t, a.Ping(), "pong")

	gobot.Assert(t, len(a.Connect()), 0)

	gobot.Assert(t, len(a.Finalize()), 0)
}
`
}

func readme() string {
	return `# {{.Name}}

Gobot (http://gobot.io/) is a framework for robotics and physical computing using Go

This repository contains the Gobot adaptor and driver for {{.Name}}.

For more information about Gobot, check out the github repo at
https://github.com/hybridgroup/gobot

## Installing
` + "```bash\ngo get path/to/repo/{{.Name}}\n```" + `

## Using
` + "```go{{.Example}}\n```" + `

## Connecting

Explain how to connect to the device here...

## License

Copyright (c) 2014 Your Name Here. See LICENSE for more details
`
}
<commit_msg>Add driver, adaptor and project subcommands to generator<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"

	"github.com/codegangsta/cli"
)

type config struct {
	Name        string
	UpperName   string
	FirstLetter string
	Example     string
	dir         string
}

func Generate() cli.Command {
	return cli.Command{
		Name:  "generate",
		Usage: "Generate new Gobot adaptors, drivers, and projects",
		Action: func(c *cli.Context) {
			valid := false
			for _, s := range []string{"adaptor", "driver", "project"} {
				if s == c.Args().First() {
					valid = true
				}
			}
			if valid == false {
				fmt.Println("Invalid/no subcommand supplied.\n")
				fmt.Println("Usage:")
				fmt.Println(" gobot generate adaptor [package name] # generate a new Gobot adaptor")
				fmt.Println(" gobot generate driver  [package name] # generate a new Gobot driver")
				fmt.Println(" gobot generate project [package name] # generate a new Gobot project")
				return
			}

			if len(c.Args()) != 2 {
				fmt.Println("Please provide a one word name.")
				return
			}

			name := strings.ToLower(c.Args()[1])
			upperName := strings.ToUpper(string(name[0])) + string(name[1:])

			cfg := config{
				UpperName:   upperName,
				Name:        name,
				FirstLetter: string(name[0]),
				dir:         ".",
			}

			switch c.Args().First() {
			case "adaptor":
				if err := generateAdaptor(cfg); err != nil {
					fmt.Println(err)
				}
			case "driver":
				if err := generateDriver(cfg); err != nil {
					fmt.Println(err)
				}
			case "project":
				pwd, err := os.Getwd()
				if err != nil {
					fmt.Println(err)
					return
				}
				dir := pwd + "/gobot-" + cfg.Name
				fmt.Println("Creating", dir)
				if err := os.MkdirAll(dir, 0700); err != nil {
					fmt.Println(err)
					return
				}
				cfg.dir = dir

				examplesDir := dir + "/examples"
				fmt.Println("Creating", examplesDir)
				if err := os.MkdirAll(examplesDir, 0700); err != nil {
					fmt.Println(err)
					return
				}

				if err := generateProject(cfg); err != nil {
					fmt.Println(err)
				}
			}
		},
	}
}

func generate(c config, file string, tmpl string) error {
	fileLocation := c.dir + "/" + file
	fmt.Println("Creating", fileLocation)

	f, err := os.Create(fileLocation)
	defer f.Close()
	if err != nil {
		return err
	}

	t, err := template.New("").Parse(tmpl)
	if err != nil {
		return err
	}

	return t.Execute(f, c)
}

func generateDriver(c config) error {
	if err := generate(c, c.Name+"_driver.go", driver()); err != nil {
		return err
	}

	return generate(c, c.Name+"_dirver_test.go", driverTest())
}

func generateAdaptor(c config) error {
	if err := generate(c, c.Name+"_adaptor.go", adaptor()); err != nil {
		return err
	}

	return generate(c, c.Name+"_adaptor_test.go", adaptorTest())
}

func generateProject(c config) error {
	if err := generateDriver(c); err != nil {
		return err
	}
	if err := generateAdaptor(c); err != nil {
		return err
	}

	dir := c.dir
	exampleDir := dir + "/examples"
	c.dir = exampleDir

	generate(c, "main.go", example())

	c.dir = dir

	if ex, err := ioutil.ReadFile(exampleDir + "/main.go"); err != nil {
		return err
	} else {
		c.Example = string(ex)
	}

	return generate(c, "README.md", readme())
}

func adaptor() string {
	return `package {{.Name}}

import (
	"github.com/hybridgroup/gobot"
)

var _ gobot.Adaptor = (*{{.UpperName}}Adaptor)(nil)

type {{.UpperName}}Adaptor struct {
	name string
}

func New{{.UpperName}}Adaptor(name string) *{{.UpperName}}Adaptor {
	return &{{.UpperName}}Adaptor{
		name: name,
	}
}

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Name() string { return {{.FirstLetter}}.name }

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Connect() []error { return nil }

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Finalize() []error { return nil }

func ({{.FirstLetter}} *{{.UpperName}}Adaptor) Ping() string { return "pong" }
`
}

func driver() string {
	return `package {{.Name }}

import (
	"time"

	"github.com/hybridgroup/gobot"
)

var _ gobot.Driver = (*{{.UpperName}}Driver)(nil)

const Hello string = "hello"

type {{.UpperName}}Driver struct {
	name string
	connection gobot.Connection
	interval time.Duration
	halt chan bool
	gobot.Eventer
	gobot.Commander
}

func New{{.UpperName}}Driver(a *{{.UpperName}}Adaptor, name string) *{{.UpperName}}Driver {
	{{.FirstLetter}} := &{{.UpperName}}Driver{
		name: name,
		connection: a,
		interval: 500*time.Millisecond,
		halt: make(chan bool, 0),
    Eventer:    gobot.NewEventer(),
    Commander:  gobot.NewCommander(),
	}

	{{.FirstLetter}}.AddEvent(Hello)

	{{.FirstLetter}}.AddCommand(Hello, func(params map[string]interface{}) interface{} {
		return {{.FirstLetter}}.Hello()
	})

	return {{.FirstLetter}}
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Name() string { return {{.FirstLetter}}.name }

func ({{.FirstLetter}} *{{.UpperName}}Driver) Connection() gobot.Connection {
	return {{.FirstLetter}}.connection
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) adaptor() *{{.UpperName}}Adaptor {
	return {{.FirstLetter}}.Connection().(*{{.UpperName}}Adaptor)
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Hello() string {
	return "hello from " + {{.FirstLetter}}.Name() + "!"
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Ping() string {
	return {{.FirstLetter}}.adaptor().Ping()
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Start() []error {
	go func() {
		for {
			gobot.Publish({{.FirstLetter}}.Event(Hello), {{.FirstLetter}}.Hello())

			select {
			case <- time.After({{.FirstLetter}}.interval):
			case <- {{.FirstLetter}}.halt:
				return
			}
		}
	}()
	return nil
}

func ({{.FirstLetter}} *{{.UpperName}}Driver) Halt() []error {
	{{.FirstLetter}}.halt <- true
	return nil
}

`
}

func example() string {
	return `
package main

import (
  "../"
  "fmt"
  "time"

  "github.com/hybridgroup/gobot"
)

func main() {
  gbot := gobot.NewGobot()

  conn := {{.Name}}.New{{.UpperName}}Adaptor("conn")
  dev := {{.Name}}.New{{.UpperName}}Driver(conn, "dev")

  work := func() {
    gobot.On(dev.Event({{.Name}}.Hello), func(data interface{}) {
      fmt.Println(data)
    })

    gobot.Every(1200*time.Millisecond, func() {
      fmt.Println(dev.Ping())
    })
  }

  robot := gobot.NewRobot(
    "robot",
    []gobot.Connection{conn},
    []gobot.Device{dev},
    work,
  )

  gbot.AddRobot(robot)
  gbot.Start()
}
`
}

func driverTest() string {
	return `package {{.Name}}

import (
	"testing"
	"time"

	"github.com/hybridgroup/gobot"
)

func Test{{.UpperName}}Driver(t *testing.T) {
	d := New{{.UpperName}}Driver(New{{.UpperName}}Adaptor("conn"), "dev")

	gobot.Assert(t, d.Name(), "dev")
	gobot.Assert(t, d.Connection().Name(), "conn")

	ret := d.Command(Hello)(nil)
	gobot.Assert(t, ret.(string), "hello from dev!")

	gobot.Assert(t, d.Ping(), "pong")

	gobot.Assert(t, len(d.Start()), 0)

	<-time.After(d.interval)

	sem := make(chan bool, 0)

	gobot.On(d.Event(Hello), func(data interface{}) {
		sem <- true
	})

	select {
	case <-sem:
	case <-time.After(600 * time.Millisecond):
		t.Errorf("Hello Event was not published")
	}

	gobot.Assert(t, len(d.Halt()), 0)

	gobot.On(d.Event(Hello), func(data interface{}) {
		sem <- true
	})

	select {
	case <-sem:
		t.Errorf("Hello Event should not publish after Halt")
	case <-time.After(600 * time.Millisecond):
	}
}

`
}

func adaptorTest() string {
	return `package {{.Name}}

import (
	"testing"

	"github.com/hybridgroup/gobot"
)

func Test{{.UpperName}}Adaptor(t *testing.T) {
	a := New{{.UpperName}}Adaptor("tester")

	gobot.Assert(t, a.Name(), "tester")

	gobot.Assert(t, len(a.Connect()), 0)

	gobot.Assert(t, a.Ping(), "pong")

	gobot.Assert(t, len(a.Connect()), 0)

	gobot.Assert(t, len(a.Finalize()), 0)
}
`
}

func readme() string {
	return `# {{.Name}}

Gobot (http://gobot.io/) is a framework for robotics and physical computing using Go

This repository contains the Gobot adaptor and driver for {{.Name}}.

For more information about Gobot, check out the github repo at
https://github.com/hybridgroup/gobot

## Installing
` + "```bash\ngo get path/to/repo/{{.Name}}\n```" + `

## Using
` + "```go{{.Example}}\n```" + `

## Connecting

Explain how to connect to the device here...

## License

Copyright (c) 2015 <Your Name Here>. Licensed under the <Insert license here> license.
`
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	slib "servicelib"
)

func getSysGroup(op opContext, sgid string) (slib.SystemGroup, error) {
	var sg slib.SystemGroup

	rows, err := op.Query(`SELECT sysgroupid, name, environment
		FROM sysgroup WHERE sysgroupid = $1`, sgid)
	if err != nil {
		return sg, err
	}
	if !rows.Next() {
		return sg, nil
	}
	err = rows.Scan(&sg.ID, &sg.Name, &sg.Environment)
	if err != nil {
		return sg, err
	}
	err = rows.Close()
	if err != nil {
		return sg, err
	}

	return sg, nil
}

func hostDynSysgroup(op opContext, hn string) (int, error) {
	rows, err := op.Query(`SELECT m.sysgroupid FROM
		hostmatch as m WHERE
		$1 ~* m.expression`, hn)
	if err != nil {
		return 0, err
	}
	defer rows.Close()
	if rows.Next() {
		var sgid sql.NullInt64
		err = rows.Scan(&sgid)
		if err != nil {
			return 0, err
		}
		if sgid.Valid {
			return int(sgid.Int64), nil
		}
	}
	return 0, nil
}

func sysGroupAddMeta(op opContext, s *slib.SystemGroup) error {
	s.Host = make([]slib.Host, 0)
	s.HostMatch = make([]slib.HostMatch, 0)

	// Grab any hosts that have been statically mapped to this group.
	rows, err := op.Query(`SELECT hostid, hostname, comment FROM
		host WHERE sysgroupid = $1`, s.ID)
	if err != nil {
		return err
	}
	for rows.Next() {
		var h slib.Host
		err = rows.Scan(&h.ID, &h.Hostname, &h.Comment)
		if err != nil {
			return err
		}
		err = hostAddComp(op, &h)
		if err != nil {
			return err
		}
		s.Host = append(s.Host, h)
	}

	// Include dynamic entries here based on matching with expressions
	// in the hostmatch table. Note we don't do this for normal static
	// hosts as it is assumed these would be linked manually when they
	// are added.
	rows, err = op.Query(`SELECT h.hostid, h.hostname,
		h.comment, h.lastused FROM
		host as h, hostmatch as m WHERE
		h.hostname ~* m.expression AND
		h.dynamic = true AND
		m.sysgroupid = $1`, s.ID)
	if err != nil {
		return err
	}
	for rows.Next() {
		var h slib.Host
		err = rows.Scan(&h.ID, &h.Hostname, &h.Comment, &h.LastUsed)
		if err != nil {
			rows.Close()
			return err
		}
		h.Dynamic = true
		err = hostAddComp(op, &h)
		if err != nil {
			rows.Close()
			return err
		}
		s.Host = append(s.Host, h)
	}

	// Grab any expressions for dynamic host mapping.
	rows, err = op.Query(`SELECT hostmatchid, expression, comment FROM
		hostmatch WHERE sysgroupid = $1`, s.ID)
	if err != nil {
		return err
	}
	for rows.Next() {
		var h slib.HostMatch
		err = rows.Scan(&h.ID, &h.Expression, &h.Comment)
		if err != nil {
			rows.Close()
			return err
		}
		s.HostMatch = append(s.HostMatch, h)
	}

	return nil
}

func serviceGetSysGroup(rw http.ResponseWriter, req *http.Request) {
	req.ParseForm()

	sgid := req.FormValue("id")
	if sgid == "" {
		http.Error(rw, "must specify a valid system group id", 500)
		return
	}

	op := opContext{}
	op.newContext(dbconn, false, req.RemoteAddr)

	sg, err := getSysGroup(op, sgid)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	if sg.Name == "" {
		http.NotFound(rw, req)
		return
	}
	err = sysGroupAddMeta(op, &sg)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}

	buf, err := json.Marshal(&sg)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	fmt.Fprintf(rw, string(buf))
}

func serviceSysGroups(rw http.ResponseWriter, req *http.Request) {
	op := opContext{}
	op.newContext(dbconn, false, req.RemoteAddr)

	rows, err := op.db.Query(`SELECT sysgroupid FROM sysgroup`)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	sgr := slib.SystemGroupsResponse{}
	sgr.Results = make([]slib.SystemGroup, 0)
	for rows.Next() {
		var sgid string
		err = rows.Scan(&sgid)
		if err != nil {
			op.logf(err.Error())
			http.Error(rw, err.Error(), 500)
			return
		}
		sg, err := getSysGroup(op, sgid)
		if err != nil {
			op.logf(err.Error())
			http.Error(rw, err.Error(), 500)
			return
		}
		if sg.Name == "" {
			continue
		}
		sgr.Results = append(sgr.Results, sg)
	}

	buf, err := json.Marshal(&sgr)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	fmt.Fprint(rw, string(buf))
}
<commit_msg>correctly set lastused timestamp for directly linked hosts<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	slib "servicelib"
)

func getSysGroup(op opContext, sgid string) (slib.SystemGroup, error) {
	var sg slib.SystemGroup

	rows, err := op.Query(`SELECT sysgroupid, name, environment
		FROM sysgroup WHERE sysgroupid = $1`, sgid)
	if err != nil {
		return sg, err
	}
	if !rows.Next() {
		return sg, nil
	}
	err = rows.Scan(&sg.ID, &sg.Name, &sg.Environment)
	if err != nil {
		return sg, err
	}
	err = rows.Close()
	if err != nil {
		return sg, err
	}

	return sg, nil
}

func hostDynSysgroup(op opContext, hn string) (int, error) {
	rows, err := op.Query(`SELECT m.sysgroupid FROM
		hostmatch as m WHERE
		$1 ~* m.expression`, hn)
	if err != nil {
		return 0, err
	}
	defer rows.Close()
	if rows.Next() {
		var sgid sql.NullInt64
		err = rows.Scan(&sgid)
		if err != nil {
			return 0, err
		}
		if sgid.Valid {
			return int(sgid.Int64), nil
		}
	}
	return 0, nil
}

func sysGroupAddMeta(op opContext, s *slib.SystemGroup) error {
	s.Host = make([]slib.Host, 0)
	s.HostMatch = make([]slib.HostMatch, 0)

	// Grab any hosts that have been statically mapped to this group.
	rows, err := op.Query(`SELECT hostid, hostname, comment, lastused
		FROM host WHERE sysgroupid = $1`, s.ID)
	if err != nil {
		return err
	}
	for rows.Next() {
		var h slib.Host
		err = rows.Scan(&h.ID, &h.Hostname, &h.Comment, &h.LastUsed)
		if err != nil {
			return err
		}
		err = hostAddComp(op, &h)
		if err != nil {
			return err
		}
		s.Host = append(s.Host, h)
	}

	// Include dynamic entries here based on matching with expressions
	// in the hostmatch table. Note we don't do this for normal static
	// hosts as it is assumed these would be linked manually when they
	// are added.
	rows, err = op.Query(`SELECT h.hostid, h.hostname,
		h.comment, h.lastused FROM
		host as h, hostmatch as m WHERE
		h.hostname ~* m.expression AND
		h.dynamic = true AND
		m.sysgroupid = $1`, s.ID)
	if err != nil {
		return err
	}
	for rows.Next() {
		var h slib.Host
		err = rows.Scan(&h.ID, &h.Hostname, &h.Comment, &h.LastUsed)
		if err != nil {
			rows.Close()
			return err
		}
		h.Dynamic = true
		err = hostAddComp(op, &h)
		if err != nil {
			rows.Close()
			return err
		}
		s.Host = append(s.Host, h)
	}

	// Grab any expressions for dynamic host mapping.
	rows, err = op.Query(`SELECT hostmatchid, expression, comment FROM
		hostmatch WHERE sysgroupid = $1`, s.ID)
	if err != nil {
		return err
	}
	for rows.Next() {
		var h slib.HostMatch
		err = rows.Scan(&h.ID, &h.Expression, &h.Comment)
		if err != nil {
			rows.Close()
			return err
		}
		s.HostMatch = append(s.HostMatch, h)
	}

	return nil
}

func serviceGetSysGroup(rw http.ResponseWriter, req *http.Request) {
	req.ParseForm()

	sgid := req.FormValue("id")
	if sgid == "" {
		http.Error(rw, "must specify a valid system group id", 500)
		return
	}

	op := opContext{}
	op.newContext(dbconn, false, req.RemoteAddr)

	sg, err := getSysGroup(op, sgid)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	if sg.Name == "" {
		http.NotFound(rw, req)
		return
	}
	err = sysGroupAddMeta(op, &sg)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}

	buf, err := json.Marshal(&sg)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	fmt.Fprintf(rw, string(buf))
}

func serviceSysGroups(rw http.ResponseWriter, req *http.Request) {
	op := opContext{}
	op.newContext(dbconn, false, req.RemoteAddr)

	rows, err := op.db.Query(`SELECT sysgroupid FROM sysgroup`)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	sgr := slib.SystemGroupsResponse{}
	sgr.Results = make([]slib.SystemGroup, 0)
	for rows.Next() {
		var sgid string
		err = rows.Scan(&sgid)
		if err != nil {
			op.logf(err.Error())
			http.Error(rw, err.Error(), 500)
			return
		}
		sg, err := getSysGroup(op, sgid)
		if err != nil {
			op.logf(err.Error())
			http.Error(rw, err.Error(), 500)
			return
		}
		if sg.Name == "" {
			continue
		}
		sgr.Results = append(sgr.Results, sg)
	}

	buf, err := json.Marshal(&sgr)
	if err != nil {
		op.logf(err.Error())
		http.Error(rw, err.Error(), 500)
		return
	}
	fmt.Fprint(rw, string(buf))
}
<|endoftext|>


<commit_before>package tchannel

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"golang.org/x/net/context"
)

var (
	errAlreadyListening = errors.New("channel already listening")
)

const (
	ephemeralHostPort = "0.0.0.0:0"
)

// ChannelOptions are used to control parameters on a create a TChannel
type ChannelOptions struct {
	// Default Connection options
	DefaultConnectionOptions ConnectionOptions

	// The name of the process, for logging and reporting to peers
	ProcessName string

	// The logger to use for this channel
	Logger Logger

	// The reporter to use for reporting stats for this channel.
	StatsReporter StatsReporter

	// Trace reporter to use for this channel.
	TraceReporter TraceReporter
}

// ChannelState is the state of a channel.
type ChannelState int

const (
	// ChannelClient is a channel that can be used as a client.
	ChannelClient ChannelState = iota + 1

	// ChannelListening is a channel that is listening for new connnections.
	ChannelListening

	// ChannelStartClose is a channel that has received a Close request.
	// The channel is no longer listening, and all new incoming connections are rejected.
	ChannelStartClose

	// ChannelInboundClosed is a channel that has drained all incoming connections, but may
	// have outgoing connections. All incoming calls and new outgoing calls are rejected.
	ChannelInboundClosed

	// ChannelClosed is a channel that has closed completely.
	ChannelClosed
)

//go:generate stringer -type=ChannelState

// A Channel is a bi-directional connection to the peering and routing network.
// Applications can use a Channel to make service calls to remote peers via
// BeginCall, or to listen for incoming calls from peers.  Applications that
// want to receive requests should call one of Serve or ListenAndServe
// TODO(prashant): Shutdown all subchannels + peers when channel is closed.
type Channel struct {
	log               Logger
	commonStatsTags   map[string]string
	statsReporter     StatsReporter
	traceReporter     TraceReporter
	connectionOptions ConnectionOptions
	handlers          *handlerMap
	peers             *PeerList

	// mutable contains all the members of Channel which are mutable.
	mutable struct {
		mut         sync.RWMutex // protects members of the mutable struct.
		closed      bool
		peerInfo    LocalPeerInfo // May be ephemeral if this is a client only channel
		l           net.Listener  // May be nil if this is a client only channel
		subChannels map[string]*SubChannel
		conns       []*Connection
	}
}

// NewChannel creates a new Channel.  The new channel can be used to send outbound requests
// to peers, but will not listen or handling incoming requests until one of ListenAndServe
// or Serve is called. The local service name should be passed to serviceName.
func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) {
	if opts == nil {
		opts = &ChannelOptions{}
	}

	logger := opts.Logger
	if logger == nil {
		logger = NullLogger
	}

	traceReporter := opts.TraceReporter
	if traceReporter == nil {
		traceReporter = NullReporter
	}

	processName := opts.ProcessName
	if processName == "" {
		processName = fmt.Sprintf("%s[%d]", filepath.Base(os.Args[0]), os.Getpid())
	}

	statsReporter := opts.StatsReporter
	if statsReporter == nil {
		statsReporter = NullStatsReporter
	}

	ch := &Channel{
		connectionOptions: opts.DefaultConnectionOptions,
		log:               logger,
		statsReporter:     statsReporter,
		traceReporter:     traceReporter,
		handlers:          &handlerMap{},
	}
	ch.mutable.peerInfo = LocalPeerInfo{
		PeerInfo: PeerInfo{
			ProcessName: processName,
			HostPort:    ephemeralHostPort,
		},
		ServiceName: serviceName,
	}
	ch.mutable.subChannels = make(map[string]*SubChannel)
	ch.peers = newPeerList(ch)
	ch.createCommonStats()
	return ch, nil
}

// Serve serves incoming requests using the provided listener.
// The local peer info is set synchronously, but the actual socket listening is done in
// a separate goroutine.
func (ch *Channel) Serve(l net.Listener) error {
	mutable := &ch.mutable
	mutable.mut.Lock()
	defer mutable.mut.Unlock()

	if mutable.l != nil {
		return errAlreadyListening
	}

	mutable.l = l
	mutable.peerInfo.HostPort = l.Addr().String()

	peerInfo := mutable.peerInfo
	ch.log.Debugf("%v (%v) listening on %v", peerInfo.ProcessName, peerInfo.ServiceName, peerInfo.HostPort)
	go ch.serve()
	return nil
}

// ListenAndServe listens on the given address and serves incoming requests.
// The port may be 0, in which case the channel will use an OS assigned port
// This method does not block as the handling of connections is done in a goroutine.
func (ch *Channel) ListenAndServe(hostPort string) error {
	mutable := &ch.mutable
	mutable.mut.RLock()

	if mutable.l != nil {
		mutable.mut.RUnlock()
		return errAlreadyListening
	}

	l, err := net.Listen("tcp", hostPort)
	if err != nil {
		mutable.mut.RUnlock()
		return err
	}

	mutable.mut.RUnlock()
	return ch.Serve(l)
}

// Register registers a handler for a service+operation pair
func (ch *Channel) Register(h Handler, operationName string) {
	ch.handlers.register(h, ch.PeerInfo().ServiceName, operationName)
}

// PeerInfo returns the current peer info for the channel
func (ch *Channel) PeerInfo() LocalPeerInfo {
	ch.mutable.mut.RLock()
	defer ch.mutable.mut.RUnlock()

	return ch.mutable.peerInfo
}

func (ch *Channel) createCommonStats() {
	ch.commonStatsTags = map[string]string{
		"app":     ch.mutable.peerInfo.ProcessName,
		"service": ch.mutable.peerInfo.ServiceName,
	}
	host, err := os.Hostname()
	if err != nil {
		ch.log.Infof("channel failed to get host: %v", err)
		return
	}
	ch.commonStatsTags["host"] = host
	// TODO(prashant): Allow user to pass extra tags (such as cluster, version).
}

func (ch *Channel) registerNewSubChannel(serviceName string) *SubChannel {
	mutable := &ch.mutable
	mutable.mut.Lock()
	defer mutable.mut.Unlock()

	// Recheck for the subchannel under the write lock.
	if sc, ok := mutable.subChannels[serviceName]; ok {
		return sc
	}

	sc := newSubChannel(serviceName, ch.peers)
	mutable.subChannels[serviceName] = sc
	return sc
}

// GetSubChannel returns a SubChannel for the given service name. If the subchannel does not
// exist, it is created.
func (ch *Channel) GetSubChannel(serviceName string) *SubChannel {
	mutable := &ch.mutable
	mutable.mut.RLock()

	if sc, ok := mutable.subChannels[serviceName]; ok {
		mutable.mut.RUnlock()
		return sc
	}

	mutable.mut.RUnlock()
	return ch.registerNewSubChannel(serviceName)
}

// Peers returns the PeerList for the channel.
func (ch *Channel) Peers() *PeerList {
	return ch.peers
}

// BeginCall starts a new call to a remote peer, returning an OutboundCall that can
// be used to write the arguments of the call.
func (ch *Channel) BeginCall(ctx context.Context, hostPort, serviceName, operationName string, callOptions *CallOptions) (*OutboundCall, error) {
	p := ch.peers.GetOrAdd(hostPort)
	return p.BeginCall(ctx, serviceName, operationName, callOptions)
}

// serve runs the listener to accept and manage new incoming connections, blocking
// until the channel is closed.
func (ch *Channel) serve() {
	acceptBackoff := 0 * time.Millisecond

	for {
		netConn, err := ch.mutable.l.Accept()
		if err != nil {
			// Backoff from new accepts if this is a temporary error
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if acceptBackoff == 0 {
					acceptBackoff = 5 * time.Millisecond
				} else {
					acceptBackoff *= 2
				}
				if max := 1 * time.Second; acceptBackoff > max {
					acceptBackoff = max
				}
				ch.log.Warnf("accept error: %v; retrying in %v", err, acceptBackoff)
				time.Sleep(acceptBackoff)
				continue
			} else {
				// Only log an error if we are not shutdown.
				if ch.Closed() {
					return
				}
				ch.log.Fatalf("unrecoverable accept error: %v; closing server", err)
				return
			}
		}

		acceptBackoff = 0

		// Register the connection in the peer once the channel is set up.
		onActive := func(c *Connection) {
			ch.log.Debugf("Add connection as an active peer for %v", c.remotePeerInfo.HostPort)
			p := ch.peers.GetOrAdd(c.remotePeerInfo.HostPort)
			p.AddConnection(c)
		}
		_, err = ch.newInboundConnection(netConn, onActive, &ch.connectionOptions)
		if err != nil {
			// Server is getting overloaded - begin rejecting new connections
			ch.log.Errorf("could not create new TChannelConnection for incoming conn: %v", err)
			netConn.Close()
			continue
		}
	}
}

// Ping sends a ping message to the given hostPort and waits for a response.
func (ch *Channel) Ping(ctx context.Context, hostPort string) error {
	peer := ch.Peers().GetOrAdd(hostPort)
	conn, err := peer.GetConnection(ctx)
	if err != nil {
		return err
	}

	return conn.ping(ctx)
}

// Logger returns the logger for this channel.
func (ch *Channel) Logger() Logger {
	return ch.log
}

// Connect connects the channel.
func (ch *Channel) Connect(ctx context.Context, hostPort string, connectionOptions *ConnectionOptions) (*Connection, error) {
	c, err := ch.newOutboundConnection(hostPort, connectionOptions)
	if err != nil {
		return nil, err
	}

	if err := c.sendInit(ctx); err != nil {
		return nil, err
	}

	ch.mutable.mut.Lock()
	ch.mutable.conns = append(ch.mutable.conns, c)
	ch.mutable.mut.Unlock()

	return c, err
}

// Closed returns whether this channel has been closed with .Close()
func (ch *Channel) Closed() bool {
	ch.mutable.mut.RLock()
	defer ch.mutable.mut.RUnlock()
	return ch.mutable.closed
}

// Close closes the channel including all connections to any active peers.
func (ch *Channel) Close() {
	ch.mutable.mut.Lock()
	defer ch.mutable.mut.Unlock()

	ch.mutable.closed = true
	if ch.mutable.l != nil {
		ch.mutable.l.Close()
	}
	ch.peers.Close()
}
<commit_msg>Add incoming connections to channel's connections<commit_after>package tchannel

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"golang.org/x/net/context"
)

var (
	errAlreadyListening = errors.New("channel already listening")
)

const (
	ephemeralHostPort = "0.0.0.0:0"
)

// ChannelOptions are used to control parameters on a create a TChannel
type ChannelOptions struct {
	// Default Connection options
	DefaultConnectionOptions ConnectionOptions

	// The name of the process, for logging and reporting to peers
	ProcessName string

	// The logger to use for this channel
	Logger Logger

	// The reporter to use for reporting stats for this channel.
	StatsReporter StatsReporter

	// Trace reporter to use for this channel.
	TraceReporter TraceReporter
}

// ChannelState is the state of a channel.
type ChannelState int

const (
	// ChannelClient is a channel that can be used as a client.
	ChannelClient ChannelState = iota + 1

	// ChannelListening is a channel that is listening for new connnections.
	ChannelListening

	// ChannelStartClose is a channel that has received a Close request.
	// The channel is no longer listening, and all new incoming connections are rejected.
	ChannelStartClose

	// ChannelInboundClosed is a channel that has drained all incoming connections, but may
	// have outgoing connections. All incoming calls and new outgoing calls are rejected.
	ChannelInboundClosed

	// ChannelClosed is a channel that has closed completely.
	ChannelClosed
)

//go:generate stringer -type=ChannelState

// A Channel is a bi-directional connection to the peering and routing network.
// Applications can use a Channel to make service calls to remote peers via
// BeginCall, or to listen for incoming calls from peers.  Applications that
// want to receive requests should call one of Serve or ListenAndServe
// TODO(prashant): Shutdown all subchannels + peers when channel is closed.
type Channel struct {
	log               Logger
	commonStatsTags   map[string]string
	statsReporter     StatsReporter
	traceReporter     TraceReporter
	connectionOptions ConnectionOptions
	handlers          *handlerMap
	peers             *PeerList

	// mutable contains all the members of Channel which are mutable.
	mutable struct {
		mut         sync.RWMutex // protects members of the mutable struct.
		closed      bool
		peerInfo    LocalPeerInfo // May be ephemeral if this is a client only channel
		l           net.Listener  // May be nil if this is a client only channel
		subChannels map[string]*SubChannel
		conns       []*Connection
	}
}

// NewChannel creates a new Channel.  The new channel can be used to send outbound requests
// to peers, but will not listen or handling incoming requests until one of ListenAndServe
// or Serve is called. The local service name should be passed to serviceName.
func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) {
	if opts == nil {
		opts = &ChannelOptions{}
	}

	logger := opts.Logger
	if logger == nil {
		logger = NullLogger
	}

	traceReporter := opts.TraceReporter
	if traceReporter == nil {
		traceReporter = NullReporter
	}

	processName := opts.ProcessName
	if processName == "" {
		processName = fmt.Sprintf("%s[%d]", filepath.Base(os.Args[0]), os.Getpid())
	}

	statsReporter := opts.StatsReporter
	if statsReporter == nil {
		statsReporter = NullStatsReporter
	}

	ch := &Channel{
		connectionOptions: opts.DefaultConnectionOptions,
		log:               logger,
		statsReporter:     statsReporter,
		traceReporter:     traceReporter,
		handlers:          &handlerMap{},
	}
	ch.mutable.peerInfo = LocalPeerInfo{
		PeerInfo: PeerInfo{
			ProcessName: processName,
			HostPort:    ephemeralHostPort,
		},
		ServiceName: serviceName,
	}
	ch.mutable.subChannels = make(map[string]*SubChannel)
	ch.peers = newPeerList(ch)
	ch.createCommonStats()
	return ch, nil
}

// Serve serves incoming requests using the provided listener.
// The local peer info is set synchronously, but the actual socket listening is done in
// a separate goroutine.
func (ch *Channel) Serve(l net.Listener) error {
	mutable := &ch.mutable
	mutable.mut.Lock()
	defer mutable.mut.Unlock()

	if mutable.l != nil {
		return errAlreadyListening
	}

	mutable.l = l
	mutable.peerInfo.HostPort = l.Addr().String()

	peerInfo := mutable.peerInfo
	ch.log.Debugf("%v (%v) listening on %v", peerInfo.ProcessName, peerInfo.ServiceName, peerInfo.HostPort)
	go ch.serve()
	return nil
}

// ListenAndServe listens on the given address and serves incoming requests.
// The port may be 0, in which case the channel will use an OS assigned port
// This method does not block as the handling of connections is done in a goroutine.
func (ch *Channel) ListenAndServe(hostPort string) error {
	mutable := &ch.mutable
	mutable.mut.RLock()

	if mutable.l != nil {
		mutable.mut.RUnlock()
		return errAlreadyListening
	}

	l, err := net.Listen("tcp", hostPort)
	if err != nil {
		mutable.mut.RUnlock()
		return err
	}

	mutable.mut.RUnlock()
	return ch.Serve(l)
}

// Register registers a handler for a service+operation pair
func (ch *Channel) Register(h Handler, operationName string) {
	ch.handlers.register(h, ch.PeerInfo().ServiceName, operationName)
}

// PeerInfo returns the current peer info for the channel
func (ch *Channel) PeerInfo() LocalPeerInfo {
	ch.mutable.mut.RLock()
	defer ch.mutable.mut.RUnlock()

	return ch.mutable.peerInfo
}

func (ch *Channel) createCommonStats() {
	ch.commonStatsTags = map[string]string{
		"app":     ch.mutable.peerInfo.ProcessName,
		"service": ch.mutable.peerInfo.ServiceName,
	}
	host, err := os.Hostname()
	if err != nil {
		ch.log.Infof("channel failed to get host: %v", err)
		return
	}
	ch.commonStatsTags["host"] = host
	// TODO(prashant): Allow user to pass extra tags (such as cluster, version).
}

func (ch *Channel) registerNewSubChannel(serviceName string) *SubChannel {
	mutable := &ch.mutable
	mutable.mut.Lock()
	defer mutable.mut.Unlock()

	// Recheck for the subchannel under the write lock.
	if sc, ok := mutable.subChannels[serviceName]; ok {
		return sc
	}

	sc := newSubChannel(serviceName, ch.peers)
	mutable.subChannels[serviceName] = sc
	return sc
}

// GetSubChannel returns a SubChannel for the given service name. If the subchannel does not
// exist, it is created.
func (ch *Channel) GetSubChannel(serviceName string) *SubChannel {
	mutable := &ch.mutable
	mutable.mut.RLock()

	if sc, ok := mutable.subChannels[serviceName]; ok {
		mutable.mut.RUnlock()
		return sc
	}

	mutable.mut.RUnlock()
	return ch.registerNewSubChannel(serviceName)
}

// Peers returns the PeerList for the channel.
func (ch *Channel) Peers() *PeerList {
	return ch.peers
}

// BeginCall starts a new call to a remote peer, returning an OutboundCall that can
// be used to write the arguments of the call.
func (ch *Channel) BeginCall(ctx context.Context, hostPort, serviceName, operationName string, callOptions *CallOptions) (*OutboundCall, error) {
	p := ch.peers.GetOrAdd(hostPort)
	return p.BeginCall(ctx, serviceName, operationName, callOptions)
}

// serve runs the listener to accept and manage new incoming connections, blocking
// until the channel is closed.
func (ch *Channel) serve() {
	acceptBackoff := 0 * time.Millisecond

	for {
		netConn, err := ch.mutable.l.Accept()
		if err != nil {
			// Backoff from new accepts if this is a temporary error
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if acceptBackoff == 0 {
					acceptBackoff = 5 * time.Millisecond
				} else {
					acceptBackoff *= 2
				}
				if max := 1 * time.Second; acceptBackoff > max {
					acceptBackoff = max
				}
				ch.log.Warnf("accept error: %v; retrying in %v", err, acceptBackoff)
				time.Sleep(acceptBackoff)
				continue
			} else {
				// Only log an error if we are not shutdown.
				if ch.Closed() {
					return
				}
				ch.log.Fatalf("unrecoverable accept error: %v; closing server", err)
				return
			}
		}

		acceptBackoff = 0

		// Register the connection in the peer once the channel is set up.
		onActive := func(c *Connection) {
			ch.log.Debugf("Add connection as an active peer for %v", c.remotePeerInfo.HostPort)
			p := ch.peers.GetOrAdd(c.remotePeerInfo.HostPort)
			p.AddConnection(c)

			ch.mutable.mut.Lock()
			ch.mutable.conns = append(ch.mutable.conns, c)
			ch.mutable.mut.Unlock()
		}
		_, err = ch.newInboundConnection(netConn, onActive, &ch.connectionOptions)
		if err != nil {
			// Server is getting overloaded - begin rejecting new connections
			ch.log.Errorf("could not create new TChannelConnection for incoming conn: %v", err)
			netConn.Close()
			continue
		}
	}
}

// Ping sends a ping message to the given hostPort and waits for a response.
func (ch *Channel) Ping(ctx context.Context, hostPort string) error {
	peer := ch.Peers().GetOrAdd(hostPort)
	conn, err := peer.GetConnection(ctx)
	if err != nil {
		return err
	}

	return conn.ping(ctx)
}

// Logger returns the logger for this channel.
func (ch *Channel) Logger() Logger {
	return ch.log
}

// Connect connects the channel.
func (ch *Channel) Connect(ctx context.Context, hostPort string, connectionOptions *ConnectionOptions) (*Connection, error) {
	c, err := ch.newOutboundConnection(hostPort, connectionOptions)
	if err != nil {
		return nil, err
	}

	if err := c.sendInit(ctx); err != nil {
		return nil, err
	}

	ch.mutable.mut.Lock()
	ch.mutable.conns = append(ch.mutable.conns, c)
	ch.mutable.mut.Unlock()

	return c, err
}

// Closed returns whether this channel has been closed with .Close()
func (ch *Channel) Closed() bool {
	ch.mutable.mut.RLock()
	defer ch.mutable.mut.RUnlock()
	return ch.mutable.closed
}

// Close closes the channel including all connections to any active peers.
func (ch *Channel) Close() {
	ch.mutable.mut.Lock()
	defer ch.mutable.mut.Unlock()

	ch.mutable.closed = true
	if ch.mutable.l != nil {
		ch.mutable.l.Close()
	}
	ch.peers.Close()
}
<|endoftext|>


<commit_before>package out

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/smtp"
	"os"
	"path/filepath"
	"strings"
	"time"
)

//Execute - provides out capability
func Execute(sourceRoot, version string, input []byte) (string, error) {
	var buildTokens = map[string]string{
		"${BUILD_ID}":            os.Getenv("BUILD_ID"),
		"${BUILD_NAME}":          os.Getenv("BUILD_NAME"),
		"${BUILD_JOB_NAME}":      os.Getenv("BUILD_JOB_NAME"),
		"${BUILD_PIPELINE_NAME}": os.Getenv("BUILD_PIPELINE_NAME"),
		"${ATC_EXTERNAL_URL}":    os.Getenv("ATC_EXTERNAL_URL"),
		"${BUILD_TEAM_NAME}":     os.Getenv("BUILD_TEAM_NAME"),
	}

	if sourceRoot == "" {
		return "", errors.New("expected path to build sources as first argument")
	}

	var indata Input

	err := json.Unmarshal(input, &indata)
	if err != nil {
		return "", err
	}

	if indata.Source.SMTP.Host == "" {
		return "", errors.New(`missing required field "source.smtp.host"`)
	}

	if indata.Source.SMTP.Port == "" {
		return "", errors.New(`missing required field "source.smtp.port"`)
	}

	if indata.Source.From == "" {
		return "", errors.New(`missing required field "source.from"`)
	}

	if len(indata.Source.To) == 0 && len(indata.Params.To) == 0 {
		return "", errors.New(`missing required field "source.to" or "params.to". Must specify at least one`)
	}

	if indata.Params.Subject == "" {
		return "", errors.New(`missing required field "params.subject"`)
	}

	if indata.Source.SMTP.Anonymous == false {
		if indata.Source.SMTP.Username == "" {
			return "", errors.New(`missing required field "source.smtp.username" if anonymous specify anonymous: true`)
		}

		if indata.Source.SMTP.Password == "" {
			return "", errors.New(`missing required field "source.smtp.password" if anonymous specify anonymous: true`)
		}
	}

	replaceTokens := func(sourceString string) string {
		for k, v := range buildTokens {
			sourceString = strings.Replace(sourceString, k, v, -1)
		}
		return sourceString
	}

	readSource := func(sourcePath string) (string, error) {
		if !filepath.IsAbs(sourcePath) {
			sourcePath = filepath.Join(sourceRoot, sourcePath)
		}
		var bytes []byte
		bytes, err = ioutil.ReadFile(sourcePath)
		return replaceTokens(string(bytes)), err
	}

	subject, err := readSource(indata.Params.Subject)
	if err != nil {
		return "", err
	}
	subject = strings.Trim(subject, "\n")

	var headers string
	if indata.Params.Headers != "" {
		headers, err = readSource(indata.Params.Headers)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		headers = strings.Trim(headers, "\n")
	}

	var body string
	if indata.Params.Body != "" {
		body, err = readSource(indata.Params.Body)
		if err != nil {
			return "", err
		}
	}

	if indata.Params.To != "" {
		var toList string
		toList, err = readSource(indata.Params.To)
		if err != nil {
			return "", err
		}
		if len(toList) > 0 {
			toListArray := strings.Split(toList, ",")
			for _, toAddress := range toListArray {
				indata.Source.To = append(indata.Source.To, strings.TrimSpace(toAddress))
			}
		}
	}

	var outdata Output
	outdata.Version.Time = time.Now().UTC()
	outdata.Metadata = []MetadataItem{
		{Name: "smtp_host", Value: indata.Source.SMTP.Host},
		{Name: "subject", Value: subject},
		{Name: "version", Value: version},
	}
	outbytes, err := json.Marshal(outdata)
	if err != nil {
		return "", err
	}

	if indata.Params.SendEmptyBody == false && len(body) == 0 {
		fmt.Fprintf(os.Stderr, "Message not sent because the message body is empty and send_empty_body parameter was set to false. Github readme: https://github.com/pivotal-cf/email-resource")
		return string(outbytes), nil
	}
	var messageData []byte
	messageData = append(messageData, []byte("To: "+strings.Join(indata.Source.To, ", ")+"\n")...)
	if headers != "" {
		messageData = append(messageData, []byte(headers+"\n")...)
	}
	messageData = append(messageData, []byte("Subject: "+subject+"\n")...)

	messageData = append(messageData, []byte("\n")...)
	messageData = append(messageData, []byte(body)...)

	var c *smtp.Client
	var wc io.WriteCloser
	c, err = smtp.Dial(fmt.Sprintf("%s:%s", indata.Source.SMTP.Host, indata.Source.SMTP.Port))
	if err != nil {
		return "", err
	}
	defer c.Close()

	if !indata.Source.SMTP.Anonymous {
		auth := smtp.PlainAuth(
			"",
			indata.Source.SMTP.Username,
			indata.Source.SMTP.Password,
			indata.Source.SMTP.Host,
		)
		if err = c.Hello("localhost"); err != nil {
			return "", err
		}
		if ok, _ := c.Extension("STARTTLS"); ok {
			config := &tls.Config{
				ServerName:         indata.Source.SMTP.Host,
				InsecureSkipVerify: indata.Source.SMTP.SkipSSLValidation,
			}
			if err = c.StartTLS(config); err != nil {
				return "", err
			}
		}
		if auth != nil {
			if ok, _ := c.Extension("AUTH"); ok {
				if err = c.Auth(auth); err != nil {
					return "", err
				}
			}
		}
	}
	if err = c.Mail(indata.Source.From); err != nil {
		return "", err
	}
	for _, addr := range indata.Source.To {
		if err = c.Rcpt(addr); err != nil {
			return "", err
		}
	}
	wc, err = c.Data()
	if err != nil {
		return "", err
	}
	_, err = wc.Write(messageData)
	if err != nil {
		return "", err
	}
	err = wc.Close()
	if err != nil {
		return "", err
	}
	err = c.Quit()
	if err != nil {
		return "", err
	}

	return string(outbytes), err
}
<commit_msg>added back in from: to message payload<commit_after>package out

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/smtp"
	"os"
	"path/filepath"
	"strings"
	"time"
)

//Execute - provides out capability
func Execute(sourceRoot, version string, input []byte) (string, error) {
	var buildTokens = map[string]string{
		"${BUILD_ID}":            os.Getenv("BUILD_ID"),
		"${BUILD_NAME}":          os.Getenv("BUILD_NAME"),
		"${BUILD_JOB_NAME}":      os.Getenv("BUILD_JOB_NAME"),
		"${BUILD_PIPELINE_NAME}": os.Getenv("BUILD_PIPELINE_NAME"),
		"${ATC_EXTERNAL_URL}":    os.Getenv("ATC_EXTERNAL_URL"),
		"${BUILD_TEAM_NAME}":     os.Getenv("BUILD_TEAM_NAME"),
	}

	if sourceRoot == "" {
		return "", errors.New("expected path to build sources as first argument")
	}

	var indata Input

	err := json.Unmarshal(input, &indata)
	if err != nil {
		return "", err
	}

	if indata.Source.SMTP.Host == "" {
		return "", errors.New(`missing required field "source.smtp.host"`)
	}

	if indata.Source.SMTP.Port == "" {
		return "", errors.New(`missing required field "source.smtp.port"`)
	}

	if indata.Source.From == "" {
		return "", errors.New(`missing required field "source.from"`)
	}

	if len(indata.Source.To) == 0 && len(indata.Params.To) == 0 {
		return "", errors.New(`missing required field "source.to" or "params.to". Must specify at least one`)
	}

	if indata.Params.Subject == "" {
		return "", errors.New(`missing required field "params.subject"`)
	}

	if indata.Source.SMTP.Anonymous == false {
		if indata.Source.SMTP.Username == "" {
			return "", errors.New(`missing required field "source.smtp.username" if anonymous specify anonymous: true`)
		}

		if indata.Source.SMTP.Password == "" {
			return "", errors.New(`missing required field "source.smtp.password" if anonymous specify anonymous: true`)
		}
	}

	replaceTokens := func(sourceString string) string {
		for k, v := range buildTokens {
			sourceString = strings.Replace(sourceString, k, v, -1)
		}
		return sourceString
	}

	readSource := func(sourcePath string) (string, error) {
		if !filepath.IsAbs(sourcePath) {
			sourcePath = filepath.Join(sourceRoot, sourcePath)
		}
		var bytes []byte
		bytes, err = ioutil.ReadFile(sourcePath)
		return replaceTokens(string(bytes)), err
	}

	subject, err := readSource(indata.Params.Subject)
	if err != nil {
		return "", err
	}
	subject = strings.Trim(subject, "\n")

	var headers string
	if indata.Params.Headers != "" {
		headers, err = readSource(indata.Params.Headers)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		headers = strings.Trim(headers, "\n")
	}

	var body string
	if indata.Params.Body != "" {
		body, err = readSource(indata.Params.Body)
		if err != nil {
			return "", err
		}
	}

	if indata.Params.To != "" {
		var toList string
		toList, err = readSource(indata.Params.To)
		if err != nil {
			return "", err
		}
		if len(toList) > 0 {
			toListArray := strings.Split(toList, ",")
			for _, toAddress := range toListArray {
				indata.Source.To = append(indata.Source.To, strings.TrimSpace(toAddress))
			}
		}
	}

	var outdata Output
	outdata.Version.Time = time.Now().UTC()
	outdata.Metadata = []MetadataItem{
		{Name: "smtp_host", Value: indata.Source.SMTP.Host},
		{Name: "subject", Value: subject},
		{Name: "version", Value: version},
	}
	outbytes, err := json.Marshal(outdata)
	if err != nil {
		return "", err
	}

	if indata.Params.SendEmptyBody == false && len(body) == 0 {
		fmt.Fprintf(os.Stderr, "Message not sent because the message body is empty and send_empty_body parameter was set to false. Github readme: https://github.com/pivotal-cf/email-resource")
		return string(outbytes), nil
	}
	var messageData []byte
	messageData = append(messageData, []byte("To: "+strings.Join(indata.Source.To, ", ")+"\n")...)
	messageData = append(messageData, []byte("From: "+indata.Source.From+"\n")...)
	if headers != "" {
		messageData = append(messageData, []byte(headers+"\n")...)
	}
	messageData = append(messageData, []byte("Subject: "+subject+"\n")...)

	messageData = append(messageData, []byte("\n")...)
	messageData = append(messageData, []byte(body)...)

	var c *smtp.Client
	var wc io.WriteCloser
	c, err = smtp.Dial(fmt.Sprintf("%s:%s", indata.Source.SMTP.Host, indata.Source.SMTP.Port))
	if err != nil {
		return "", err
	}
	defer c.Close()

	if !indata.Source.SMTP.Anonymous {
		auth := smtp.PlainAuth(
			"",
			indata.Source.SMTP.Username,
			indata.Source.SMTP.Password,
			indata.Source.SMTP.Host,
		)
		if err = c.Hello("localhost"); err != nil {
			return "", err
		}
		if ok, _ := c.Extension("STARTTLS"); ok {
			config := &tls.Config{
				ServerName:         indata.Source.SMTP.Host,
				InsecureSkipVerify: indata.Source.SMTP.SkipSSLValidation,
			}
			if err = c.StartTLS(config); err != nil {
				return "", err
			}
		}
		if auth != nil {
			if ok, _ := c.Extension("AUTH"); ok {
				if err = c.Auth(auth); err != nil {
					return "", err
				}
			}
		}
	}
	if err = c.Mail(indata.Source.From); err != nil {
		return "", err
	}
	for _, addr := range indata.Source.To {
		if err = c.Rcpt(addr); err != nil {
			return "", err
		}
	}
	wc, err = c.Data()
	if err != nil {
		return "", err
	}
	_, err = wc.Write(messageData)
	if err != nil {
		return "", err
	}
	err = wc.Close()
	if err != nil {
		return "", err
	}
	err = c.Quit()
	if err != nil {
		return "", err
	}

	return string(outbytes), err
}
<|endoftext|>