<commit_before>package main

import (
	"flag"
	"fmt"
	"log"

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

var (
	dataFile = flag.String("file", "store.json", "data store file name")
	status   = flag.String("status", "stopped|pending|terminated", "the status you would like to poll")
	useToml  = flag.Bool("toml", false, "A switch to use creds from tomlfile instead of ENV")
)

var store *StatusStore

type Conn struct {
	aw2  *ec2.EC2
	data map[string]string
}

// create a struct to map toml config file
// TODO: add this as an switch in init()
type AwsConfig struct {
	AwsSecretKey string `toml:"AWS_ACCESS_KEY_ID"`
	AwsAccessKey string `toml:"AWS_SECRET_ACCESS_KEY"`
	Region       string `toml:"AWS_REGION"`
}

func recieveStatus(dataMap map[string]string) <-chan string {
	c := make(chan string)
	go func() {
		for _, v := range dataMap {
			c <- fmt.Sprintf("%s", v)
			//			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c

}

func (c *Conn) iterateResToMap(resp *ec2.DescribeInstancesOutput) {
	insMap := make(map[string]string)
	for idx, _ := range resp.Reservations {
		for _, inst := range resp.Reservations[idx].Instances {
			// fmt.Printf("   Instance State: %v InstanceID: %v \n", *inst.State.Name, *inst.InstanceID)
			// dereference pointer
			var id, state string
			id = *inst.PrivateDNSName
			state = *inst.State.Name
			insMap[id] = state
		}
	}
	c.data = insMap
}

func (c *Conn) GetEc2Data() {

	resp, err := c.aw2.DescribeInstances(nil)
	if err != nil {
		log.Fatal(err)
	}

	c.iterateResToMap(resp)

}

func NewEc2() *Conn {
	c := new(Conn)
	c.aw2 = ec2.New(&aws.Config{Region: "us-west-2"})

	return c
}

func (d *StatusStore) AddDataToFile(status string, c *Conn) {
	for k, v := range c.data {
		if v == status {
			if _, ok := d.status[k]; ok {
			} else {
				err := d.save(k, v)
				if err != nil {
					log.Printf("something went wrong save %s", k)
				}
			}
		}
	}
}

func Run() {
	// do something here to poll refresh data and other things
}

func Add(value string) {
	key := store.Put(value)
	fmt.Println(key)
}

func main() {
	// Create an EC2 service object in the "us-west-2" region
	// Note that you can also configure your region globally by
	// exporting the AWS_REGION environment variable
	flag.Parse()

	// instantiate new ec2 "object"
	c := NewEc2()

	// Get new Status store
	d := NewStatusStore(*dataFile)

	// Get a data set to work with
	c.GetEc2Data()

	// lets save some data
	d.AddDataToFile(*status, c)

}
<commit_msg>{WIP} making the run function do things<commit_after>package main

import (
	"flag"
	"fmt"
	"log"

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

var (
	dataFile = flag.String("file", "store.json", "data store file name")
	status   = flag.String("status", "stopped|pending|terminated", "the status you would like to poll")
	useToml  = flag.Bool("toml", false, "A switch to use creds from tomlfile instead of ENV")
)

var store *StatusStore

type Conn struct {
	aw2  *ec2.EC2
	data map[string]string
}

// create a struct to map toml config file
// TODO: add this as an switch in init()
type AwsConfig struct {
	AwsSecretKey string `toml:"AWS_ACCESS_KEY_ID"`
	AwsAccessKey string `toml:"AWS_SECRET_ACCESS_KEY"`
	Region       string `toml:"AWS_REGION"`
}

func recieveStatus(dataMap map[string]string) <-chan string {
	c := make(chan string)
	go func() {
		for _, v := range dataMap {
			c <- fmt.Sprintf("%s", v)
			//			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c

}

func (c *Conn) iterateResToMap(resp *ec2.DescribeInstancesOutput) {
	insMap := make(map[string]string)
	for idx, _ := range resp.Reservations {
		for _, inst := range resp.Reservations[idx].Instances {
			// fmt.Printf("   Instance State: %v InstanceID: %v \n", *inst.State.Name, *inst.InstanceID)
			// dereference pointer
			var id, state string
			id = *inst.PrivateDNSName
			state = *inst.State.Name
			insMap[id] = state
		}
	}
	c.data = insMap
}

func (c *Conn) GetEc2Data() {

	resp, err := c.aw2.DescribeInstances(nil)
	if err != nil {
		log.Fatal(err)
	}

	c.iterateResToMap(resp)

}

func NewEc2() *Conn {
	c := new(Conn)
	c.aw2 = ec2.New(&aws.Config{Region: "us-west-2"})

	return c
}

func (d *StatusStore) AddDataToFile(status string, c *Conn) {
	for k, v := range c.data {
		if v == status {
			if _, ok := d.status[k]; ok {
			} else {
				err := d.save(k, v)
				if err != nil {
					log.Printf("something went wrong save %s", k)
				}
			}
		}
	}
}

func (c *Conn) Run() {

	// Get a data set to work with
	c.GetEc2Data()
}

func Add(value string) {
	key := store.Put(value)
	fmt.Println(key)
}

func main() {
	// Create an EC2 service object in the "us-west-2" region
	// Note that you can also configure your region globally by
	// exporting the AWS_REGION environment variable
	flag.Parse()

	// instantiate new ec2 "object"
	c := NewEc2()

	c.Run()

	// Get new Status store
	d := NewStatusStore(*dataFile)

	// lets save some data
	d.AddDataToFile(*status, c)

}
<|endoftext|>


<commit_before>package builder

import (
	"errors"
	"io"
	"io/ioutil"
	"regexp"

	l "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
	"github.com/modcloth/go-fileutils"
	"github.com/onsi/gocleanup"
	"github.com/rafecolton/go-dockerclient-quick"

	"github.com/winchman/builder-core/communication"
	"github.com/winchman/builder-core/filecheck"
	"github.com/winchman/builder-core/parser"
)

var (
	// SkipPush will, when set to true, override any behavior set by a Bobfile and
	// will cause builders *NOT* to run `docker push` commands.  SkipPush is also set
	// by the `--skip-push` option when used on the command line.
	SkipPush bool

	imageWithTagRegex = regexp.MustCompile("^(.*):(.*)$")
)

/*
A Builder is the struct that actually does the work of moving files around and
executing the commands that do the docker build.
*/
type Builder struct {
	dockerClient    dockerclient.DockerClient
	workdir         string
	nextSubSequence *parser.SubSequence
	Stdout          io.Writer
	reporter        *comm.Reporter
	Builderfile     string
	contextDir      string

	// KeepTemporaryTag instructs the builder to keep the temporary tag, which
	// takes the form registry/project:<random=uuid>
	KeepTemporaryTag bool
}

/*
SetNextSubSequence sets the next subsequence within bob to be processed. This
function is exported because it is used explicitly in tests, but in Build(), it
is intended to be used as a helper function.
*/
func (bob *Builder) SetNextSubSequence(subSeq *parser.SubSequence) {
	bob.nextSubSequence = subSeq
}

// NewBuilderOptions encapsulates all of the options necessary for creating a
// new builder
type NewBuilderOptions struct {
	Log          comm.LogChan
	Event        comm.EventChan
	ContextDir   string
	dockerClient dockerclient.DockerClient // default to nil for regular docker client
}

/*
NewBuilder returns an instance of a Builder struct.  The function exists in
case we want to initialize our Builders with something.
*/
func NewBuilder(opts NewBuilderOptions) *Builder {
	var ret = &Builder{
		reporter:   comm.NewReporter(opts.Log, opts.Event),
		contextDir: opts.ContextDir,
	}

	ret.dockerClient = opts.dockerClient

	if opts.Log != nil {
		ret.Stdout = comm.NewLogEntryWriter(opts.Log)
	} else {
		ret.Stdout = ioutil.Discard /* /dev/null */
	}

	return ret
}

// BuildCommandSequence performs a build from a parser-generated CommandSequence struct
func (bob *Builder) BuildCommandSequence(commandSequence *parser.CommandSequence) error {
	bob.reporter.Event(comm.EventOptions{EventType: comm.RequestedEvent})

	if bob.dockerClient == nil {
		client, err := dockerclient.NewDockerClient()
		if err != nil {
			return err
		}
		bob.dockerClient = client
	}

	for _, seq := range commandSequence.Commands {
		var imageID string
		var err error

		if err := bob.cleanWorkdir(); err != nil {
			return err
		}
		bob.SetNextSubSequence(seq)
		if err := bob.setup(); err != nil {
			return err
		}

		bob.reporter.Log(
			l.WithField("container_section", seq.Metadata.Name),
			"running commands for container section",
		)

		for _, cmd := range seq.SubCommand {
			opts := &parser.DockerCmdOpts{
				DockerClient: bob.dockerClient,
				Image:        imageID,
				ImageUUID:    seq.Metadata.UUID,
				SkipPush:     SkipPush,
				Stdout:       bob.Stdout,
				Workdir:      bob.workdir,
				Reporter:     bob.reporter,
			}
			cmd = cmd.WithOpts(opts)

			bob.reporter.Log(l.WithField("command", cmd.Message()), "running docker command")

			if imageID, err = cmd.Run(); err != nil {
				switch err.(type) {
				case parser.NilClientError:
					continue
				default:
					return err
				}
			}

			bob.reporter.Log(
				l.WithFields(l.Fields{
					"command":  cmd.Message(),
					"image_id": imageID,
				}),
				"finished running docker command")
		}

		if !bob.KeepTemporaryTag {
			bob.attemptToDeleteTemporaryUUIDTag(seq.Metadata.UUID)
		}
	}

	bob.reporter.Event(comm.EventOptions{EventType: comm.CompletedEvent})

	return nil
}

func (bob *Builder) attemptToDeleteTemporaryUUIDTag(uuid string) {
	if bob.dockerClient == nil {
		return
	}

	regex := ":" + uuid + "$"
	image, err := bob.dockerClient.LatestImageByRegex(regex)
	if err != nil {
		bob.reporter.LogLevel(
			l.WithField("err", err),
			"error getting repo taggged with temporary tag",
			l.WarnLevel,
		)
	}

	for _, tag := range image.RepoTags {
		matched, err := regexp.MatchString(regex, tag)
		if err != nil {
			return
		}
		if matched {
			bob.reporter.LogLevel(
				l.WithFields(l.Fields{
					"image_id": image.ID,
					"tag":      tag,
				}),
				"deleting temporary tag",
				l.DebugLevel,
			)

			if err = bob.dockerClient.Client().RemoveImage(tag); err != nil {
				bob.reporter.LogLevel(
					l.WithField("err", err),
					"error deleting temporary tag",
					l.WarnLevel,
				)
			}
			return
		}
	}
}

/*
Setup moves all of the correct files into place in the temporary directory in
order to perform the docker build.
*/
func (bob *Builder) setup() error {
	var workdir = bob.workdir
	var pathToDockerfile *filecheck.TrustedFilePath
	var err error

	if bob.nextSubSequence == nil {
		return errors.New("no command sub sequence set, cannot perform setup")
	}

	meta := bob.nextSubSequence.Metadata
	dockerfile := meta.Dockerfile
	opts := filecheck.NewTrustedFilePathOptions{File: dockerfile, Top: bob.contextDir}
	pathToDockerfile, err = filecheck.NewTrustedFilePath(opts)
	if err != nil {
		return err
	}

	if pathToDockerfile.Sanitize(); pathToDockerfile.State != filecheck.OK {
		return pathToDockerfile.Error
	}

	contextDir := pathToDockerfile.Top()
	tarStream, err := archive.TarWithOptions(contextDir, &archive.TarOptions{
		Compression: archive.Uncompressed,
		Excludes:    []string{"Dockerfile"},
	})
	if err != nil {
		return err
	}

	defer tarStream.Close()
	if err := archive.Untar(tarStream, workdir, nil); err != nil {
		return err
	}

	if err := fileutils.CpWithArgs(
		contextDir+"/"+meta.Dockerfile,
		workdir+"/Dockerfile",
		fileutils.CpArgs{PreserveModTime: true},
	); err != nil {
		return err
	}

	return nil
}

func (bob *Builder) generateWorkDir() string {
	tmp, err := ioutil.TempDir("", "bob")
	if err != nil {
		return ""
	}

	gocleanup.Register(func() {
		fileutils.RmRF(tmp)
	})

	return tmp
}

/*
cleanWorkdir effectively does a rm -rf and mkdir -p on bob's workdir.  Intended
to be used before using the workdir (i.e. before new command groups).
*/
func (bob *Builder) cleanWorkdir() error {
	workdir := bob.generateWorkDir()
	bob.workdir = workdir

	if err := fileutils.RmRF(workdir); err != nil {
		return err
	}

	return fileutils.MkdirP(workdir, 0755)
}
<commit_msg>[TRIVIAL] Minor formatting changes [ci skip]<commit_after>package builder

import (
	"errors"
	"io"
	"io/ioutil"
	"regexp"

	l "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
	"github.com/modcloth/go-fileutils"
	"github.com/onsi/gocleanup"
	"github.com/rafecolton/go-dockerclient-quick"

	"github.com/winchman/builder-core/communication"
	"github.com/winchman/builder-core/filecheck"
	"github.com/winchman/builder-core/parser"
)

var (
	// SkipPush will, when set to true, override any behavior set by a Bobfile and
	// will cause builders *NOT* to run `docker push` commands.  SkipPush is also set
	// by the `--skip-push` option when used on the command line.
	SkipPush bool

	imageWithTagRegex = regexp.MustCompile("^(.*):(.*)$")
)

/*
A Builder is the struct that actually does the work of moving files around and
executing the commands that do the docker build.
*/
type Builder struct {
	dockerClient    dockerclient.DockerClient
	workdir         string
	nextSubSequence *parser.SubSequence
	Stdout          io.Writer
	reporter        *comm.Reporter
	Builderfile     string
	contextDir      string

	// KeepTemporaryTag instructs the builder to keep the temporary tag, which
	// takes the form registry/project:<random=uuid>
	KeepTemporaryTag bool
}

/*
SetNextSubSequence sets the next subsequence within bob to be processed. This
function is exported because it is used explicitly in tests, but in Build(), it
is intended to be used as a helper function.
*/
func (bob *Builder) SetNextSubSequence(subSeq *parser.SubSequence) {
	bob.nextSubSequence = subSeq
}

// NewBuilderOptions encapsulates all of the options necessary for creating a
// new builder
type NewBuilderOptions struct {
	Log          comm.LogChan
	Event        comm.EventChan
	ContextDir   string
	dockerClient dockerclient.DockerClient // default to nil for regular docker client
}

/*
NewBuilder returns an instance of a Builder struct.  The function exists in
case we want to initialize our Builders with something.
*/
func NewBuilder(opts NewBuilderOptions) *Builder {
	var ret = &Builder{
		reporter:   comm.NewReporter(opts.Log, opts.Event),
		contextDir: opts.ContextDir,
	}

	ret.dockerClient = opts.dockerClient

	if opts.Log != nil {
		ret.Stdout = comm.NewLogEntryWriter(opts.Log)
	} else {
		ret.Stdout = ioutil.Discard /* /dev/null */
	}

	return ret
}

// BuildCommandSequence performs a build from a parser-generated CommandSequence struct
func (bob *Builder) BuildCommandSequence(commandSequence *parser.CommandSequence) error {
	bob.reporter.Event(comm.EventOptions{EventType: comm.RequestedEvent})

	if bob.dockerClient == nil {
		client, err := dockerclient.NewDockerClient()
		if err != nil {
			return err
		}
		bob.dockerClient = client
	}

	for _, seq := range commandSequence.Commands {
		var imageID string
		var err error

		if err := bob.cleanWorkdir(); err != nil {
			return err
		}
		bob.SetNextSubSequence(seq)
		if err := bob.setup(); err != nil {
			return err
		}

		bob.reporter.Log(
			l.WithField("container_section", seq.Metadata.Name),
			"running commands for container section",
		)

		for _, cmd := range seq.SubCommand {
			opts := &parser.DockerCmdOpts{
				DockerClient: bob.dockerClient,
				Image:        imageID,
				ImageUUID:    seq.Metadata.UUID,
				SkipPush:     SkipPush,
				Stdout:       bob.Stdout,
				Workdir:      bob.workdir,
				Reporter:     bob.reporter,
			}
			cmd = cmd.WithOpts(opts)

			bob.reporter.Log(l.WithField("command", cmd.Message()), "running docker command")

			if imageID, err = cmd.Run(); err != nil {
				switch err.(type) {
				case parser.NilClientError:
					continue
				default:
					return err
				}
			}

			bob.reporter.Log(
				l.WithFields(l.Fields{
					"command":  cmd.Message(),
					"image_id": imageID,
				}),
				"finished running docker command",
			)
		}

		if !bob.KeepTemporaryTag {
			bob.attemptToDeleteTemporaryUUIDTag(seq.Metadata.UUID)
		}
	}

	bob.reporter.Event(comm.EventOptions{EventType: comm.CompletedEvent})

	return nil
}

func (bob *Builder) attemptToDeleteTemporaryUUIDTag(uuid string) {
	if bob.dockerClient == nil {
		return
	}

	regex := ":" + uuid + "$"
	image, err := bob.dockerClient.LatestImageByRegex(regex)
	if err != nil {
		bob.reporter.LogLevel(
			l.WithField("err", err),
			"error getting repo taggged with temporary tag",
			l.WarnLevel,
		)
	}

	for _, tag := range image.RepoTags {
		matched, err := regexp.MatchString(regex, tag)
		if err != nil {
			return
		}
		if matched {
			bob.reporter.LogLevel(
				l.WithFields(l.Fields{
					"image_id": image.ID,
					"tag":      tag,
				}),
				"deleting temporary tag",
				l.DebugLevel,
			)

			if err = bob.dockerClient.Client().RemoveImage(tag); err != nil {
				bob.reporter.LogLevel(
					l.WithField("err", err),
					"error deleting temporary tag",
					l.WarnLevel,
				)
			}
			return
		}
	}
}

/*
Setup moves all of the correct files into place in the temporary directory in
order to perform the docker build.
*/
func (bob *Builder) setup() error {
	var workdir = bob.workdir
	var pathToDockerfile *filecheck.TrustedFilePath
	var err error

	if bob.nextSubSequence == nil {
		return errors.New("no command sub sequence set, cannot perform setup")
	}

	meta := bob.nextSubSequence.Metadata
	dockerfile := meta.Dockerfile
	opts := filecheck.NewTrustedFilePathOptions{File: dockerfile, Top: bob.contextDir}
	pathToDockerfile, err = filecheck.NewTrustedFilePath(opts)
	if err != nil {
		return err
	}

	if pathToDockerfile.Sanitize(); pathToDockerfile.State != filecheck.OK {
		return pathToDockerfile.Error
	}

	contextDir := pathToDockerfile.Top()
	tarStream, err := archive.TarWithOptions(contextDir, &archive.TarOptions{
		Compression: archive.Uncompressed,
		Excludes:    []string{"Dockerfile"},
	})
	if err != nil {
		return err
	}

	defer tarStream.Close()
	if err := archive.Untar(tarStream, workdir, nil); err != nil {
		return err
	}

	if err := fileutils.CpWithArgs(
		contextDir+"/"+meta.Dockerfile,
		workdir+"/Dockerfile",
		fileutils.CpArgs{PreserveModTime: true},
	); err != nil {
		return err
	}

	return nil
}

func (bob *Builder) generateWorkDir() string {
	tmp, err := ioutil.TempDir("", "bob")
	if err != nil {
		return ""
	}

	gocleanup.Register(func() {
		fileutils.RmRF(tmp)
	})

	return tmp
}

/*
cleanWorkdir effectively does a rm -rf and mkdir -p on bob's workdir.  Intended
to be used before using the workdir (i.e. before new command groups).
*/
func (bob *Builder) cleanWorkdir() error {
	workdir := bob.generateWorkDir()
	bob.workdir = workdir

	if err := fileutils.RmRF(workdir); err != nil {
		return err
	}

	return fileutils.MkdirP(workdir, 0755)
}
<|endoftext|>


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

package zmqutil

import (
	"log"
	"os"
	"strconv"
	"sync"
	"time"

	zmq "github.com/alecthomas/gozmq"
)

// A SocketEvent represents a set of events on a socket.
//
type SocketEvent struct {
	Socket  *Socket        // socket on which events occurred
	Events  zmq.PollEvents // bitmask of events that occurred
	Fault   error          // handlers may set this
	Message [][]byte       // when Events&POLLIN, the full message
}

// A SocketHandler acts on a *SocketEvent.
//
// When a SocketHandler returns an error to a poller, the poller will exit.  An
// instance of this interface is returned from *Poller.HandleFunc(), and can be
// passed to *Poller.Unhandle() to unsubscribe.
//
type SocketHandler interface {
	HandleSocketEvent(*SocketEvent)
}

type socketHandlerFunc struct {
	fun func(*SocketEvent)
}

func (h socketHandlerFunc) HandleSocketEvent(e *SocketEvent) {
	h.fun(e)
}

// A Poller is a ZeroMQ poller running in a goroutine.
//
// The poller will respond to events on sockets by calling handlers that have
// been associated with those events on those sockets through Handle() and
// HandleFunc().
//
// Note: since a Socket is not thread-safe, a Socket being polled by a Poller
// should not be operated on outside the scope of a handler.
//
type Poller struct {
	items   []pollItem  // sockets with their channels
	closing bool        // true if poller is either stopping or stopped
	logger  *log.Logger // changes when SetVerbose is called
	locker  *sync.Mutex // synchronize access to Poller state
}

type pollItem struct {
	socket  *Socket        // socket to poll
	events  zmq.PollEvents // events to poll for
	handler SocketHandler  // func to call when events occur
}

// NewPoller creates a new poller.
//
func NewPoller(context *Context) *Poller {
	return &Poller{
		locker: &sync.Mutex{},
	}
}

// Handle adds a socket event handler to p.
//
func (p *Poller) Handle(s *Socket, e zmq.PollEvents, h SocketHandler) {
	p.locker.Lock()
	defer p.locker.Unlock()
	var exists bool
	for _, existing := range p.items {
		if existing.socket == s && existing.handler == h {
			existing.events = existing.events | e
			exists = true
		}
	}
	if !exists {
		p.items = append(p.items, pollItem{s, e, h})
	}
}

// HandleFunc adds a socket event handler to p.
//
func (p *Poller) HandleFunc(s *Socket, e zmq.PollEvents, h func(*SocketEvent)) SocketHandler {
	handler := &socketHandlerFunc{h}
	p.Handle(s, e, handler)
	return handler
}

// Unhandle removes a handler from p for the given socket and socket events.
//
// If there are no remaining handlers for any event on this socket, the socket
// itself will cease to be polled in p.
//
func (p *Poller) Unhandle(s *Socket, e zmq.PollEvents, h SocketHandler) {
	p.locker.Lock()
	defer p.locker.Unlock()
	index := -1
	for i, existing := range p.items {
		if existing.socket == s && existing.handler == h {
			existing.events = existing.events & ^e
			if existing.events == 0 {
				index = i
			}
		}
	}
	if index == len(p.items)-1 {
		p.items = p.items[0:index]
	} else if index >= 0 {
		p.items = append(p.items[0:index], p.items[index+1:len(p.items)]...)
	}
}

// SetVerbose enables (or disables) logging to os.Stdout.
//
func (p *Poller) SetVerbose(verbose bool) error {
	if verbose == (p.logger != nil) {
		return nil
	}
	p.logf("poller.verbose = %t", verbose)
	if verbose && p.logger == nil {
		p.logger = log.New(os.Stdout, "", log.Lmicroseconds)
	} else if !verbose {
		p.logger = nil
	}
	p.logf("poller.verbose = %t", p.logger != nil)
	return nil
}

func (p *Poller) logf(s string, args ...interface{}) {
	if p.logger != nil {
		if s[len(s)-1] != '\n' {
			s += "\n"
		}
		p.logger.Printf("[zmqutil] "+s, args...)
	}
}

// Run calls Poll until an error is returned; then it closes p.
//
func (p *Poller) Run() error {
	for !p.closing {
		if err := p.Poll(-1); err != nil {
			p.logf("poller: closing notification-receiving socket...")
			p.closing = true
			return err
		}
	}
	return nil
}

// Poll polls, with the specified timeout, all sockets for all events that have
// been registered with event handlers.
//
// A negative timeout means forever; otherwise, timeout wll be truncated
// to millisecond precision.
//
// Execution will halt and return first error encountered from polling
// or handling.
//
func (p *Poller) Poll(timeout time.Duration) (err error) {

	// This PollItems construction may become inefficient for large
	// numbers of handlers.
	pollItems := make(zmq.PollItems, 0, len(p.items))
	for _, item := range p.items {
		var exists bool
		for _, existing := range pollItems {
			if existing.Socket == item.socket.base {
				existing.Events = existing.Events | item.events
				exists = true
			}
		}
		if !exists {
			pollItems = append(pollItems, zmq.PollItem{
				Socket: item.socket.base,
				Events: item.events,
			})
		}
	}

	_, err = zmq.Poll(pollItems, timeout)

	// Possible errors returned from Poll() are: ETERM, meaning a
	// context was closed; EFAULT, meaning a mistake was made in
	// setting up the PollItems list; and EINTR, meaning a signal
	// was delivered before any events were available.  Here, we
	// treat all errors the same:
	if err != nil {
		return err
	}

	p.logf("poller: events detected.")

	// Check all other sockets, sending any available messages to
	// their associated channels:
	for _, pollItem := range pollItems {
		event := SocketEvent{
			Events: pollItem.REvents,
		}
		for _, pollItem := range p.items {
			if pollItem.socket.base == pollItem.Socket && (pollItem.events&pollItem.REvents) != 0 {
				event.Socket = pollItem.socket
				if (pollItem.REvents&zmq.POLLIN) != 0 && event.Message == nil {
					event.Message, err = event.Socket.RecvMultipart(0)
					if err != nil {
						return err
					}
				}
				pollItem.handler.HandleSocketEvent(&event)
				if event.Fault != nil {
					return event.Fault
				}
			}
		}
	}

	return nil
}

// newPair returns a PUSH/PULL pair of inproc sockets.
func newPair(c *Context) (send *Socket, recv *Socket, err error) {
	send, err = c.NewSocket(zmq.PUSH)
	if err != nil {
		return
	}
	send.SetLinger(0)
	recv, err = c.NewSocket(zmq.PULL)
	if err != nil {
		return
	}
	send.SetLinger(0)
	addr := newInprocAddress()
	err = send.Bind(addr)
	if err != nil {
		return
	}
	err = recv.Connect(addr)
	if err != nil {
		return
	}
	return
}

// newInprocAddress returns a unique incproc address.
func newInprocAddress() string {
	inprocNextMutex.Lock()
	defer inprocNextMutex.Unlock()
	inprocNext += 1
	return "inproc://github.com/jtacoma/zmqutil/" + strconv.Itoa(inprocNext-1)
}

var inprocNext = 1
var inprocNextMutex sync.Mutex
<commit_msg>Fix compilation failure.<commit_after>// Copyright 2012-2013 Joshua Tacoma. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package zmqutil

import (
	"log"
	"os"
	"strconv"
	"sync"
	"time"

	zmq "github.com/alecthomas/gozmq"
)

// A SocketEvent represents a set of events on a socket.
//
type SocketEvent struct {
	Socket  *Socket        // socket on which events occurred
	Events  zmq.PollEvents // bitmask of events that occurred
	Fault   error          // handlers may set this
	Message [][]byte       // when Events&POLLIN, the full message
}

// A SocketHandler acts on a *SocketEvent.
//
// When a SocketHandler returns an error to a poller, the poller will exit.  An
// instance of this interface is returned from *Poller.HandleFunc(), and can be
// passed to *Poller.Unhandle() to unsubscribe.
//
type SocketHandler interface {
	HandleSocketEvent(*SocketEvent)
}

type socketHandlerFunc struct {
	fun func(*SocketEvent)
}

func (h socketHandlerFunc) HandleSocketEvent(e *SocketEvent) {
	h.fun(e)
}

// A Poller is a ZeroMQ poller running in a goroutine.
//
// The poller will respond to events on sockets by calling handlers that have
// been associated with those events on those sockets through Handle() and
// HandleFunc().
//
// Note: since a Socket is not thread-safe, a Socket being polled by a Poller
// should not be operated on outside the scope of a handler.
//
type Poller struct {
	items   []pollItem  // sockets with their channels
	closing bool        // true if poller is either stopping or stopped
	logger  *log.Logger // changes when SetVerbose is called
	locker  *sync.Mutex // synchronize access to Poller state
}

type pollItem struct {
	socket  *Socket        // socket to poll
	events  zmq.PollEvents // events to poll for
	handler SocketHandler  // func to call when events occur
}

// NewPoller creates a new poller.
//
func NewPoller(context *Context) *Poller {
	return &Poller{
		locker: &sync.Mutex{},
	}
}

// Handle adds a socket event handler to p.
//
func (p *Poller) Handle(s *Socket, e zmq.PollEvents, h SocketHandler) {
	p.locker.Lock()
	defer p.locker.Unlock()
	var exists bool
	for _, existing := range p.items {
		if existing.socket == s && existing.handler == h {
			existing.events = existing.events | e
			exists = true
		}
	}
	if !exists {
		p.items = append(p.items, pollItem{s, e, h})
	}
}

// HandleFunc adds a socket event handler to p.
//
func (p *Poller) HandleFunc(s *Socket, e zmq.PollEvents, h func(*SocketEvent)) SocketHandler {
	handler := &socketHandlerFunc{h}
	p.Handle(s, e, handler)
	return handler
}

// Unhandle removes a handler from p for the given socket and socket events.
//
// If there are no remaining handlers for any event on this socket, the socket
// itself will cease to be polled in p.
//
func (p *Poller) Unhandle(s *Socket, e zmq.PollEvents, h SocketHandler) {
	p.locker.Lock()
	defer p.locker.Unlock()
	index := -1
	for i, existing := range p.items {
		if existing.socket == s && existing.handler == h {
			existing.events = existing.events & ^e
			if existing.events == 0 {
				index = i
			}
		}
	}
	if index == len(p.items)-1 {
		p.items = p.items[0:index]
	} else if index >= 0 {
		p.items = append(p.items[0:index], p.items[index+1:len(p.items)]...)
	}
}

// SetVerbose enables (or disables) logging to os.Stdout.
//
func (p *Poller) SetVerbose(verbose bool) error {
	if verbose == (p.logger != nil) {
		return nil
	}
	p.logf("poller.verbose = %t", verbose)
	if verbose && p.logger == nil {
		p.logger = log.New(os.Stdout, "", log.Lmicroseconds)
	} else if !verbose {
		p.logger = nil
	}
	p.logf("poller.verbose = %t", p.logger != nil)
	return nil
}

func (p *Poller) logf(s string, args ...interface{}) {
	if p.logger != nil {
		if s[len(s)-1] != '\n' {
			s += "\n"
		}
		p.logger.Printf("[zmqutil] "+s, args...)
	}
}

// Run calls Poll until an error is returned; then it closes p.
//
func (p *Poller) Run() error {
	for !p.closing {
		if err := p.Poll(-1); err != nil {
			p.logf("poller: closing notification-receiving socket...")
			p.closing = true
			return err
		}
	}
	return nil
}

// Poll polls, with the specified timeout, all sockets for all events that have
// been registered with event handlers.
//
// A negative timeout means forever; otherwise, timeout wll be truncated
// to millisecond precision.
//
// Execution will halt and return first error encountered from polling
// or handling.
//
func (p *Poller) Poll(timeout time.Duration) (err error) {

	// This PollItems construction may become inefficient for large
	// numbers of handlers.
	baseItems := make(zmq.PollItems, 0, len(p.items))
	for _, item := range p.items {
		var exists bool
		for _, base := range baseItems {
			if base.Socket == item.socket.base {
				base.Events = base.Events | item.events
				exists = true
			}
		}
		if !exists {
			baseItems = append(baseItems, zmq.PollItem{
				Socket: item.socket.base,
				Events: item.events,
			})
		}
	}

	_, err = zmq.Poll(baseItems, timeout)

	// Possible errors returned from Poll() are: ETERM, meaning a
	// context was closed; EFAULT, meaning a mistake was made in
	// setting up the PollItems list; and EINTR, meaning a signal
	// was delivered before any events were available.  Here, we
	// treat all errors the same:
	if err != nil {
		return err
	}

	p.logf("poller: events detected.")

	// Check all other sockets, sending any available messages to
	// their associated channels:
	for _, base := range baseItems {
		event := SocketEvent{
			Events: base.REvents,
		}
		for _, item := range p.items {
			if item.socket.base == base.Socket && (item.events&base.REvents) != 0 {
				event.Socket = item.socket
				if (base.REvents&zmq.POLLIN) != 0 && event.Message == nil {
					event.Message, err = event.Socket.RecvMultipart(0)
					if err != nil {
						return err
					}
				}
				item.handler.HandleSocketEvent(&event)
				if event.Fault != nil {
					return event.Fault
				}
			}
		}
	}

	return nil
}

// newPair returns a PUSH/PULL pair of inproc sockets.
func newPair(c *Context) (send *Socket, recv *Socket, err error) {
	send, err = c.NewSocket(zmq.PUSH)
	if err != nil {
		return
	}
	send.SetLinger(0)
	recv, err = c.NewSocket(zmq.PULL)
	if err != nil {
		return
	}
	send.SetLinger(0)
	addr := newInprocAddress()
	err = send.Bind(addr)
	if err != nil {
		return
	}
	err = recv.Connect(addr)
	if err != nil {
		return
	}
	return
}

// newInprocAddress returns a unique incproc address.
func newInprocAddress() string {
	inprocNextMutex.Lock()
	defer inprocNextMutex.Unlock()
	inprocNext += 1
	return "inproc://github.com/jtacoma/zmqutil/" + strconv.Itoa(inprocNext-1)
}

var inprocNext = 1
var inprocNextMutex sync.Mutex
<|endoftext|>


<commit_before>/*
Package builtin implements some built-in functions for gosl (Go Language
Script Language, github.com/daviddengcn/gosl)

For use of convinience as a script language, the parameters are commonly
defined as an interface{}.
*/
package builtin

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"syscall"
)

/*
S converts anything into a string. If args is specified, v is used as a format
string.
*/
func S(v interface{}, args ...interface{}) string {
	return fmt.Sprintf(fmt.Sprint(v), args...)
}

/*
I converts anything into an int. When the value is malformed, if the optional
default value is specified, it is converted to int and returned; otherwise,
0 is returned.
*/
func I(v interface{}, def ...interface{}) int {
	if i, ok := v.(int); ok {
		return i
	}
	if i, ok := v.(int64); ok {
		return int(i)
	}

	i, err := strconv.Atoi(S(v))
	if err != nil && len(def) > 0 {
		return I(def[0])
	}
	return i
}

func execCode(err error) int {
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return status.ExitStatus()
		}
	}
	return 0
}

/*
Exec runs a command. exe is the path to the executable and args are arugment
passed to it.

If the command is executed successfuly without mistakes, (nil, 0) will be
returned. Otherwise, the error and error code will be returned.
NOTE the error code could be 0 with a non-nil error.

Stdout/stderr are directed to the current stdout/stderr.
*/
func Exec(exe interface{}, args ...string) (error, int) {
	cmd := exec.Command(S(exe), args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	return err, execCode(err)
}

/*
ExecWithStdout is similar to Exec but the stdout is captured and returned as
the first return value.
*/
func ExecWithStdout(exe interface{}, args ...string) (stdout string, err error, errCode int) {
	var stdoutBuf bytes.Buffer

	cmd := exec.Command(S(exe), args...)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Run()

	return string(stdoutBuf.Bytes()), err, execCode(err)
}

/*
ExecWithStdout is similar to Exec but the stdout/stderr are captured and
returned as the first/second return values.
*/
func ExecWithStdErrOut(exe interface{}, args ...string) (stdout, stderr string, err error, errCode int) {
	var stdoutBuf, stderrBuf bytes.Buffer

	cmd := exec.Command(S(exe), args...)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf
	cmd.Stdin = os.Stdin
	err = cmd.Run()

	return string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()), err, execCode(err)
}

/*
Eval is similar to ExecWithStdout but with stdout captured and returned as a
string. Trainling newlines are deleted.
*/
func Eval(exe interface{}, args ...string) string {
	out, _, _ := ExecWithStdout(exe, args...)
	return strings.TrimRight(out, "\r\n")
}

/*
Bash runs a command with bash. Return values are defined in Exec.
*/
func Bash(cmd interface{}, args ...interface{}) (error, int) {
	return Exec("bash", "-c", S(cmd, args...))
}

/*
BashWithStdout is similar to Bash but with stdout captured and returned as a
string.
*/
func BashWithStdout(cmd interface{}, args ...interface{}) (string, error, int) {
	return ExecWithStdout("bash", "-c", S(cmd, args...))
}

/*
BashEval is similar to BashWithStdout but only returns captured stdout as a
string. Trainling newlines are deleted. It's like the backtick substitution in
Bash.
*/
func BashEval(cmd interface{}, args ...interface{}) string {
	out, _, _ := BashWithStdout(cmd, args...)
	return strings.TrimRight(out, "\r\n")
}

/*
Pwd is similar to os.Getwd() without error returned.
*/
func Pwd() string {
	pwd, _ := os.Getwd()
	return pwd
}

/*
DefExitCode is the default exit code.
*/
var DefExitCode = 1

/*
Fatalf prints a message and let the program exit with DefExitCode.
*/
func Fatalf(msg interface{}, args ...interface{}) {
	fmt.Fprintln(os.Stderr, S(msg, args...))
	os.Exit(DefExitCode)
}

/*
Eprintf is similar to fmt.Printf but the output is stderr.
*/
func Eprintf(format interface{}, args ...interface{}) {
	fmt.Fprint(os.Stderr, S(format, args...))
}

/*
Eprint is similar to fmt.Print but the output is stderr.
*/
func Eprint(args ...interface{}) {
	fmt.Fprint(os.Stderr, args...)
}

/*
Eprintln is similar to fmt.Println but the output is stderr.
*/
func Eprintln(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
}

/*
Eprintfln is similar to Eprintf but with a trailing new-line printed
*/
func Eprintfln(format interface{}, args ...interface{}) {
	fmt.Fprintln(os.Stderr, S(format, args...))
}

/*
Printfln is similar to fmt.Printf but with a trailing new-line printed
*/
func Printfln(format interface{}, args ...interface{}) {
	fmt.Println(S(format, args...))
}

/*
Succ check whether return values of Exec and friends means succeed.
*/
func Succ(err error, code int) bool {
	return err == nil
}

/*
MustSucc checks the result of Exec/Bash. If not succeed, exit the application.
*/
func MustSucc(err error, code int) {
	if err == nil {
		return
	}

	if code != 0 {
		Fatalf("Failed with error code: %d", code)
	}

	Fatalf("Failed with error: %v", err)
}

type sortI struct {
	l    int
	less func(int, int) bool
	swap func(int, int)
}

func (s *sortI) Len() int {
	return s.l
}

func (s *sortI) Less(i, j int) bool {
	return s.less(i, j)
}

func (s *sortI) Swap(i, j int) {
	s.swap(i, j)
}

/*
SortF sorts the data defined by the length, Less and Swap functions.
*/
func SortF(Len int, Less func(int, int) bool, Swap func(int, int)) {
	sort.Sort(&sortI{l: Len, less: Less, swap: Swap})
}

/*
ScriptDir returns the folder of the current script.
*/
func ScriptDir() string {
	return path.Dir(os.Args[0])
}

/*
ScriptName returns the filename of the current script not including the path.
*/
func ScriptName() string {
	return path.Base(os.Args[0])
}

/*
Exists checks whether the path exists
*/
func Exists(p interface{}, args ...interface{}) bool {
	_, err := os.Stat(S(p, args...))
	return err == nil
}

/*
IsDir returns true only if the path exists and indicates a directory
*/
func IsDir(p interface{}, args ...interface{}) bool {
	info, err := os.Stat(S(p, args...))
	if err != nil {
		// the path does not exist
		return false
	}
	return info.Mode().IsDir()
}

/*
IsFile returns true only if the path exists and indicates a file
*/
func IsFile(p interface{}, args ...interface{}) bool {
	info, err := os.Stat(S(p, args...))
	if err != nil {
		// the path does not exist
		return false
	}
	return !info.Mode().IsDir()
}

/*
Match use regular expression pattern to match str and returns all capturing groups.
*/
func Match(str interface{}, pattern interface{}) []string {
	return regexp.MustCompile(S(pattern)).FindStringSubmatch(S(str))
}
<commit_msg>ADD DryRun<commit_after>/*
Package builtin implements some built-in functions for gosl (Go Language
Script Language, github.com/daviddengcn/gosl)

For use of convinience as a script language, the parameters are commonly
defined as an interface{}.
*/
package builtin

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"syscall"
)

var (
	// Set DryRun to true will make all Exec related functions in dry run mode, i.e. they only print the command to run.
	DryRun = false
)

/*
S converts anything into a string. If args is specified, v is used as a format
string.
*/
func S(v interface{}, args ...interface{}) string {
	return fmt.Sprintf(fmt.Sprint(v), args...)
}

/*
S2Is returns a slice of interface{} given strings.
*/
func S2Is(args ...string) (ifs []interface{}) {
	ifs = make([]interface{}, len(args))
	for i, arg := range args {
		ifs[i] = arg
	}
	return
}

/*
I converts anything into an int. When the value is malformed, if the optional
default value is specified, it is converted to int and returned; otherwise,
0 is returned.
*/
func I(v interface{}, def ...interface{}) int {
	if i, ok := v.(int); ok {
		return i
	}
	if i, ok := v.(int64); ok {
		return int(i)
	}

	i, err := strconv.Atoi(S(v))
	if err != nil && len(def) > 0 {
		return I(def[0])
	}
	return i
}

func execCode(err error) int {
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return status.ExitStatus()
		}
	}
	return 0
}

/*
Exec runs a command. exe is the path to the executable and args are arugment
passed to it.

If the command is executed successfuly without mistakes, (nil, 0) will be
returned. Otherwise, the error and error code will be returned.
NOTE the error code could be 0 with a non-nil error.

Stdout/stderr are directed to the current stdout/stderr.
*/
func Exec(exe interface{}, args ...string) (error, int) {
	if DryRun {
		Eprintfln("Exec: " + S(exe), S2Is(args...)...)
		return nil, 0
	}
	cmd := exec.Command(S(exe), args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	return err, execCode(err)
}

/*
ExecWithStdout is similar to Exec but the stdout is captured and returned as
the first return value.
*/
func ExecWithStdout(exe interface{}, args ...string) (stdout string, err error, errCode int) {
	if DryRun {
		Eprintfln("ExecWithStdout" + S(exe), S2Is(args...)...)
		return "", nil, 0
	}
	var stdoutBuf bytes.Buffer

	cmd := exec.Command(S(exe), args...)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Run()

	return string(stdoutBuf.Bytes()), err, execCode(err)
}

/*
ExecWithStdout is similar to Exec but the stdout/stderr are captured and
returned as the first/second return values.
*/
func ExecWithStdErrOut(exe interface{}, args ...string) (stdout, stderr string, err error, errCode int) {
	if DryRun {
		Eprintfln("ExecWithStdErrOut" + S(exe), S2Is(args...)...)
		return "", "", nil, 0
	}
	var stdoutBuf, stderrBuf bytes.Buffer

	cmd := exec.Command(S(exe), args...)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf
	cmd.Stdin = os.Stdin
	err = cmd.Run()

	return string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()), err, execCode(err)
}

/*
Eval is similar to ExecWithStdout but with stdout captured and returned as a
string. Trainling newlines are deleted.
*/
func Eval(exe interface{}, args ...string) string {
	out, _, _ := ExecWithStdout(exe, args...)
	return strings.TrimRight(out, "\r\n")
}

/*
Bash runs a command with bash. Return values are defined in Exec.
*/
func Bash(cmd interface{}, args ...interface{}) (error, int) {
	return Exec("bash", "-c", S(cmd, args...))
}

/*
BashWithStdout is similar to Bash but with stdout captured and returned as a
string.
*/
func BashWithStdout(cmd interface{}, args ...interface{}) (string, error, int) {
	return ExecWithStdout("bash", "-c", S(cmd, args...))
}

/*
BashEval is similar to BashWithStdout but only returns captured stdout as a
string. Trainling newlines are deleted. It's like the backtick substitution in
Bash.
*/
func BashEval(cmd interface{}, args ...interface{}) string {
	out, _, _ := BashWithStdout(cmd, args...)
	return strings.TrimRight(out, "\r\n")
}

/*
Pwd is similar to os.Getwd() without error returned.
*/
func Pwd() string {
	pwd, _ := os.Getwd()
	return pwd
}

/*
DefExitCode is the default exit code.
*/
var DefExitCode = 1

/*
Fatalf prints a message and let the program exit with DefExitCode.
*/
func Fatalf(msg interface{}, args ...interface{}) {
	fmt.Fprintln(os.Stderr, S(msg, args...))
	os.Exit(DefExitCode)
}

/*
Eprintf is similar to fmt.Printf but the output is stderr.
*/
func Eprintf(format interface{}, args ...interface{}) {
	fmt.Fprint(os.Stderr, S(format, args...))
}

/*
Eprint is similar to fmt.Print but the output is stderr.
*/
func Eprint(args ...interface{}) {
	fmt.Fprint(os.Stderr, args...)
}

/*
Eprintln is similar to fmt.Println but the output is stderr.
*/
func Eprintln(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
}

/*
Eprintfln is similar to Eprintf but with a trailing new-line printed
*/
func Eprintfln(format interface{}, args ...interface{}) {
	fmt.Fprintln(os.Stderr, S(format, args...))
}

/*
Printfln is similar to fmt.Printf but with a trailing new-line printed
*/
func Printfln(format interface{}, args ...interface{}) {
	fmt.Println(S(format, args...))
}

/*
Succ check whether return values of Exec and friends means succeed.
*/
func Succ(err error, code int) bool {
	return err == nil
}

/*
MustSucc checks the result of Exec/Bash. If not succeed, exit the application.
*/
func MustSucc(err error, code int) {
	if err == nil {
		return
	}

	if code != 0 {
		Fatalf("Failed with error code: %d", code)
	}

	Fatalf("Failed with error: %v", err)
}

type sortI struct {
	l    int
	less func(int, int) bool
	swap func(int, int)
}

func (s *sortI) Len() int {
	return s.l
}

func (s *sortI) Less(i, j int) bool {
	return s.less(i, j)
}

func (s *sortI) Swap(i, j int) {
	s.swap(i, j)
}

/*
SortF sorts the data defined by the length, Less and Swap functions.
*/
func SortF(Len int, Less func(int, int) bool, Swap func(int, int)) {
	sort.Sort(&sortI{l: Len, less: Less, swap: Swap})
}

/*
ScriptDir returns the folder of the current script.
*/
func ScriptDir() string {
	return path.Dir(os.Args[0])
}

/*
ScriptName returns the filename of the current script not including the path.
*/
func ScriptName() string {
	return path.Base(os.Args[0])
}

/*
Exists checks whether the path exists
*/
func Exists(p interface{}, args ...interface{}) bool {
	_, err := os.Stat(S(p, args...))
	return err == nil
}

/*
IsDir returns true only if the path exists and indicates a directory
*/
func IsDir(p interface{}, args ...interface{}) bool {
	info, err := os.Stat(S(p, args...))
	if err != nil {
		// the path does not exist
		return false
	}
	return info.Mode().IsDir()
}

/*
IsFile returns true only if the path exists and indicates a file
*/
func IsFile(p interface{}, args ...interface{}) bool {
	info, err := os.Stat(S(p, args...))
	if err != nil {
		// the path does not exist
		return false
	}
	return !info.Mode().IsDir()
}

/*
Match use regular expression pattern to match str and returns all capturing groups.
*/
func Match(str interface{}, pattern interface{}) []string {
	return regexp.MustCompile(S(pattern)).FindStringSubmatch(S(str))
}
<|endoftext|>


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

package walk

import . "github.com/lxn/go-winapi"

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

var mainWindowWindowClassRegistered bool

type MainWindow struct {
	TopLevelWindow
	menu            *Menu
	toolBar         *ToolBar
	clientComposite *Composite
}

func NewMainWindow() (*MainWindow, error) {
	ensureRegisteredWindowClass(mainWindowWindowClass, &mainWindowWindowClassRegistered)

	mw := &MainWindow{}

	if err := initWidget(
		mw,
		nil,
		mainWindowWindowClass,
		WS_OVERLAPPEDWINDOW,
		WS_EX_CONTROLPARENT); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(); err != nil {
		return nil, err
	}
	SetMenu(mw.hWnd, mw.menu.hMenu)

	if mw.toolBar, err = NewToolBar(mw); err != nil {
		return nil, err
	}

	if mw.clientComposite, err = NewComposite(mw); err != nil {
		return nil, err
	}
	mw.clientComposite.SetName("clientComposite")

	// This forces display of focus rectangles, as soon as the user starts to type.
	SendMessage(mw.hWnd, WM_CHANGEUISTATE, UIS_INITIALIZE, 0)

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Children() *WidgetList {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Children()
}

func (mw *MainWindow) Layout() Layout {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Layout()
}

func (mw *MainWindow) SetLayout(value Layout) error {
	if mw.clientComposite == nil {
		return newError("clientComposite not initialized")
	}

	return mw.clientComposite.SetLayout(value)
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) ClientBounds() Rectangle {
	bounds := mw.WidgetBase.ClientBounds()

	if mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.Bounds()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	return bounds
}

func (mw *MainWindow) Run() int {
	if mw.clientComposite.layout != nil {
		mw.clientComposite.layout.Update(false)
	}

	return mw.TopLevelWindow.Run()
}

func (mw *MainWindow) wndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_SIZE, WM_SIZING:
		SendMessage(mw.toolBar.hWnd, TB_AUTOSIZE, 0, 0)

		mw.clientComposite.SetBounds(mw.ClientBounds())
	}

	return mw.TopLevelWindow.wndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>MainWindow: Ensure correct initial menu display<commit_after>// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import . "github.com/lxn/go-winapi"

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

var mainWindowWindowClassRegistered bool

type MainWindow struct {
	TopLevelWindow
	menu            *Menu
	toolBar         *ToolBar
	clientComposite *Composite
}

func NewMainWindow() (*MainWindow, error) {
	ensureRegisteredWindowClass(mainWindowWindowClass, &mainWindowWindowClassRegistered)

	mw := &MainWindow{}

	if err := initWidget(
		mw,
		nil,
		mainWindowWindowClass,
		WS_OVERLAPPEDWINDOW,
		WS_EX_CONTROLPARENT); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(); err != nil {
		return nil, err
	}
	SetMenu(mw.hWnd, mw.menu.hMenu)

	if mw.toolBar, err = NewToolBar(mw); err != nil {
		return nil, err
	}

	if mw.clientComposite, err = NewComposite(mw); err != nil {
		return nil, err
	}
	mw.clientComposite.SetName("clientComposite")

	// This forces display of focus rectangles, as soon as the user starts to type.
	SendMessage(mw.hWnd, WM_CHANGEUISTATE, UIS_INITIALIZE, 0)

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Children() *WidgetList {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Children()
}

func (mw *MainWindow) Layout() Layout {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Layout()
}

func (mw *MainWindow) SetLayout(value Layout) error {
	if mw.clientComposite == nil {
		return newError("clientComposite not initialized")
	}

	return mw.clientComposite.SetLayout(value)
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) ClientBounds() Rectangle {
	bounds := mw.WidgetBase.ClientBounds()

	if mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.Bounds()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	return bounds
}

func (mw *MainWindow) Show() {
	DrawMenuBar(mw.hWnd)

	if mw.clientComposite.layout != nil {
		mw.clientComposite.layout.Update(false)
	}

	mw.TopLevelWindow.Show()
}

func (mw *MainWindow) wndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_SIZE, WM_SIZING:
		SendMessage(mw.toolBar.hWnd, TB_AUTOSIZE, 0, 0)

		mw.clientComposite.SetBounds(mw.ClientBounds())
	}

	return mw.TopLevelWindow.wndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>


<commit_before>package communicator

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"github.com/tgermain/grandRepositorySky/shared"
)

//Const parts -----------------------------------------------------------
type MessageType int

const (
	LOOKUP            MessageType = iota //0
	LOOKUPRESPONSE                       //1
	UPDATESUCCESSOR                      //2
	UPDATEPREDECESSOR                    //3
	PRINTRING                            //4
	JOINRING                             //5
	UPDATEFINGERTABLE                    //6
	AREYOUALIVE                          //7
	IAMALIVE                             //8
)

var messageTypes = []string{
	"lookup",
	"lookup response",
	"update successor",
	"update predecessor",
	"print ring",
	"join ring",
	"update finger table",
	"are you alive ?",
	"i am alive",
}

func (mt MessageType) String() string {
	return messageTypes[mt]
}

//Objects parts ---------------------------------------------------------

type Message struct {
	TypeOfMsg   MessageType
	Origin      shared.DistantNode
	Destination shared.DistantNode
	Parameters  map[string]string
}

//Global variable -------------------------------------------------------
var PendingLookups = make(map[string]chan shared.DistantNode)
var PendingHearBeat = make(map[string]chan shared.DistantNode)

//Exported methods ------------------------------------------------------
func MarshallMessage(msg *Message) []byte {
	buffer, err := json.Marshal(msg)
	if err != nil {
		shared.Logger.Error("error while marshalling:", err)
	}
	// fmt.Printf("Marshalized shared.Message :%s\n", buffer)

	return buffer
}

func UnmarshallMessage(buffer []byte) Message {
	var msg Message
	err := json.Unmarshal(buffer, &msg)
	if err != nil {
		shared.Logger.Error("error while unmarshalling:", err)
	}
	// fmt.Printf("Unmarshalized shared.Message :%+v\n", msg)

	return msg
}

func GenerateId() string {
	size := 8 // change the length of the generated id here

	rb := make([]byte, size)
	_, err := rand.Read(rb)

	if err != nil {
		shared.Logger.Error("Error when generating id", err)
	}

	rs := base64.URLEncoding.EncodeToString(rb)

	return rs
}
<commit_msg>little refactoring of message type def<commit_after>package communicator

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"github.com/tgermain/grandRepositorySky/shared"
)

//Const parts -----------------------------------------------------------
type MessageType int

const (
	LOOKUP            MessageType = iota + 1 //1
	LOOKUPRESPONSE                           //2
	UPDATESUCCESSOR                          //3
	UPDATEPREDECESSOR                        //4
	PRINTRING                                //5
	JOINRING                                 //6
	UPDATEFINGERTABLE                        //7
	AREYOUALIVE                              //8
	IAMALIVE                                 //9
)

var messageTypes = []string{
	"unknown",
	"lookup",
	"lookup response",
	"update successor",
	"update predecessor",
	"print ring",
	"join ring",
	"update finger table",
	"are you alive ?",
	"i am alive",
}

func (mt MessageType) String() string {
	return messageTypes[mt]
}

//Objects parts ---------------------------------------------------------

type Message struct {
	TypeOfMsg   MessageType
	Origin      shared.DistantNode
	Destination shared.DistantNode
	Parameters  map[string]string
}

//Global variable -------------------------------------------------------
var PendingLookups = make(map[string]chan shared.DistantNode)
var PendingHearBeat = make(map[string]chan shared.DistantNode)

//Exported methods ------------------------------------------------------
func MarshallMessage(msg *Message) []byte {
	buffer, err := json.Marshal(msg)
	if err != nil {
		shared.Logger.Error("error while marshalling:", err)
	}
	// fmt.Printf("Marshalized shared.Message :%s\n", buffer)

	return buffer
}

func UnmarshallMessage(buffer []byte) Message {
	var msg Message
	err := json.Unmarshal(buffer, &msg)
	if err != nil {
		shared.Logger.Error("error while unmarshalling:", err)
	}
	// fmt.Printf("Unmarshalized shared.Message :%+v\n", msg)

	return msg
}

func GenerateId() string {
	size := 8 // change the length of the generated id here

	rb := make([]byte, size)
	_, err := rand.Read(rb)

	if err != nil {
		shared.Logger.Error("Error when generating id", err)
	}

	rs := base64.URLEncoding.EncodeToString(rb)

	return rs
}
<|endoftext|>


<commit_before>package ssh

import (
	"bufio"
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/packer/packer"
	"io"
	"log"
	"net"
	"path/filepath"
)

type comm struct {
	client *ssh.ClientConn
	config *Config
	conn   net.Conn
}

// Config is the structure used to configure the SSH communicator.
type Config struct {
	// The configuration of the Go SSH connection
	SSHConfig *ssh.ClientConfig

	// Connection returns a new connection. The current connection
	// in use will be closed as part of the Close method, or in the
	// case an error occurs.
	Connection func() (net.Conn, error)
}

// Creates a new packer.Communicator implementation over SSH. This takes
// an already existing TCP connection and SSH configuration.
func New(config *Config) (result *comm, err error) {
	// Establish an initial connection and connect
	result = &comm{
		config: config,
	}

	if err = result.reconnect(); err != nil {
		result = nil
		return
	}

	return
}

func (c *comm) Start(cmd *packer.RemoteCmd) (err error) {
	session, err := c.newSession()
	if err != nil {
		return
	}

	// Setup our session
	session.Stdin = cmd.Stdin
	session.Stdout = cmd.Stdout
	session.Stderr = cmd.Stderr

	// Request a PTY
	termModes := ssh.TerminalModes{
		ssh.ECHO:          0,     // do not echo
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err = session.RequestPty("xterm", 80, 40, termModes); err != nil {
		return
	}

	log.Printf("starting remote command: %s", cmd.Command)
	err = session.Start(cmd.Command + "\n")
	if err != nil {
		return
	}

	// Start a goroutine to wait for the session to end and set the
	// exit boolean and status.
	go func() {
		defer session.Close()
		err := session.Wait()
		exitStatus := 0
		if err != nil {
			exitErr, ok := err.(*ssh.ExitError)
			if ok {
				exitStatus = exitErr.ExitStatus()
			}
		}

		log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command)
		cmd.SetExited(exitStatus)
	}()

	return
}

func (c *comm) Upload(path string, input io.Reader) error {
	session, err := c.newSession()
	if err != nil {
		return err
	}

	defer session.Close()

	// Get a pipe to stdin so that we can send data down
	w, err := session.StdinPipe()
	if err != nil {
		return err
	}

	// We only want to close once, so we nil w after we close it,
	// and only close in the defer if it hasn't been closed already.
	defer func() {
		if w != nil {
			w.Close()
		}
	}()

	// Get a pipe to stdout so that we can get responses back
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		return err
	}
	stdoutR := bufio.NewReader(stdoutPipe)

	// Set stderr to a bytes buffer
	stderr := new(bytes.Buffer)
	session.Stderr = stderr

	// The target directory and file for talking the SCP protocol
	target_dir := filepath.Dir(path)
	target_file := filepath.Base(path)

	// On windows, filepath.Dir uses backslash seperators (ie. "\tmp").
	// This does not work when the target host is unix.  Switch to forward slash
	// which works for unix and windows
	target_dir = filepath.ToSlash(target_dir)

	// Start the sink mode on the other side
	// TODO(mitchellh): There are probably issues with shell escaping the path
	log.Println("Starting remote scp process in sink mode")
	if err = session.Start("scp -vt " + target_dir); err != nil {
		return err
	}

	// Determine the length of the upload content by copying it
	// into an in-memory buffer. Note that this means what we upload
	// must fit into memory.
	log.Println("Copying input data into in-memory buffer so we can get the length")
	input_memory := new(bytes.Buffer)
	if _, err = io.Copy(input_memory, input); err != nil {
		return err
	}

	// Start the protocol
	log.Println("Beginning file upload...")
	fmt.Fprintln(w, "C0644", input_memory.Len(), target_file)
	err = checkSCPStatus(stdoutR)
	if err != nil {
		return err
	}

	io.Copy(w, input_memory)
	fmt.Fprint(w, "\x00")
	err = checkSCPStatus(stdoutR)
	if err != nil {
		return err
	}

	// Close the stdin, which sends an EOF, and then set w to nil so that
	// our defer func doesn't close it again since that is unsafe with
	// the Go SSH package.
	log.Println("Upload complete, closing stdin pipe")
	w.Close()
	w = nil

	// Wait for the SCP connection to close, meaning it has consumed all
	// our data and has completed. Or has errored.
	log.Println("Waiting for SSH session to complete")
	err = session.Wait()
	if err != nil {
		if exitErr, ok := err.(*ssh.ExitError); ok {
			// Otherwise, we have an ExitErorr, meaning we can just read
			// the exit status
			log.Printf("non-zero exit status: %d", exitErr.ExitStatus())

			// If we exited with status 127, it means SCP isn't available.
			// Return a more descriptive error for that.
			if exitErr.ExitStatus() == 127 {
				return errors.New(
					"SCP failed to start. This usually means that SCP is not\n" +
						"properly installed on the remote system.")
			}
		}

		return err
	}

	log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String())

	return nil
}

func (c *comm) Download(string, io.Writer) error {
	panic("not implemented yet")
}

func (c *comm) newSession() (session *ssh.Session, err error) {
	log.Println("opening new ssh session")
	if c.client == nil {
		err = errors.New("client not available")
	} else {
		session, err = c.client.NewSession()
	}

	if err != nil {
		log.Printf("ssh session open error: '%s', attempting reconnect", err)
		if err := c.reconnect(); err != nil {
			return nil, err
		}

		return c.client.NewSession()
	}

	return session, nil
}

func (c *comm) reconnect() (err error) {
	if c.conn != nil {
		c.conn.Close()
	}

	// Set the conn and client to nil since we'll recreate it
	c.conn = nil
	c.client = nil

	log.Printf("reconnecting to TCP connection for SSH")
	c.conn, err = c.config.Connection()
	if err != nil {
		log.Printf("reconnection error: %s", err)
		return
	}

	log.Printf("handshaking with SSH")
	c.client, err = ssh.Client(c.conn, c.config.SSHConfig)
	if err != nil {
		log.Printf("handshake error: %s", err)
	}

	return
}

// checkSCPStatus checks that a prior command sent to SCP completed
// successfully. If it did not complete successfully, an error will
// be returned.
func checkSCPStatus(r *bufio.Reader) error {
	code, err := r.ReadByte()
	if err != nil {
		return err
	}

	if code != 0 {
		// Treat any non-zero (really 1 and 2) as fatal errors
		message, _, err := r.ReadLine()
		if err != nil {
			return fmt.Errorf("Error reading error message: %s", err)
		}

		return errors.New(string(message))
	}

	return nil
}
<commit_msg>communicator/ssh: more error handlingin Upload<commit_after>package ssh

import (
	"bufio"
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/packer/packer"
	"io"
	"log"
	"net"
	"path/filepath"
)

type comm struct {
	client *ssh.ClientConn
	config *Config
	conn   net.Conn
}

// Config is the structure used to configure the SSH communicator.
type Config struct {
	// The configuration of the Go SSH connection
	SSHConfig *ssh.ClientConfig

	// Connection returns a new connection. The current connection
	// in use will be closed as part of the Close method, or in the
	// case an error occurs.
	Connection func() (net.Conn, error)
}

// Creates a new packer.Communicator implementation over SSH. This takes
// an already existing TCP connection and SSH configuration.
func New(config *Config) (result *comm, err error) {
	// Establish an initial connection and connect
	result = &comm{
		config: config,
	}

	if err = result.reconnect(); err != nil {
		result = nil
		return
	}

	return
}

func (c *comm) Start(cmd *packer.RemoteCmd) (err error) {
	session, err := c.newSession()
	if err != nil {
		return
	}

	// Setup our session
	session.Stdin = cmd.Stdin
	session.Stdout = cmd.Stdout
	session.Stderr = cmd.Stderr

	// Request a PTY
	termModes := ssh.TerminalModes{
		ssh.ECHO:          0,     // do not echo
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err = session.RequestPty("xterm", 80, 40, termModes); err != nil {
		return
	}

	log.Printf("starting remote command: %s", cmd.Command)
	err = session.Start(cmd.Command + "\n")
	if err != nil {
		return
	}

	// Start a goroutine to wait for the session to end and set the
	// exit boolean and status.
	go func() {
		defer session.Close()
		err := session.Wait()
		exitStatus := 0
		if err != nil {
			exitErr, ok := err.(*ssh.ExitError)
			if ok {
				exitStatus = exitErr.ExitStatus()
			}
		}

		log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command)
		cmd.SetExited(exitStatus)
	}()

	return
}

func (c *comm) Upload(path string, input io.Reader) error {
	session, err := c.newSession()
	if err != nil {
		return err
	}

	defer session.Close()

	// Get a pipe to stdin so that we can send data down
	w, err := session.StdinPipe()
	if err != nil {
		return err
	}

	// We only want to close once, so we nil w after we close it,
	// and only close in the defer if it hasn't been closed already.
	defer func() {
		if w != nil {
			w.Close()
		}
	}()

	// Get a pipe to stdout so that we can get responses back
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		return err
	}
	stdoutR := bufio.NewReader(stdoutPipe)

	// Set stderr to a bytes buffer
	stderr := new(bytes.Buffer)
	session.Stderr = stderr

	// The target directory and file for talking the SCP protocol
	target_dir := filepath.Dir(path)
	target_file := filepath.Base(path)

	// On windows, filepath.Dir uses backslash seperators (ie. "\tmp").
	// This does not work when the target host is unix.  Switch to forward slash
	// which works for unix and windows
	target_dir = filepath.ToSlash(target_dir)

	// Start the sink mode on the other side
	// TODO(mitchellh): There are probably issues with shell escaping the path
	log.Println("Starting remote scp process in sink mode")
	if err = session.Start("scp -vt " + target_dir); err != nil {
		return err
	}

	// Determine the length of the upload content by copying it
	// into an in-memory buffer. Note that this means what we upload
	// must fit into memory.
	log.Println("Copying input data into in-memory buffer so we can get the length")
	input_memory := new(bytes.Buffer)
	if _, err = io.Copy(input_memory, input); err != nil {
		return err
	}

	// Start the protocol
	log.Println("Beginning file upload...")
	fmt.Fprintln(w, "C0644", input_memory.Len(), target_file)
	err = checkSCPStatus(stdoutR)
	if err != nil {
		return err
	}

	if _, err := io.Copy(w, input_memory); err != nil{
		return err
	}

	fmt.Fprint(w, "\x00")
	err = checkSCPStatus(stdoutR)
	if err != nil {
		return err
	}

	// Close the stdin, which sends an EOF, and then set w to nil so that
	// our defer func doesn't close it again since that is unsafe with
	// the Go SSH package.
	log.Println("Upload complete, closing stdin pipe")
	w.Close()
	w = nil

	// Wait for the SCP connection to close, meaning it has consumed all
	// our data and has completed. Or has errored.
	log.Println("Waiting for SSH session to complete")
	err = session.Wait()
	if err != nil {
		if exitErr, ok := err.(*ssh.ExitError); ok {
			// Otherwise, we have an ExitErorr, meaning we can just read
			// the exit status
			log.Printf("non-zero exit status: %d", exitErr.ExitStatus())

			// If we exited with status 127, it means SCP isn't available.
			// Return a more descriptive error for that.
			if exitErr.ExitStatus() == 127 {
				return errors.New(
					"SCP failed to start. This usually means that SCP is not\n" +
						"properly installed on the remote system.")
			}
		}

		return err
	}

	log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String())

	return nil
}

func (c *comm) Download(string, io.Writer) error {
	panic("not implemented yet")
}

func (c *comm) newSession() (session *ssh.Session, err error) {
	log.Println("opening new ssh session")
	if c.client == nil {
		err = errors.New("client not available")
	} else {
		session, err = c.client.NewSession()
	}

	if err != nil {
		log.Printf("ssh session open error: '%s', attempting reconnect", err)
		if err := c.reconnect(); err != nil {
			return nil, err
		}

		return c.client.NewSession()
	}

	return session, nil
}

func (c *comm) reconnect() (err error) {
	if c.conn != nil {
		c.conn.Close()
	}

	// Set the conn and client to nil since we'll recreate it
	c.conn = nil
	c.client = nil

	log.Printf("reconnecting to TCP connection for SSH")
	c.conn, err = c.config.Connection()
	if err != nil {
		log.Printf("reconnection error: %s", err)
		return
	}

	log.Printf("handshaking with SSH")
	c.client, err = ssh.Client(c.conn, c.config.SSHConfig)
	if err != nil {
		log.Printf("handshake error: %s", err)
	}

	return
}

// checkSCPStatus checks that a prior command sent to SCP completed
// successfully. If it did not complete successfully, an error will
// be returned.
func checkSCPStatus(r *bufio.Reader) error {
	code, err := r.ReadByte()
	if err != nil {
		return err
	}

	if code != 0 {
		// Treat any non-zero (really 1 and 2) as fatal errors
		message, _, err := r.ReadLine()
		if err != nil {
			return fmt.Errorf("Error reading error message: %s", err)
		}

		return errors.New(string(message))
	}

	return nil
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/jroimartin/gorest"
	restlog "github.com/jroimartin/gorest/handlers/log"
	"github.com/jroimartin/gorest/handlers/sessions"
)

var sessionHandler *sessions.SessionHandler

const logLine = `{{.Req.RemoteAddr}} - {{.Req.Method}} {{.Req.RequestURI}}
{{range  $err := .Errors}}  Err: {{$err}}
{{end}}`

func main() {
	s := gorest.NewServer("localhost:8080")

	logger := log.New(os.Stdout, "[SESSION] ", log.LstdFlags)
	logHandler := restlog.NewLogHandler(logger, logLine)

	sessionHandler = sessions.NewSessionHandler("gorest", []byte("secret"))
	sessionHandler.SetOptions(&sessions.Options{
		Path:     "/",
		MaxAge:   3600,
		HttpOnly: true,
	})

	s.Route("/",
		gorest.M(sessionHandler),
		http.HandlerFunc(homeHandler),
		gorest.M(logHandler),
	)

	log.Fatalln(s.ListenAndServe())
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	sessionId, err := sessionHandler.SessionID(r)
	if err != nil {
		internalServerError(w)
		gorest.RegisterError(w, err)
	}
	fmt.Fprintln(w, "SessionID:", sessionId)
}

func internalServerError(w http.ResponseWriter) {
	status := http.StatusInternalServerError
	http.Error(w, http.StatusText(status), status)
}
<commit_msg>Fix _examples/session.go<commit_after>// Copyright 2014 The gorest Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/jroimartin/gorest"
	restlog "github.com/jroimartin/gorest/handlers/log"
	"github.com/jroimartin/gorest/handlers/sessions"
)

var sessionHandler sessions.SessionHandler

const logLine = `{{.Req.RemoteAddr}} - {{.Req.Method}} {{.Req.RequestURI}}
{{range  $err := .Errors}}  Err: {{$err}}
{{end}}`

func main() {
	s := gorest.NewServer("localhost:8080")

	logger := log.New(os.Stdout, "[SESSION] ", log.LstdFlags)
	logHandler := restlog.NewLogHandler(logger, logLine)

	sessionHandler = sessions.NewSessionHandler("gorest", []byte("secret"))
	sessionHandler.SetOptions(&sessions.Options{
		Path:     "/",
		MaxAge:   3600,
		HttpOnly: true,
	})

	s.Route("/",
		gorest.M(sessionHandler),
		http.HandlerFunc(homeHandler),
		gorest.M(logHandler),
	)

	log.Fatalln(s.ListenAndServe())
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	sessionId, err := sessionHandler.SessionID(r)
	if err != nil {
		internalServerError(w)
		gorest.RegisterError(w, err)
	}
	fmt.Fprintln(w, "SessionID:", sessionId)
}

func internalServerError(w http.ResponseWriter) {
	status := http.StatusInternalServerError
	http.Error(w, http.StatusText(status), status)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"

	"github.com/herald-it/goncord/controllers"
	"github.com/herald-it/goncord/models"
	. "github.com/herald-it/goncord/utils"

	"net/http"

	"github.com/herald-it/goncord/middleware"
	. "github.com/julienschmidt/httprouter"
	"gopkg.in/mgo.v2"
	"time"
	"runtime/debug"
)

var (
	settingPath = flag.String("s", "./settings.yml", "setting file path")
)

func getSession() *mgo.Session {
	s, err := mgo.Dial(models.Set.Database.Host)

	if err != nil {
		panic(err)
	}

	return s
}

func init() {
	flag.Parse()

	if err := models.LoadSettings(*settingPath); err != nil {
		panic(err)
	}
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			TelegramReport(struct{
				Error string `yaml:"error"`
				Stack string `yaml:"stack"`
			}{
				r.(error).Error(),
				string(debug.Stack()),
			})

			log.Println(r.(error).Error())
			log.Println("Wait 5 second...")
			time.Sleep(5000)
			main()
		}
	}()

	log.Println("Start initialize...")

	uc := controllers.NewUserController(getSession())
	us := controllers.NewServiceController(getSession())

	coll := middleware.MidCollect{}
	coll = coll.Add(middleware.CheckPermission).Add(middleware.Logging)

	router := New()
	router.POST(
		models.Set.Router.Register.Path,
		coll.Wrap(ErrWrap(uc.RegisterUser)),
	)
	router.POST(
		models.Set.Router.Login.Path,
		coll.Wrap(ErrWrap(uc.LoginUser)),
	)
	router.POST(
		models.Set.Router.Validate.Path,
		coll.Wrap(ErrWrap(us.IsValid)),
	)
	router.POST(
		models.Set.Router.Logout.Path,
		coll.Wrap(ErrWrap(us.Logout)),
	)
	router.POST(
		models.Set.Router.Update.Path,
		coll.Wrap(ErrWrap(uc.UpdateUser)),
	)
	router.POST(
		models.Set.Router.ResetPassword.Path,
		coll.Wrap(ErrWrap(uc.ResetPassword)),
	)

	router.GET("/", func(w http.ResponseWriter, r *http.Request, p Params) {
		w.Write([]byte("Service authorization"))
	})

	log.Println("Start auth gate!")
	if err := http.ListenAndServe(models.Set.IP, router); err != nil {
		panic(err)
	}
}
<commit_msg>Added timber bot section.<commit_after>package main

import (
	"flag"
	"log"

	"github.com/herald-it/goncord/controllers"
	"github.com/herald-it/goncord/models"
	. "github.com/herald-it/goncord/utils"

	"net/http"

	"runtime/debug"
	"time"

	"github.com/herald-it/goncord/middleware"
	. "github.com/julienschmidt/httprouter"
	"gopkg.in/mgo.v2"
)

var (
	settingPath = flag.String("s", "./settings.yml", "setting file path")
)

func getSession() *mgo.Session {
	s, err := mgo.Dial(models.Set.Database.Host)

	if err != nil {
		panic(err)
	}

	return s
}

func init() {
	flag.Parse()

	if err := models.LoadSettings(*settingPath); err != nil {
		panic(err)
	}
}

func main() {
	defer func() {
		time.Sleep(5000)

		if r := recover(); r != nil {
			TelegramReport(struct {
				Error string `yaml:"error"`
				Stack string `yaml:"stack"`
			}{
				r.(error).Error(),
				string(debug.Stack()),
			})

			log.Println(r.(error).Error())
			log.Println("Wait 5 second...")
			main()
		}
	}()

	log.Println("Start initialize...")

	uc := controllers.NewUserController(getSession())
	us := controllers.NewServiceController(getSession())

	coll := middleware.MidCollect{}
	coll = coll.Add(middleware.CheckPermission).Add(middleware.Logging)

	router := New()
	router.POST(
		models.Set.Router.Register.Path,
		coll.Wrap(ErrWrap(uc.RegisterUser)),
	)
	router.POST(
		models.Set.Router.Login.Path,
		coll.Wrap(ErrWrap(uc.LoginUser)),
	)
	router.POST(
		models.Set.Router.Validate.Path,
		coll.Wrap(ErrWrap(us.IsValid)),
	)
	router.POST(
		models.Set.Router.Logout.Path,
		coll.Wrap(ErrWrap(us.Logout)),
	)
	router.POST(
		models.Set.Router.Update.Path,
		coll.Wrap(ErrWrap(uc.UpdateUser)),
	)
	router.POST(
		models.Set.Router.ResetPassword.Path,
		coll.Wrap(ErrWrap(uc.ResetPassword)),
	)

	router.GET("/", func(w http.ResponseWriter, r *http.Request, p Params) {
		w.Write([]byte("Service authorization"))
	})

	log.Println("Start auth gate!")
	if err := http.ListenAndServe(models.Set.IP, router); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>//go:generate statik -src=./static

package main

import (
	"fmt"
	"html/template"
	"image"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	"image/jpeg"
	"image/png"

	_ "github.com/jakdept/sp9k1/statik"
	"github.com/nfnt/resize"
	"github.com/oliamb/cutter"
)

// SplitHandler allows the routing of one handler at /, and another at all locations below /.
func SplitHandler(root, more http.Handler) http.Handler {
	return splitHandler{bare: root, more: more}
}

type splitHandler struct {
	bare http.Handler
	more http.Handler
}

func (p splitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if path.Clean(r.URL.Path) == "/" {
		p.bare.ServeHTTP(w, r)
	} else {
		p.more.ServeHTTP(w, r)
	}
}

// InternalHandler serves a static, in memory filesystem..
func InternalHandler(fs http.FileSystem) http.Handler {
	return internalHandler{handler: http.FileServer(fs)}
}

type internalHandler struct {
	handler http.Handler
}

func (c internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if path.Ext(r.URL.Path) == ".template" {
		http.Error(w, fmt.Sprintf("template requested, blocked: %s", r.URL.Path), http.StatusForbidden)
		log.Printf("403 - error responding: %s", r.URL.Path)
		return
	}

	c.handler.ServeHTTP(w, r)
	return
}

// IndexHandler lists all files in a directory, and passes them to template execution to build a directory listing.
func IndexHandler(basePath string, templ *template.Template) http.Handler {
	return indexHandler{basePath, templ}
}

type IndexData struct {
	Files []string
	Dirs  []string
	All   []string
}

type indexHandler struct {
	basePath string
	templ    *template.Template
}

func (c indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(filepath.Join(c.basePath, r.URL.Path))
	if err != nil {
		http.Error(w, fmt.Sprintf("not found: %s", r.URL.Path), http.StatusNotFound)
		log.Printf("404 - could not find file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	stat, err := f.Stat()
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read target: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not stat file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	if !stat.IsDir() {
		http.Error(w, fmt.Sprintf("cannot read target: %s", r.URL.Path), http.StatusForbidden)
		log.Printf("403 - could not stat file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	contents, err := f.Readdir(0)
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read directory: %s", r.URL.Path), http.StatusForbidden)
		log.Printf("403 - could not read file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	var data IndexData

	for _, each := range contents {
		data.All = append(data.All, each.Name())
		switch each.IsDir() {
		case true:
			data.Dirs = append(data.Dirs, each.Name())
		default:
			data.Files = append(data.Files, each.Name())
		}
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = c.templ.Execute(w, data)
	if err != nil {
		http.Error(w, fmt.Sprintf("error building response: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - error responding: %s", err)
		return
	}
}

// ContentTypeHandler serves a given file back to the requester, and determines content type by algorithm only.
// It does not use the file's extension to determine the content type.
func ContentTypeHandler(basePath string) http.Handler {
	return contentTypeHandler{basePath}
}

type contentTypeHandler struct {
	basePath string
}

// contentTypeHandler.ServeHTTP satasifies the Handler interface.
func (c contentTypeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(filepath.Join(c.basePath, r.URL.Path))
	if err != nil {
		http.Error(w, fmt.Sprintf("not found: %s", r.URL.Path), http.StatusNotFound)
		log.Printf("404 - could not open file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not stat file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	chunk := make([]byte, 512)

	_, err = f.Read(chunk)
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not read from file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not seek within file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	w.Header().Set("Content-Type", http.DetectContentType(chunk))
	http.ServeContent(w, r, r.URL.Path, stat.ModTime(), f)

	return
}

func ThumbnailHandler(targetHeight, targetWidth int, rawImageDirectory, thumbnailDirectory string) http.Handler {
	return thumbnailHandler{x: targetHeight, y: targetWidth, raw: rawImageDirectory, thumbs: thumbnailDirectory}
}

type thumbnailHandler struct {
	x        int
	y        int
	raw      string
	thumbs   string
	thumbExt string
}

func (h *thumbnailHandler) openThumb(imageName string) (image.Image, error) {
	img, format, err := h.openImage(fmt.Sprintf("%s/%s.%s", h.thumbs, imageName, h.thumbExt))
	if os.ISNotExist(err) || format != h.thumbExt {
		img, _, err = h.openImage(fmt.Sprintf("%s/%s", h.thumbs, imageName))
		if err != nil {
			return nil, fmt.Errorf("could not open image [%s]: %s", imageName, err)
		}
		img, err = h.generateTHumbnail(img)
		if err != nil {
			return nil, fmt.Errorf("could not process [%s]: %s", imageName, err)
		}
		err = h.writeThumbnail(imageName, img)
		if err != nil {
			return nil, fmt.Errorf("could not cache [%s]: %s", imageName, err)
		}
	}
	if err != nil {
		return nil, fmt.Errorf("problem loading thumbnail [%s]: %s", imageName, err)
	}
	return img, nil
}

func (h *thumbnailHandler) openImage(imageName string) (image.Image, string, error) {
	path := filepath.Clean(imageName)
	reader, err := os.Open(path)
	if err != nil {
		return nil, "", err
	}
	img, format, err := image.Decode(reader)
	if err != nil {
		return nil, "", err
	}
	return img, format, nil
}

func (h *thumbnailHandler) writeThumbnail(imageName string, thumbnailImage image.Image) error {
	out, err := os.Create(filepath.Join(h.raw, imageName))
	if err != nil {
		return err
	}
	defer out.Close()
	switch h.thumbExt {
	case "jpg":
		return jpeg.Encode(out, thumbnailImage, nil)
	case "jpeg":
		return jpeg.Encode(out, thumbnailImage, nil)
	case "png":
		return png.Encode(out, thumbnailImage)
	default:
		return fmt.Errorf("extension type [%s] not supported for thumbnails", h.thumbExt)
	}
}

func (h *thumbnailHandler) generateThumbnail(rawImage image.Image) (image.Image, error) {
	shrunk := resize.Resize(0, uint(h.y), rawImage, resize.MitchellNetravali)
	thumbnail, err := cutter.Crop(shrunk, cutter.Config{
		Height:  h.x,
		Options: cutter.Copy,
		Mode:    cutter.Centered,
	})
	if err != nil {
		return nil, err
	}
	return thumbnail, nil
}
<commit_msg>added final (maybe?) functions for thumbnail handler<commit_after>//go:generate statik -src=./static

package main

import (
	"fmt"
	"html/template"
	"image"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	"image/jpeg"
	"image/png"

	_ "github.com/jakdept/sp9k1/statik"
	"github.com/nfnt/resize"
	"github.com/oliamb/cutter"
)

// SplitHandler allows the routing of one handler at /, and another at all locations below /.
func SplitHandler(root, more http.Handler) http.Handler {
	return splitHandler{bare: root, more: more}
}

type splitHandler struct {
	bare http.Handler
	more http.Handler
}

func (p splitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if path.Clean(r.URL.Path) == "/" {
		p.bare.ServeHTTP(w, r)
	} else {
		p.more.ServeHTTP(w, r)
	}
}

// InternalHandler serves a static, in memory filesystem..
func InternalHandler(fs http.FileSystem) http.Handler {
	return internalHandler{handler: http.FileServer(fs)}
}

type internalHandler struct {
	handler http.Handler
}

func (c internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if path.Ext(r.URL.Path) == ".template" {
		http.Error(w, fmt.Sprintf("template requested, blocked: %s", r.URL.Path), http.StatusForbidden)
		log.Printf("403 - error responding: %s", r.URL.Path)
		return
	}

	c.handler.ServeHTTP(w, r)
	return
}

// IndexHandler lists all files in a directory, and passes them to template execution to build a directory listing.
func IndexHandler(basePath string, templ *template.Template) http.Handler {
	return indexHandler{basePath, templ}
}

type IndexData struct {
	Files []string
	Dirs  []string
	All   []string
}

type indexHandler struct {
	basePath string
	templ    *template.Template
}

func (c indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(filepath.Join(c.basePath, r.URL.Path))
	if err != nil {
		http.Error(w, fmt.Sprintf("not found: %s", r.URL.Path), http.StatusNotFound)
		log.Printf("404 - could not find file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	stat, err := f.Stat()
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read target: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not stat file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	if !stat.IsDir() {
		http.Error(w, fmt.Sprintf("cannot read target: %s", r.URL.Path), http.StatusForbidden)
		log.Printf("403 - could not stat file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	contents, err := f.Readdir(0)
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read directory: %s", r.URL.Path), http.StatusForbidden)
		log.Printf("403 - could not read file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	var data IndexData

	for _, each := range contents {
		data.All = append(data.All, each.Name())
		switch each.IsDir() {
		case true:
			data.Dirs = append(data.Dirs, each.Name())
		default:
			data.Files = append(data.Files, each.Name())
		}
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = c.templ.Execute(w, data)
	if err != nil {
		http.Error(w, fmt.Sprintf("error building response: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - error responding: %s", err)
		return
	}
}

// ContentTypeHandler serves a given file back to the requester, and determines content type by algorithm only.
// It does not use the file's extension to determine the content type.
func ContentTypeHandler(basePath string) http.Handler {
	return contentTypeHandler{basePath}
}

type contentTypeHandler struct {
	basePath string
}

// contentTypeHandler.ServeHTTP satasifies the Handler interface.
func (c contentTypeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(filepath.Join(c.basePath, r.URL.Path))
	if err != nil {
		http.Error(w, fmt.Sprintf("not found: %s", r.URL.Path), http.StatusNotFound)
		log.Printf("404 - could not open file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not stat file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	chunk := make([]byte, 512)

	_, err = f.Read(chunk)
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not read from file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not seek within file: %s - %s", filepath.Join(c.basePath, r.URL.Path), err)
		return
	}

	w.Header().Set("Content-Type", http.DetectContentType(chunk))
	http.ServeContent(w, r, r.URL.Path, stat.ModTime(), f)

	return
}

func ThumbnailHandler(targetHeight, targetWidth int, rawImageDirectory, thumbnailDirectory string) http.Handler {
	return thumbnailHandler{x: targetHeight, y: targetWidth, raw: rawImageDirectory, thumbs: thumbnailDirectory}
}

type thumbnailHandler struct {
	x        int
	y        int
	raw      string
	thumbs   string
	thumbExt string
}

func (h thumbnailHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(filepath.Clean(fmt.Sprintf("%s/%s.%s", h.thumbs, r.URL.Path, h.thumbExt)))
	if os.IsNotExist(err) {
		var img image.Image
		img, err = h.cacheThumb(r.URL.Path)
		if err != nil {
			http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
			log.Printf("500 - error opening file: %s - %s", filepath.Join(h.thumbs, r.URL.Path), err)
			return
		}

		w.Header().Set("Content-Type", "image/"+h.thumbExt)
		switch h.thumbExt {
		case "jpg":
			jpeg.Encode(w, img, nil)
			return
		case "jpeg":
			jpeg.Encode(w, img, nil)
			return
		case "png":
			png.Encode(w, img)
			return
		default:
			http.Error(w, fmt.Sprintf("could not respond with file; %s", r.URL.Path), http.StatusInternalServerError)
			log.Printf("500 - error pushing thumbnail: %s - %s", filepath.Join(h.thumbs, r.URL.Path), err)
			return
		}

	} else if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - error opening file: %s - %s", filepath.Join(h.thumbs, r.URL.Path), err)
		return
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		http.Error(w, fmt.Sprintf("cannot read file: %s", r.URL.Path), http.StatusInternalServerError)
		log.Printf("500 - could not stat file: %s - %s", filepath.Join(h.thumbs, r.URL.Path), err)
		return
	}

	w.Header().Set("Content-Type", "image/"+h.thumbExt)
	http.ServeContent(w, r, r.URL.Path, stat.ModTime(), f)
	return
}

func (h thumbnailHandler) cacheThumb(imageName string) (image.Image, error) {
	img, format, err := h.openImage(fmt.Sprintf("%s/%s.%s", h.thumbs, imageName, h.thumbExt))
	if os.IsNotExist(err) || format != h.thumbExt {
		img, _, err = h.openImage(fmt.Sprintf("%s/%s", h.thumbs, imageName))
		if err != nil {
			return nil, fmt.Errorf("could not open image [%s]: %s", imageName, err)
		}
		img, err = h.generateThumbnail(img)
		if err != nil {
			return nil, fmt.Errorf("could not process [%s]: %s", imageName, err)
		}
		err = h.writeThumbnail(imageName, img)
		if err != nil {
			return nil, fmt.Errorf("could not cache [%s]: %s", imageName, err)
		}
	}
	if err != nil {
		return nil, fmt.Errorf("problem loading thumbnail [%s]: %s", imageName, err)
	}
	return img, nil
}

func (h thumbnailHandler) openImage(imageName string) (image.Image, string, error) {
	path := filepath.Clean(imageName)
	reader, err := os.Open(path)
	if err != nil {
		return nil, "", err
	}
	img, format, err := image.Decode(reader)
	if err != nil {
		return nil, "", err
	}
	return img, format, nil
}

func (h thumbnailHandler) writeThumbnail(imageName string, thumbnailImage image.Image) error {
	out, err := os.Create(filepath.Join(h.raw, imageName))
	if err != nil {
		return err
	}
	defer out.Close()
	switch h.thumbExt {
	case "jpg":
		return jpeg.Encode(out, thumbnailImage, nil)
	case "jpeg":
		return jpeg.Encode(out, thumbnailImage, nil)
	case "png":
		return png.Encode(out, thumbnailImage)
	default:
		return fmt.Errorf("extension type [%s] not supported for thumbnails", h.thumbExt)
	}
}

func (h thumbnailHandler) generateThumbnail(rawImage image.Image) (image.Image, error) {
	shrunk := resize.Resize(0, uint(h.y), rawImage, resize.MitchellNetravali)
	thumbnail, err := cutter.Crop(shrunk, cutter.Config{
		Height:  h.x,
		Options: cutter.Copy,
		Mode:    cutter.Centered,
	})
	if err != nil {
		return nil, err
	}
	return thumbnail, nil
}
<|endoftext|>