<commit_before>package commands

import (
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/lfs"
	"github.com/git-lfs/git-lfs/lfsapi"
	"github.com/git-lfs/git-lfs/locking"
	"github.com/git-lfs/git-lfs/progress"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tq"
	"github.com/rubyist/tracerx"
)

func uploadLeftOrAll(g *lfs.GitScanner, ctx *uploadContext, ref string) error {
	if pushAll {
		if err := g.ScanRefWithDeleted(ref, nil); err != nil {
			return err
		}
	} else {
		if err := g.ScanLeftToRemote(ref, nil); err != nil {
			return err
		}
	}
	return ctx.scannerError()
}

type uploadContext struct {
	Remote       string
	DryRun       bool
	Manifest     *tq.Manifest
	uploadedOids tools.StringSet

	meter progress.Meter
	tq    *tq.TransferQueue

	committerName  string
	committerEmail string

	trackedLocksMu *sync.Mutex

	// ALL verifiable locks
	ourLocks   map[string]locking.Lock
	theirLocks map[string]locking.Lock

	// locks from ourLocks that were modified in this push
	ownedLocks []locking.Lock

	// locks from theirLocks that were modified in this push
	unownedLocks []locking.Lock

	// tracks errors from gitscanner callbacks
	scannerErr error
	errMu      sync.Mutex
}

// Determines if a filename is lockable. Serves as a wrapper around theirLocks
// that implements GitScannerSet.
type gitScannerLockables struct {
	m map[string]locking.Lock
}

func (l *gitScannerLockables) Contains(name string) bool {
	if l == nil {
		return false
	}
	_, ok := l.m[name]
	return ok
}

type verifyState byte

const (
	verifyStateUnknown verifyState = iota
	verifyStateEnabled
	verifyStateDisabled
)

func newUploadContext(remote string, dryRun bool) *uploadContext {
	cfg.CurrentRemote = remote

	ctx := &uploadContext{
		Remote:         remote,
		Manifest:       getTransferManifest(),
		DryRun:         dryRun,
		uploadedOids:   tools.NewStringSet(),
		ourLocks:       make(map[string]locking.Lock),
		theirLocks:     make(map[string]locking.Lock),
		trackedLocksMu: new(sync.Mutex),
	}

	ctx.meter = buildProgressMeter(ctx.DryRun)
	ctx.tq = newUploadQueue(ctx.Manifest, ctx.Remote, tq.WithProgress(ctx.meter), tq.DryRun(ctx.DryRun))
	ctx.committerName, ctx.committerEmail = cfg.CurrentCommitter()

	ourLocks, theirLocks := verifyLocks(remote)
	for _, l := range theirLocks {
		ctx.theirLocks[l.Path] = l
	}
	for _, l := range ourLocks {
		ctx.ourLocks[l.Path] = l
	}

	return ctx
}

func verifyLocks(remote string) (ours, theirs []locking.Lock) {
	endpoint := getAPIClient().Endpoints.Endpoint("upload", remote)

	state := getVerifyStateFor(endpoint)
	if state == verifyStateDisabled {
		return
	}

	lockClient := newLockClient(remote)

	ours, theirs, err := lockClient.VerifiableLocks(0)
	if err != nil {
		if errors.IsNotImplementedError(err) {
			disableFor(endpoint)
		} else if !errors.IsAuthError(err) {
			Print("Remote %q does not support the LFS locking API. Consider disabling it with:", remote)
			Print("  $ git config 'lfs.%s.locksverify' false", endpoint.Url)

			if state == verifyStateEnabled {
				ExitWithError(err)
			}
		} else {
			ExitWithError(err)
		}
	} else if state == verifyStateUnknown && !supportsLockingAPI(endpoint) {
		Print("Locking support detected on remote %q. Consider enabling it with:", remote)
		Print("  $ git config 'lfs.%s.locksverify' true", endpoint.Url)
	}

	return ours, theirs
}

var (
	// hostsWithKnownLockingSupport is a list of scheme-less hostnames
	// (without port numbers) that are known to implement the LFS locking
	// API.
	//
	// Additions are welcome.
	hostsWithKnownLockingSupport = []string{
		"github.com",
	}
)

// supportsLockingAPI returns whether or not a given lfsapi.Endpoint "e"
// is known to support the LFS locking API by whether or not its hostname is
// included in the list above.
func supportsLockingAPI(e lfsapi.Endpoint) bool {
	u, err := url.Parse(e.Url)
	if err != nil {
		tracerx.Printf("commands: unable to parse %q to determine locking support: %v", e.Url, err)
		return false
	}

	for _, host := range hostsWithKnownLockingSupport {
		if u.Hostname() == host {
			return true
		}
	}
	return false
}

func (c *uploadContext) scannerError() error {
	c.errMu.Lock()
	defer c.errMu.Unlock()

	return c.scannerErr
}

func (c *uploadContext) addScannerError(err error) {
	c.errMu.Lock()
	defer c.errMu.Unlock()

	if c.scannerErr != nil {
		c.scannerErr = fmt.Errorf("%v\n%v", c.scannerErr, err)
	} else {
		c.scannerErr = err
	}
}

func (c *uploadContext) buildGitScanner() (*lfs.GitScanner, error) {
	gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) {
		if err != nil {
			c.addScannerError(err)
		} else {
			uploadPointers(c, p)
		}
	})

	gitscanner.FoundLockable = func(name string) {
		if lock, ok := c.theirLocks[name]; ok {
			c.trackedLocksMu.Lock()
			c.unownedLocks = append(c.unownedLocks, lock)
			c.trackedLocksMu.Unlock()
		}
	}

	gitscanner.PotentialLockables = &gitScannerLockables{m: c.theirLocks}
	return gitscanner, gitscanner.RemoteForPush(c.Remote)
}

// AddUpload adds the given oid to the set of oids that have been uploaded in
// the current process.
func (c *uploadContext) SetUploaded(oid string) {
	c.uploadedOids.Add(oid)
}

// HasUploaded determines if the given oid has already been uploaded in the
// current process.
func (c *uploadContext) HasUploaded(oid string) bool {
	return c.uploadedOids.Contains(oid)
}

func (c *uploadContext) prepareUpload(unfiltered ...*lfs.WrappedPointer) (*tq.TransferQueue, []*lfs.WrappedPointer) {
	numUnfiltered := len(unfiltered)
	uploadables := make([]*lfs.WrappedPointer, 0, numUnfiltered)

	// XXX(taylor): temporary measure to fix duplicate (broken) results from
	// scanner
	uniqOids := tools.NewStringSet()

	// separate out objects that _should_ be uploaded, but don't exist in
	// .git/lfs/objects. Those will skipped if the server already has them.
	for _, p := range unfiltered {
		// object already uploaded in this process, or we've already
		// seen this OID (see above), skip!
		if uniqOids.Contains(p.Oid) || c.HasUploaded(p.Oid) {
			continue
		}
		uniqOids.Add(p.Oid)

		// canUpload determines whether the current pointer "p" can be
		// uploaded through the TransferQueue below. It is set to false
		// only when the file is locked by someone other than the
		// current committer.
		var canUpload bool = true

		if lock, ok := c.theirLocks[p.Name]; ok {
			c.trackedLocksMu.Lock()
			c.unownedLocks = append(c.unownedLocks, lock)
			c.trackedLocksMu.Unlock()
			canUpload = false
		}

		if lock, ok := c.ourLocks[p.Name]; ok {
			c.trackedLocksMu.Lock()
			c.ownedLocks = append(c.ownedLocks, lock)
			c.trackedLocksMu.Unlock()
		}

		if canUpload {
			// estimate in meter early (even if it's not going into
			// uploadables), since we will call Skip() based on the
			// results of the download check queue.
			c.meter.Add(p.Size)

			uploadables = append(uploadables, p)
		}
	}

	return c.tq, uploadables
}

func uploadPointers(c *uploadContext, unfiltered ...*lfs.WrappedPointer) {
	if c.DryRun {
		for _, p := range unfiltered {
			if c.HasUploaded(p.Oid) {
				continue
			}

			Print("push %s => %s", p.Oid, p.Name)
			c.SetUploaded(p.Oid)
		}

		return
	}

	q, pointers := c.prepareUpload(unfiltered...)
	for _, p := range pointers {
		t, err := uploadTransfer(p)
		if err != nil && !errors.IsCleanPointerError(err) {
			ExitWithError(err)
		}

		q.Add(t.Name, t.Path, t.Oid, t.Size)
		c.SetUploaded(p.Oid)
	}
}

func (c *uploadContext) Await() {
	c.tq.Wait()

	var missing = make(map[string]string)
	var corrupt = make(map[string]string)
	var others = make([]error, 0, len(c.tq.Errors()))

	for _, err := range c.tq.Errors() {
		if malformed, ok := err.(*tq.MalformedObjectError); ok {
			if malformed.Missing() {
				missing[malformed.Name] = malformed.Oid
			} else if malformed.Corrupt() {
				corrupt[malformed.Name] = malformed.Oid
			}
		} else {
			others = append(others, err)
		}
	}

	if len(missing) > 0 || len(corrupt) > 0 {
		Print("LFS upload failed:")
		for name, oid := range missing {
			Print("  (missing) %s (%s)", name, oid)
		}
		for name, oid := range corrupt {
			Print("  (corrupt) %s (%s)", name, oid)
		}
	}

	for _, err := range others {
		FullError(err)
	}

	if len(c.tq.Errors()) > 0 {
		os.Exit(2)
	}

	var avoidPush bool

	c.trackedLocksMu.Lock()
	if ul := len(c.unownedLocks); ul > 0 {
		avoidPush = true

		Print("Unable to push %d locked file(s):", ul)
		for _, unowned := range c.unownedLocks {
			Print("* %s - %s", unowned.Path, unowned.Owner)
		}
	} else if len(c.ownedLocks) > 0 {
		Print("Consider unlocking your own locked file(s): (`git lfs unlock <path>`)")
		for _, owned := range c.ownedLocks {
			Print("* %s", owned.Path)
		}
	}
	c.trackedLocksMu.Unlock()

	if avoidPush {
		Error("WARNING: The above files would have halted this push.")
	}
}

// getVerifyStateFor returns whether or not lock verification is enabled for the
// given "endpoint". If no state has been explicitly set, an "unknown" state
// will be returned instead.
func getVerifyStateFor(endpoint lfsapi.Endpoint) verifyState {
	uc := config.NewURLConfig(cfg.Git)

	v, ok := uc.Get("lfs", endpoint.Url, "locksverify")
	if !ok {
		return verifyStateUnknown
	}

	if enabled, _ := strconv.ParseBool(v); enabled {
		return verifyStateEnabled
	}
	return verifyStateDisabled
}

// disableFor disables lock verification for the given lfsapi.Endpoint,
// "endpoint".
func disableFor(endpoint lfsapi.Endpoint) error {
	tracerx.Printf("commands: disabling lock verification for %q", endpoint.Url)

	key := strings.Join([]string{"lfs", endpoint.Url, "locksverify"}, ".")

	_, err := git.Config.SetLocal("", key, "false")
	return err
}
<commit_msg>commands/uploader: eagerly check supported locking status<commit_after>package commands

import (
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/lfs"
	"github.com/git-lfs/git-lfs/lfsapi"
	"github.com/git-lfs/git-lfs/locking"
	"github.com/git-lfs/git-lfs/progress"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tq"
	"github.com/rubyist/tracerx"
)

func uploadLeftOrAll(g *lfs.GitScanner, ctx *uploadContext, ref string) error {
	if pushAll {
		if err := g.ScanRefWithDeleted(ref, nil); err != nil {
			return err
		}
	} else {
		if err := g.ScanLeftToRemote(ref, nil); err != nil {
			return err
		}
	}
	return ctx.scannerError()
}

type uploadContext struct {
	Remote       string
	DryRun       bool
	Manifest     *tq.Manifest
	uploadedOids tools.StringSet

	meter progress.Meter
	tq    *tq.TransferQueue

	committerName  string
	committerEmail string

	trackedLocksMu *sync.Mutex

	// ALL verifiable locks
	ourLocks   map[string]locking.Lock
	theirLocks map[string]locking.Lock

	// locks from ourLocks that were modified in this push
	ownedLocks []locking.Lock

	// locks from theirLocks that were modified in this push
	unownedLocks []locking.Lock

	// tracks errors from gitscanner callbacks
	scannerErr error
	errMu      sync.Mutex
}

// Determines if a filename is lockable. Serves as a wrapper around theirLocks
// that implements GitScannerSet.
type gitScannerLockables struct {
	m map[string]locking.Lock
}

func (l *gitScannerLockables) Contains(name string) bool {
	if l == nil {
		return false
	}
	_, ok := l.m[name]
	return ok
}

type verifyState byte

const (
	verifyStateUnknown verifyState = iota
	verifyStateEnabled
	verifyStateDisabled
)

func newUploadContext(remote string, dryRun bool) *uploadContext {
	cfg.CurrentRemote = remote

	ctx := &uploadContext{
		Remote:         remote,
		Manifest:       getTransferManifest(),
		DryRun:         dryRun,
		uploadedOids:   tools.NewStringSet(),
		ourLocks:       make(map[string]locking.Lock),
		theirLocks:     make(map[string]locking.Lock),
		trackedLocksMu: new(sync.Mutex),
	}

	ctx.meter = buildProgressMeter(ctx.DryRun)
	ctx.tq = newUploadQueue(ctx.Manifest, ctx.Remote, tq.WithProgress(ctx.meter), tq.DryRun(ctx.DryRun))
	ctx.committerName, ctx.committerEmail = cfg.CurrentCommitter()

	ourLocks, theirLocks := verifyLocks(remote)
	for _, l := range theirLocks {
		ctx.theirLocks[l.Path] = l
	}
	for _, l := range ourLocks {
		ctx.ourLocks[l.Path] = l
	}

	return ctx
}

func verifyLocks(remote string) (ours, theirs []locking.Lock) {
	endpoint := getAPIClient().Endpoints.Endpoint("upload", remote)

	state := getVerifyStateFor(endpoint)
	if state == verifyStateDisabled {
		return
	}

	lockClient := newLockClient(remote)

	ours, theirs, err := lockClient.VerifiableLocks(0)
	if err != nil {
		if errors.IsNotImplementedError(err) {
			disableFor(endpoint)
		} else if !errors.IsAuthError(err) {
			Print("Remote %q does not support the LFS locking API. Consider disabling it with:", remote)
			Print("  $ git config 'lfs.%s.locksverify' false", endpoint.Url)

			if state == verifyStateEnabled {
				ExitWithError(err)
			}
		} else {
			ExitWithError(err)
		}
	} else if state == verifyStateUnknown {
		Print("Locking support detected on remote %q. Consider enabling it with:", remote)
		Print("  $ git config 'lfs.%s.locksverify' true", endpoint.Url)
	}

	return ours, theirs
}

var (
	// hostsWithKnownLockingSupport is a list of scheme-less hostnames
	// (without port numbers) that are known to implement the LFS locking
	// API.
	//
	// Additions are welcome.
	hostsWithKnownLockingSupport = []string{
		"github.com",
	}
)

// supportsLockingAPI returns whether or not a given lfsapi.Endpoint "e"
// is known to support the LFS locking API by whether or not its hostname is
// included in the list above.
func supportsLockingAPI(e lfsapi.Endpoint) bool {
	u, err := url.Parse(e.Url)
	if err != nil {
		tracerx.Printf("commands: unable to parse %q to determine locking support: %v", e.Url, err)
		return false
	}

	for _, host := range hostsWithKnownLockingSupport {
		if u.Hostname() == host {
			return true
		}
	}
	return false
}

func (c *uploadContext) scannerError() error {
	c.errMu.Lock()
	defer c.errMu.Unlock()

	return c.scannerErr
}

func (c *uploadContext) addScannerError(err error) {
	c.errMu.Lock()
	defer c.errMu.Unlock()

	if c.scannerErr != nil {
		c.scannerErr = fmt.Errorf("%v\n%v", c.scannerErr, err)
	} else {
		c.scannerErr = err
	}
}

func (c *uploadContext) buildGitScanner() (*lfs.GitScanner, error) {
	gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) {
		if err != nil {
			c.addScannerError(err)
		} else {
			uploadPointers(c, p)
		}
	})

	gitscanner.FoundLockable = func(name string) {
		if lock, ok := c.theirLocks[name]; ok {
			c.trackedLocksMu.Lock()
			c.unownedLocks = append(c.unownedLocks, lock)
			c.trackedLocksMu.Unlock()
		}
	}

	gitscanner.PotentialLockables = &gitScannerLockables{m: c.theirLocks}
	return gitscanner, gitscanner.RemoteForPush(c.Remote)
}

// AddUpload adds the given oid to the set of oids that have been uploaded in
// the current process.
func (c *uploadContext) SetUploaded(oid string) {
	c.uploadedOids.Add(oid)
}

// HasUploaded determines if the given oid has already been uploaded in the
// current process.
func (c *uploadContext) HasUploaded(oid string) bool {
	return c.uploadedOids.Contains(oid)
}

func (c *uploadContext) prepareUpload(unfiltered ...*lfs.WrappedPointer) (*tq.TransferQueue, []*lfs.WrappedPointer) {
	numUnfiltered := len(unfiltered)
	uploadables := make([]*lfs.WrappedPointer, 0, numUnfiltered)

	// XXX(taylor): temporary measure to fix duplicate (broken) results from
	// scanner
	uniqOids := tools.NewStringSet()

	// separate out objects that _should_ be uploaded, but don't exist in
	// .git/lfs/objects. Those will skipped if the server already has them.
	for _, p := range unfiltered {
		// object already uploaded in this process, or we've already
		// seen this OID (see above), skip!
		if uniqOids.Contains(p.Oid) || c.HasUploaded(p.Oid) {
			continue
		}
		uniqOids.Add(p.Oid)

		// canUpload determines whether the current pointer "p" can be
		// uploaded through the TransferQueue below. It is set to false
		// only when the file is locked by someone other than the
		// current committer.
		var canUpload bool = true

		if lock, ok := c.theirLocks[p.Name]; ok {
			c.trackedLocksMu.Lock()
			c.unownedLocks = append(c.unownedLocks, lock)
			c.trackedLocksMu.Unlock()
			canUpload = false
		}

		if lock, ok := c.ourLocks[p.Name]; ok {
			c.trackedLocksMu.Lock()
			c.ownedLocks = append(c.ownedLocks, lock)
			c.trackedLocksMu.Unlock()
		}

		if canUpload {
			// estimate in meter early (even if it's not going into
			// uploadables), since we will call Skip() based on the
			// results of the download check queue.
			c.meter.Add(p.Size)

			uploadables = append(uploadables, p)
		}
	}

	return c.tq, uploadables
}

func uploadPointers(c *uploadContext, unfiltered ...*lfs.WrappedPointer) {
	if c.DryRun {
		for _, p := range unfiltered {
			if c.HasUploaded(p.Oid) {
				continue
			}

			Print("push %s => %s", p.Oid, p.Name)
			c.SetUploaded(p.Oid)
		}

		return
	}

	q, pointers := c.prepareUpload(unfiltered...)
	for _, p := range pointers {
		t, err := uploadTransfer(p)
		if err != nil && !errors.IsCleanPointerError(err) {
			ExitWithError(err)
		}

		q.Add(t.Name, t.Path, t.Oid, t.Size)
		c.SetUploaded(p.Oid)
	}
}

func (c *uploadContext) Await() {
	c.tq.Wait()

	var missing = make(map[string]string)
	var corrupt = make(map[string]string)
	var others = make([]error, 0, len(c.tq.Errors()))

	for _, err := range c.tq.Errors() {
		if malformed, ok := err.(*tq.MalformedObjectError); ok {
			if malformed.Missing() {
				missing[malformed.Name] = malformed.Oid
			} else if malformed.Corrupt() {
				corrupt[malformed.Name] = malformed.Oid
			}
		} else {
			others = append(others, err)
		}
	}

	if len(missing) > 0 || len(corrupt) > 0 {
		Print("LFS upload failed:")
		for name, oid := range missing {
			Print("  (missing) %s (%s)", name, oid)
		}
		for name, oid := range corrupt {
			Print("  (corrupt) %s (%s)", name, oid)
		}
	}

	for _, err := range others {
		FullError(err)
	}

	if len(c.tq.Errors()) > 0 {
		os.Exit(2)
	}

	var avoidPush bool

	c.trackedLocksMu.Lock()
	if ul := len(c.unownedLocks); ul > 0 {
		avoidPush = true

		Print("Unable to push %d locked file(s):", ul)
		for _, unowned := range c.unownedLocks {
			Print("* %s - %s", unowned.Path, unowned.Owner)
		}
	} else if len(c.ownedLocks) > 0 {
		Print("Consider unlocking your own locked file(s): (`git lfs unlock <path>`)")
		for _, owned := range c.ownedLocks {
			Print("* %s", owned.Path)
		}
	}
	c.trackedLocksMu.Unlock()

	if avoidPush {
		Error("WARNING: The above files would have halted this push.")
	}
}

// getVerifyStateFor returns whether or not lock verification is enabled for the
// given "endpoint". If no state has been explicitly set, an "unknown" state
// will be returned instead.
func getVerifyStateFor(endpoint lfsapi.Endpoint) verifyState {
	uc := config.NewURLConfig(cfg.Git)

	v, ok := uc.Get("lfs", endpoint.Url, "locksverify")
	if !ok {
		if supportsLockingAPI(endpoint.Url) {
			return verifyStateEnabled
		}
		return verifyStateUnknown
	}

	if enabled, _ := strconv.ParseBool(v); enabled {
		return verifyStateEnabled
	}
	return verifyStateDisabled
}

// disableFor disables lock verification for the given lfsapi.Endpoint,
// "endpoint".
func disableFor(endpoint lfsapi.Endpoint) error {
	tracerx.Printf("commands: disabling lock verification for %q", endpoint.Url)

	key := strings.Join([]string{"lfs", endpoint.Url, "locksverify"}, ".")

	_, err := git.Config.SetLocal("", key, "false")
	return err
}
<|endoftext|>


<commit_before>package cartogram

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

const (
	configName = ".cartograms"
)

// Role holds information about authenticating to a role
type Role struct {
	Mfa bool `json:"mfa"`
}

// Pack defines a group of Cartograms
type Pack map[string]Cartogram

// Cartogram defines a set of Accounts
type Cartogram []Account

// Account defines the spec for a role assumption target
type Account struct {
	Account string            `json:"account"`
	Region  string            `json:"region"`
	Source  string            `json:"source"`
	Roles   map[string]Role   `json:"roles"`
	Tags    map[string]string `json:"tags"`
}

// Load populates the Cartograms from disk
func (cp Pack) Load() error {
	config, err := configDir()
	if err != nil {
		return err
	}
	fileObjs, err := ioutil.ReadDir(config)
	if err != nil {
		return nil
	}
	var files []string
	for _, fileObj := range fileObjs {
		files = append(files, path.Join(config, fileObj.Name()))
	}
	err = cp.loadFromFiles(files)
	return err
}

func (cp Pack) loadFromFiles(filePaths []string) error {
	for _, filePath := range filePaths {
		name := path.Base(filePath)
		cp[name] = Cartogram{}
		if err := cp[name].loadFromFile(filePath); err != nil {
			return err
		}
	}
	return nil
}

func (c Cartogram) loadFromFile(filePath string) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return c.loadFromString(data)
}

func (c Cartogram) loadFromString(data []byte) error {
	var results Cartogram
	if err := json.Unmarshal(data, &results); err != nil {
		return err
	}
	c = append(c, results...)
	return nil
}

// Write dumps the Cartograms to disk
func (cp Pack) Write() error {
	config, err := configDir()
	if err != nil {
		return err
	}

	for name, c := range cp {
		filePath := path.Join(config, name)
		if err := c.writeToFile(filePath); err != nil {
			return err
		}
	}
	return nil
}

func (c Cartogram) writeToFile(filePath string) error {
	data, err := c.writeToString()
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filePath, data, 0600)
	return err
}

func (c Cartogram) writeToString() ([]byte, error) {
	buffer, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return []byte{}, err
	}
	return buffer, nil
}

func configDir() (string, error) {
	home, err := homeDir()
	if err != nil {
		return "", err
	}
	return path.Join(home, configName), nil
}

func homeDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	return usr.HomeDir, nil
}
<commit_msg>mkdir for config dir<commit_after>package cartogram

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

const (
	configName = ".cartograms"
)

// Role holds information about authenticating to a role
type Role struct {
	Mfa bool `json:"mfa"`
}

// Pack defines a group of Cartograms
type Pack map[string]Cartogram

// Cartogram defines a set of Accounts
type Cartogram []Account

// Account defines the spec for a role assumption target
type Account struct {
	Account string            `json:"account"`
	Region  string            `json:"region"`
	Source  string            `json:"source"`
	Roles   map[string]Role   `json:"roles"`
	Tags    map[string]string `json:"tags"`
}

// Load populates the Cartograms from disk
func (cp Pack) Load() error {
	config, err := configDir()
	if err != nil {
		return err
	}
	fileObjs, err := ioutil.ReadDir(config)
	if err != nil {
		return nil
	}
	var files []string
	for _, fileObj := range fileObjs {
		files = append(files, path.Join(config, fileObj.Name()))
	}
	err = cp.loadFromFiles(files)
	return err
}

func (cp Pack) loadFromFiles(filePaths []string) error {
	for _, filePath := range filePaths {
		name := path.Base(filePath)
		cp[name] = Cartogram{}
		if err := cp[name].loadFromFile(filePath); err != nil {
			return err
		}
	}
	return nil
}

func (c Cartogram) loadFromFile(filePath string) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return c.loadFromString(data)
}

func (c Cartogram) loadFromString(data []byte) error {
	var results Cartogram
	if err := json.Unmarshal(data, &results); err != nil {
		return err
	}
	c = append(c, results...)
	return nil
}

// Write dumps the Cartograms to disk
func (cp Pack) Write() error {
	config, err := configDir()
	if err != nil {
		return err
	}

	for name, c := range cp {
		filePath := path.Join(config, name)
		if err := c.writeToFile(filePath); err != nil {
			return err
		}
	}
	return nil
}

func (c Cartogram) writeToFile(filePath string) error {
	data, err := c.writeToString()
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filePath, data, 0600)
	return err
}

func (c Cartogram) writeToString() ([]byte, error) {
	buffer, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return []byte{}, err
	}
	return buffer, nil
}

func configDir() (string, error) {
	home, err := homeDir()
	if err != nil {
		return "", err
	}
	dir := path.Join(home, configName)
	err = os.MkdirAll(dir, 0700)
	if err != nil {
		return "", err
	}
	return dir, nil
}

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


<commit_before>package main

import (
	"flag"

	"github.com/mattn/go-gtk/gtk"
)

func main() {
	flag.Parse()

	gtk.Main()
}
<commit_msg>Displaying window<commit_after>package main

import (
	"flag"

	"github.com/mattn/go-gtk/gtk"
)

func main() {
	flag.Parse()

	gtk.Init(nil)

	window := gtk.Window(gtk.GTK_WINDOW_TOPLEVEL)
	window.SetPosition(gtk.GTK_WIN_POS_CENTER)
	window.SetTitle("GTK Go!")

	window.SetSizeRequest(600, 600)
	window.ShowAll()

	gtk.Main()
}
<|endoftext|>


<commit_before>package board

import (
	"fmt"
	"github.com/pressly/chi"
	"github.com/tidwall/buntdb"
	"go.rls.moe/nyx/http/errw"
	"go.rls.moe/nyx/http/middle"
	"go.rls.moe/nyx/resources"
	_ "image/gif"
	_ "image/jpeg"
	"net/http"
)

func handleNewReply(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}
	err = r.ParseMultipartForm(4 * 1024 * 1024)
	if err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}

	if !resources.VerifyCaptcha(r) {
		http.Redirect(w, r,
			fmt.Sprintf("/%s/%s/thread.html?err=wrong_captcha",
				chi.URLParam(r, "board"), chi.URLParam(r, "thread")),
			http.StatusSeeOther)
		return
	}

	var reply = &resources.Reply{}

	err = parseReply(r, reply)
	if err == trollThrottle {
		http.Redirect(w, r,
			fmt.Sprintf("/%s/%s/thread.html?err=trollthrottle",
				chi.URLParam(r, "board"), chi.URLParam(r, "thread")),
			http.StatusSeeOther)
		return
	} else if err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}

	db := middle.GetDB(r)
	if err = db.Update(func(tx *buntdb.Tx) error {
		thread, err := resources.GetThread(tx, r.Host, reply.Board, reply.Thread)
		if err != nil {
			return err
		}
		return resources.NewReply(tx, r.Host, reply.Board, thread, reply, false)
	}); err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}

	http.Redirect(w, r, fmt.Sprintf("/%s/%d/thread.html", chi.URLParam(r, "board"), reply.Thread), http.StatusSeeOther)
}
<commit_msg>ignore captcha when creatig new reply<commit_after>package board

import (
	"fmt"
	"github.com/pressly/chi"
	"github.com/tidwall/buntdb"
	"go.rls.moe/nyx/config"
	"go.rls.moe/nyx/http/errw"
	"go.rls.moe/nyx/http/middle"
	"go.rls.moe/nyx/resources"
	_ "image/gif"
	_ "image/jpeg"
	"net/http"
)

func handleNewReply(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}
	err = r.ParseMultipartForm(4 * 1024 * 1024)
	if err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}

	if middle.GetConfig(r).Captcha.Mode != config.CaptchaDisabled {
		if !resources.VerifyCaptcha(r) {
			http.Redirect(w, r,
				fmt.Sprintf("/%s/%s/thread.html?err=wrong_captcha",
					chi.URLParam(r, "board"), chi.URLParam(r, "thread")),
				http.StatusSeeOther)
			return
		}
	}

	var reply = &resources.Reply{}

	err = parseReply(r, reply)
	if err == trollThrottle {
		http.Redirect(w, r,
			fmt.Sprintf("/%s/%s/thread.html?err=trollthrottle",
				chi.URLParam(r, "board"), chi.URLParam(r, "thread")),
			http.StatusSeeOther)
		return
	} else if err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}

	db := middle.GetDB(r)
	if err = db.Update(func(tx *buntdb.Tx) error {
		thread, err := resources.GetThread(tx, r.Host, reply.Board, reply.Thread)
		if err != nil {
			return err
		}
		return resources.NewReply(tx, r.Host, reply.Board, thread, reply, false)
	}); err != nil {
		errw.ErrorWriter(err, w, r)
		return
	}

	http.Redirect(w, r, fmt.Sprintf("/%s/%d/thread.html", chi.URLParam(r, "board"), reply.Thread), http.StatusSeeOther)
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"sync"
	"time"
	"unsafe"
)

var (
	maxIntChars = func() int {
		switch ^uint(0) {
		case 0xffffffff:
			// 32 bit
			return 9
		case 0xffffffffffffffff:
			// 64 bit
			return 18
		default:
			panic("Unsupported architecture :)")
		}
	}()

	maxHexIntChars = func() int {
		switch ^uint(0) {
		case 0xffffffff:
			// 32 bit
			return 7
		case 0xffffffffffffffff:
			// 64 bit
			return 15
		default:
			panic("Unsupported architecture :)")
		}
	}()

	gmtLocation = func() *time.Location {
		x, err := time.LoadLocation("GMT")
		if err != nil {
			panic(fmt.Sprintf("cannot load GMT location: %s", err))
		}
		return x
	}()
)

// AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
// to dst and returns dst (which may be newly allocated).
func AppendHTTPDate(dst []byte, date time.Time) []byte {
	return date.In(gmtLocation).AppendFormat(dst, time.RFC1123)
}

// AppendUint appends n to dst and returns dst (which may be newly allocated).
func AppendUint(dst []byte, n int) []byte {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	v := uintBufPool.Get()
	if v == nil {
		v = make([]byte, maxIntChars+8)
	}
	buf := v.([]byte)
	i := len(buf) - 1
	for {
		buf[i] = '0' + byte(n%10)
		n /= 10
		if n == 0 {
			break
		}
		i--
	}

	dst = append(dst, buf[i:]...)
	uintBufPool.Put(v)
	return dst
}

// ParseUint parses uint from buf.
func ParseUint(buf []byte) (int, error) {
	v, n, err := parseUintBuf(buf)
	if n != len(buf) {
		return -1, fmt.Errorf("only %b bytes out of %d bytes exhausted when parsing int %q", n, len(buf), buf)
	}
	return v, err
}

func parseUintBuf(b []byte) (int, int, error) {
	n := len(b)
	if n == 0 {
		return -1, 0, fmt.Errorf("empty integer")
	}
	v := 0
	for i := 0; i < n; i++ {
		c := b[i]
		k := c - '0'
		if k > 9 {
			if i == 0 {
				return -1, i, fmt.Errorf("unexpected first char %c. Expected 0-9", c)
			}
			return v, i, nil
		}
		if i >= maxIntChars {
			return -1, i, fmt.Errorf("too long int %q", b[:i+1])
		}
		v = 10*v + int(k)
	}
	return v, n, nil
}

// ParseUfloat parses unsigned float from buf.
func ParseUfloat(buf []byte) (float64, error) {
	if len(buf) == 0 {
		return -1, fmt.Errorf("empty float number")
	}
	b := buf
	var v uint64
	var offset float64 = 1.0
	var pointFound bool
	for i, c := range b {
		if c < '0' || c > '9' {
			if c == '.' {
				if pointFound {
					return -1, fmt.Errorf("duplicate point found in %q", buf)
				}
				pointFound = true
				continue
			}
			if c == 'e' || c == 'E' {
				if i+1 >= len(b) {
					return -1, fmt.Errorf("unexpected end of float after %c. num=%q", c, buf)
				}
				b = b[i+1:]
				minus := -1
				switch b[0] {
				case '+':
					b = b[1:]
					minus = 1
				case '-':
					b = b[1:]
				default:
					minus = 1
				}
				vv, err := ParseUint(b)
				if err != nil {
					return -1, fmt.Errorf("cannot parse exponent part of %q: %s", buf, err)
				}
				return float64(v) * offset * math.Pow10(minus*int(vv)), nil
			}
			return -1, fmt.Errorf("unexpected char found %c in %q", c, buf)
		}
		v = 10*v + uint64(c-'0')
		if pointFound {
			offset /= 10
		}
	}
	return float64(v) * offset, nil
}

func readHexInt(r *bufio.Reader) (int, error) {
	n := 0
	i := 0
	var k int
	for {
		c, err := r.ReadByte()
		if err != nil {
			if err == io.EOF && i > 0 {
				return n, nil
			}
			return -1, err
		}
		k = hexbyte2int(c)
		if k < 0 {
			if i == 0 {
				return -1, fmt.Errorf("cannot read hex num from empty string")
			}
			r.UnreadByte()
			return n, nil
		}
		if i >= maxHexIntChars {
			return -1, fmt.Errorf("cannot read hex num with more than %d digits", maxHexIntChars)
		}
		n = (n << 4) | k
		i++
	}
}

var uintBufPool sync.Pool

func writeHexInt(w *bufio.Writer, n int) error {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	v := uintBufPool.Get()
	if v == nil {
		v = make([]byte, maxIntChars+8)
	}
	buf := v.([]byte)
	i := len(buf) - 1
	for {
		buf[i] = int2hexbyte(n & 0xf)
		n >>= 4
		if n == 0 {
			break
		}
		i--
	}
	_, err := w.Write(buf[i:])
	uintBufPool.Put(v)
	return err
}

func int2hexbyte(n int) byte {
	if n < 10 {
		return '0' + byte(n)
	}
	return 'a' + byte(n) - 10
}

func hexbyte2int(c byte) int {
	if c >= '0' && c <= '9' {
		return int(c - '0')
	}
	if c >= 'a' && c <= 'f' {
		return int(c - 'a' + 10)
	}
	if c >= 'A' && c <= 'F' {
		return int(c - 'A' + 10)
	}
	return -1
}

const toLower = 'a' - 'A'

func uppercaseByte(p *byte) {
	c := *p
	if c >= 'a' && c <= 'z' {
		*p = c - toLower
	}
}

func lowercaseByte(p *byte) {
	c := *p
	if c >= 'A' && c <= 'Z' {
		*p = c + toLower
	}
}

func lowercaseBytes(b []byte) {
	for i, n := 0, len(b); i < n; i++ {
		lowercaseByte(&b[i])
	}
}

// Converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func unsafeBytesToStr(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func unhex(c byte) int {
	if c >= '0' && c <= '9' {
		return int(c - '0')
	}
	if c >= 'a' && c <= 'f' {
		return 10 + int(c-'a')
	}
	if c >= 'A' && c <= 'F' {
		return 10 + int(c-'A')
	}
	return -1
}

func appendQuotedArg(dst, v []byte) []byte {
	for _, c := range v {
		if c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '/' || c == '.' {
			dst = append(dst, c)
		} else {
			dst = append(dst, '%', hexChar(c>>4), hexChar(c&15))
		}
	}
	return dst
}

func hexChar(c byte) byte {
	if c < 10 {
		return '0' + c
	}
	return c - 10 + 'A'
}

// EqualBytesStr returns true if string(b) == s.
//
// This function has no performance benefits comparing to string(b) == s.
// It is left here for backwards compatilbility only.
func EqualBytesStr(b []byte, s string) bool {
	return string(b) == s
}

// AppendBytesStr appends src to dst and returns dst
// (which may be newly allocated).
func AppendBytesStr(dst []byte, src string) []byte {
	for i, n := 0, len(src); i < n; i++ {
		dst = append(dst, src[i])
	}
	return dst
}
<commit_msg>Optimized AppendBytesStr - now it is 2x faster<commit_after>package fasthttp

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"reflect"
	"sync"
	"time"
	"unsafe"
)

var (
	maxIntChars = func() int {
		switch ^uint(0) {
		case 0xffffffff:
			// 32 bit
			return 9
		case 0xffffffffffffffff:
			// 64 bit
			return 18
		default:
			panic("Unsupported architecture :)")
		}
	}()

	maxHexIntChars = func() int {
		switch ^uint(0) {
		case 0xffffffff:
			// 32 bit
			return 7
		case 0xffffffffffffffff:
			// 64 bit
			return 15
		default:
			panic("Unsupported architecture :)")
		}
	}()

	gmtLocation = func() *time.Location {
		x, err := time.LoadLocation("GMT")
		if err != nil {
			panic(fmt.Sprintf("cannot load GMT location: %s", err))
		}
		return x
	}()
)

// AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
// to dst and returns dst (which may be newly allocated).
func AppendHTTPDate(dst []byte, date time.Time) []byte {
	return date.In(gmtLocation).AppendFormat(dst, time.RFC1123)
}

// AppendUint appends n to dst and returns dst (which may be newly allocated).
func AppendUint(dst []byte, n int) []byte {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	v := uintBufPool.Get()
	if v == nil {
		v = make([]byte, maxIntChars+8)
	}
	buf := v.([]byte)
	i := len(buf) - 1
	for {
		buf[i] = '0' + byte(n%10)
		n /= 10
		if n == 0 {
			break
		}
		i--
	}

	dst = append(dst, buf[i:]...)
	uintBufPool.Put(v)
	return dst
}

// ParseUint parses uint from buf.
func ParseUint(buf []byte) (int, error) {
	v, n, err := parseUintBuf(buf)
	if n != len(buf) {
		return -1, fmt.Errorf("only %b bytes out of %d bytes exhausted when parsing int %q", n, len(buf), buf)
	}
	return v, err
}

func parseUintBuf(b []byte) (int, int, error) {
	n := len(b)
	if n == 0 {
		return -1, 0, fmt.Errorf("empty integer")
	}
	v := 0
	for i := 0; i < n; i++ {
		c := b[i]
		k := c - '0'
		if k > 9 {
			if i == 0 {
				return -1, i, fmt.Errorf("unexpected first char %c. Expected 0-9", c)
			}
			return v, i, nil
		}
		if i >= maxIntChars {
			return -1, i, fmt.Errorf("too long int %q", b[:i+1])
		}
		v = 10*v + int(k)
	}
	return v, n, nil
}

// ParseUfloat parses unsigned float from buf.
func ParseUfloat(buf []byte) (float64, error) {
	if len(buf) == 0 {
		return -1, fmt.Errorf("empty float number")
	}
	b := buf
	var v uint64
	var offset float64 = 1.0
	var pointFound bool
	for i, c := range b {
		if c < '0' || c > '9' {
			if c == '.' {
				if pointFound {
					return -1, fmt.Errorf("duplicate point found in %q", buf)
				}
				pointFound = true
				continue
			}
			if c == 'e' || c == 'E' {
				if i+1 >= len(b) {
					return -1, fmt.Errorf("unexpected end of float after %c. num=%q", c, buf)
				}
				b = b[i+1:]
				minus := -1
				switch b[0] {
				case '+':
					b = b[1:]
					minus = 1
				case '-':
					b = b[1:]
				default:
					minus = 1
				}
				vv, err := ParseUint(b)
				if err != nil {
					return -1, fmt.Errorf("cannot parse exponent part of %q: %s", buf, err)
				}
				return float64(v) * offset * math.Pow10(minus*int(vv)), nil
			}
			return -1, fmt.Errorf("unexpected char found %c in %q", c, buf)
		}
		v = 10*v + uint64(c-'0')
		if pointFound {
			offset /= 10
		}
	}
	return float64(v) * offset, nil
}

func readHexInt(r *bufio.Reader) (int, error) {
	n := 0
	i := 0
	var k int
	for {
		c, err := r.ReadByte()
		if err != nil {
			if err == io.EOF && i > 0 {
				return n, nil
			}
			return -1, err
		}
		k = hexbyte2int(c)
		if k < 0 {
			if i == 0 {
				return -1, fmt.Errorf("cannot read hex num from empty string")
			}
			r.UnreadByte()
			return n, nil
		}
		if i >= maxHexIntChars {
			return -1, fmt.Errorf("cannot read hex num with more than %d digits", maxHexIntChars)
		}
		n = (n << 4) | k
		i++
	}
}

var uintBufPool sync.Pool

func writeHexInt(w *bufio.Writer, n int) error {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	v := uintBufPool.Get()
	if v == nil {
		v = make([]byte, maxIntChars+8)
	}
	buf := v.([]byte)
	i := len(buf) - 1
	for {
		buf[i] = int2hexbyte(n & 0xf)
		n >>= 4
		if n == 0 {
			break
		}
		i--
	}
	_, err := w.Write(buf[i:])
	uintBufPool.Put(v)
	return err
}

func int2hexbyte(n int) byte {
	if n < 10 {
		return '0' + byte(n)
	}
	return 'a' + byte(n) - 10
}

func hexbyte2int(c byte) int {
	if c >= '0' && c <= '9' {
		return int(c - '0')
	}
	if c >= 'a' && c <= 'f' {
		return int(c - 'a' + 10)
	}
	if c >= 'A' && c <= 'F' {
		return int(c - 'A' + 10)
	}
	return -1
}

const toLower = 'a' - 'A'

func uppercaseByte(p *byte) {
	c := *p
	if c >= 'a' && c <= 'z' {
		*p = c - toLower
	}
}

func lowercaseByte(p *byte) {
	c := *p
	if c >= 'A' && c <= 'Z' {
		*p = c + toLower
	}
}

func lowercaseBytes(b []byte) {
	for i, n := 0, len(b); i < n; i++ {
		lowercaseByte(&b[i])
	}
}

// unsafeBytesToStr converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func unsafeBytesToStr(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// unsafeStrToBytes converts string to byte slice without memory allocation.
//
// The returned byte slice may be read until references to the original s exist.
func unsafeStrToBytes(s string) []byte {
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := reflect.SliceHeader{
		Data: sh.Data,
		Len:  sh.Len,
		Cap:  sh.Len,
	}
	return *(*[]byte)(unsafe.Pointer(&bh))
}

func unhex(c byte) int {
	if c >= '0' && c <= '9' {
		return int(c - '0')
	}
	if c >= 'a' && c <= 'f' {
		return 10 + int(c-'a')
	}
	if c >= 'A' && c <= 'F' {
		return 10 + int(c-'A')
	}
	return -1
}

func appendQuotedArg(dst, v []byte) []byte {
	for _, c := range v {
		if c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '/' || c == '.' {
			dst = append(dst, c)
		} else {
			dst = append(dst, '%', hexChar(c>>4), hexChar(c&15))
		}
	}
	return dst
}

func hexChar(c byte) byte {
	if c < 10 {
		return '0' + c
	}
	return c - 10 + 'A'
}

// EqualBytesStr returns true if string(b) == s.
//
// This function has no performance benefits comparing to string(b) == s.
// It is left here for backwards compatilbility only.
func EqualBytesStr(b []byte, s string) bool {
	return string(b) == s
}

// AppendBytesStr appends src to dst and returns dst
// (which may be newly allocated).
func AppendBytesStr(dst []byte, src string) []byte {
	// The following code is equivalent to
	//     return append(dst, []byte(src)...)
	// but it is 1.5x faster in Go1.5. I don't know why :)
	return append(dst, unsafeStrToBytes(src)...)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"testing"
	"time"
)

// Should cache first response for an unspecified period of time if when it
// doesn't specify it's own cache headers. Subsequent requests should return
// a cached response.
func TestCacheFirstResponse(t *testing.T) {
	ResetBackends(backendsByPriority)

	testRequestsCachedIndefinite(t, nil)
}

// Should cache responses for the period defined in a `Expires: n` response
// header.
func TestCacheExpires(t *testing.T) {
	ResetBackends(backendsByPriority)

	const cacheDuration = time.Duration(5 * time.Second)

	handler := func(w http.ResponseWriter) {
		headerValue := time.Now().UTC().Add(cacheDuration).Format(http.TimeFormat)
		w.Header().Set("Expires", headerValue)
	}

	testRequestsCachedDuration(t, handler, cacheDuration)
}

// Should cache responses for the period defined in a `Cache-Control:
// max-age=n` response header.
func TestCacheCacheControlMaxAge(t *testing.T) {
	ResetBackends(backendsByPriority)

	const cacheDuration = time.Duration(5 * time.Second)
	headerValue := fmt.Sprintf("max-age=%.0f", cacheDuration.Seconds())

	handler := func(w http.ResponseWriter) {
		w.Header().Set("Cache-Control", headerValue)
	}

	testRequestsCachedDuration(t, handler, cacheDuration)
}

// Should cache responses for the period defined in a `Cache-Control:
// max-age=n` response header when a `Expires: n*2` header is also present.
func TestCacheExpiresAndMaxAge(t *testing.T) {
	ResetBackends(backendsByPriority)

	const cacheDuration = time.Duration(5 * time.Second)
	const expiresDuration = cacheDuration * 2

	maxAgeValue := fmt.Sprintf("max-age=%.0f", cacheDuration.Seconds())

	handler := func(w http.ResponseWriter) {
		expiresValue := time.Now().UTC().Add(expiresDuration).Format(http.TimeFormat)

		w.Header().Set("Expires", expiresValue)
		w.Header().Set("Cache-Control", maxAgeValue)
	}

	testRequestsCachedDuration(t, handler, cacheDuration)
}

// Should cache responses with a `Cache-Control: no-cache` header. Varnish
// doesn't respect this by default.
func TestCacheCacheControlNoCache(t *testing.T) {
	ResetBackends(backendsByPriority)

	handler := func(w http.ResponseWriter) {
		w.Header().Set("Cache-Control", "no-cache")
	}

	testRequestsCachedIndefinite(t, handler)
}

// Should cache responses with a status code of 404. It's a common
// misconception that 404 responses shouldn't be cached; they should because
// they can be expensive to generate.
func TestCache404Response(t *testing.T) {
	ResetBackends(backendsByPriority)

	handler := func(w http.ResponseWriter) {
		w.WriteHeader(http.StatusNotFound)
	}

	testRequestsCachedIndefinite(t, handler)
}
<commit_msg>[#28] Test for Vary headers<commit_after>package main

import (
	"fmt"
	"net/http"
	"testing"
	"time"
)

// Should cache first response for an unspecified period of time if when it
// doesn't specify it's own cache headers. Subsequent requests should return
// a cached response.
func TestCacheFirstResponse(t *testing.T) {
	ResetBackends(backendsByPriority)

	testRequestsCachedIndefinite(t, nil)
}

// Should cache responses for the period defined in a `Expires: n` response
// header.
func TestCacheExpires(t *testing.T) {
	ResetBackends(backendsByPriority)

	const cacheDuration = time.Duration(5 * time.Second)

	handler := func(w http.ResponseWriter) {
		headerValue := time.Now().UTC().Add(cacheDuration).Format(http.TimeFormat)
		w.Header().Set("Expires", headerValue)
	}

	testRequestsCachedDuration(t, handler, cacheDuration)
}

// Should cache responses for the period defined in a `Cache-Control:
// max-age=n` response header.
func TestCacheCacheControlMaxAge(t *testing.T) {
	ResetBackends(backendsByPriority)

	const cacheDuration = time.Duration(5 * time.Second)
	headerValue := fmt.Sprintf("max-age=%.0f", cacheDuration.Seconds())

	handler := func(w http.ResponseWriter) {
		w.Header().Set("Cache-Control", headerValue)
	}

	testRequestsCachedDuration(t, handler, cacheDuration)
}

// Should cache responses for the period defined in a `Cache-Control:
// max-age=n` response header when a `Expires: n*2` header is also present.
func TestCacheExpiresAndMaxAge(t *testing.T) {
	ResetBackends(backendsByPriority)

	const cacheDuration = time.Duration(5 * time.Second)
	const expiresDuration = cacheDuration * 2

	maxAgeValue := fmt.Sprintf("max-age=%.0f", cacheDuration.Seconds())

	handler := func(w http.ResponseWriter) {
		expiresValue := time.Now().UTC().Add(expiresDuration).Format(http.TimeFormat)

		w.Header().Set("Expires", expiresValue)
		w.Header().Set("Cache-Control", maxAgeValue)
	}

	testRequestsCachedDuration(t, handler, cacheDuration)
}

// Should cache responses with a `Cache-Control: no-cache` header. Varnish
// doesn't respect this by default.
func TestCacheCacheControlNoCache(t *testing.T) {
	ResetBackends(backendsByPriority)

	handler := func(w http.ResponseWriter) {
		w.Header().Set("Cache-Control", "no-cache")
	}

	testRequestsCachedIndefinite(t, handler)
}

// Should cache responses with a status code of 404. It's a common
// misconception that 404 responses shouldn't be cached; they should because
// they can be expensive to generate.
func TestCache404Response(t *testing.T) {
	ResetBackends(backendsByPriority)

	handler := func(w http.ResponseWriter) {
		w.WriteHeader(http.StatusNotFound)
	}

	testRequestsCachedIndefinite(t, handler)
}

// Should cache multiple distinct responses for the same URL when origin responds
// with a `Vary` header and clients provide requests with different values
// for that header.
func TestCacheVary(t *testing.T) {
	ResetBackends(backendsByPriority)

	const reqHeaderName = "CustomThing"
	const respHeaderName = "Reflected-" + reqHeaderName
	headerVals := []string{
		"first distinct",
		"second distinct",
		"third distinct",
	}

	req := NewUniqueEdgeGET(t)

	for _, populateCache := range []bool{true, false} {
		for _, headerVal := range headerVals {
			if populateCache {
				originServer.SwitchHandler(func(w http.ResponseWriter, r *http.Request) {
					w.Header().Set("Vary", reqHeaderName)
					w.Header().Set(respHeaderName, r.Header.Get(reqHeaderName))
				})
			} else {
				originServer.SwitchHandler(func(w http.ResponseWriter, r *http.Request) {
					t.Error("Request should not have made it to origin")
					w.Header().Set(respHeaderName, "not cached")
				})
			}

			req.Header.Set(reqHeaderName, headerVal)
			resp := RoundTripCheckError(t, req)

			if recVal := resp.Header.Get(respHeaderName); recVal != headerVal {
				t.Errorf(
					"Request received wrong %q header. Expected %q, got %q",
					respHeaderName,
					headerVal,
					recVal,
				)
			}
		}
	}
}
<|endoftext|>


<commit_before>package client

import (
	"net/http"
	"reflect"
	"testing"

	"sourcegraph.com/sourcegraph/api_router"
)

func TestRepositoryBuildsService_Get(t *testing.T) {
	setup()
	defer teardown()

	want := &Build{BID: 1}

	var called bool
	mux.HandleFunc(urlPath(t, api_router.RepositoryBuild, map[string]string{"RepoURI": "r.com/x", "BID": "1"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "GET")

		writeJSON(w, want)
	})

	build, _, err := client.Builds.Get(BuildSpec{Repo: RepositorySpec{URI: "r.com/x"}, BID: 1}, nil)
	if err != nil {
		t.Errorf("RepositoryBuilds.Get returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	normalizeBuildTime(build, want)
	if !reflect.DeepEqual(build, want) {
		t.Errorf("RepositoryBuilds.Get returned %+v, want %+v", build, want)
	}
}

func TestRepositoryBuildsService_ListByRepository(t *testing.T) {
	setup()
	defer teardown()

	want := []*Build{{BID: 1}}

	var called bool
	mux.HandleFunc(urlPath(t, api_router.RepositoryBuilds, map[string]string{"RepoURI": "r.com/x"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "GET")

		writeJSON(w, want)
	})

	builds, _, err := client.Builds.ListByRepository(RepositorySpec{URI: "r.com/x"}, nil)
	if err != nil {
		t.Errorf("RepositoryBuilds.ListByRepository returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	normalizeBuildTime(builds...)
	normalizeBuildTime(want...)
	if !reflect.DeepEqual(builds, want) {
		t.Errorf("RepositoryBuilds.ListByRepository returned %+v, want %+v", builds, want)
	}
}

func TestBuildsService_Create(t *testing.T) {
	setup()
	defer teardown()

	config := BuildConfig{Import: true, Queue: true}
	want := &Build{BID: 123, Repo: 456}

	var called bool
	mux.HandleFunc(urlPath(t, api_router.RepositoryBuildsCreate, map[string]string{"RepoURI": "r.com/x"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "POST")
		testBody(t, r, `{"Import":true,"Queue":true}`+"\n")

		writeJSON(w, want)
	})

	build_, _, err := client.Builds.Create(RepositorySpec{URI: "r.com/x"}, config)
	if err != nil {
		t.Errorf("Builds.Create returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	normalizeBuildTime(build_)
	normalizeBuildTime(want)
	if !reflect.DeepEqual(build_, want) {
		t.Errorf("Builds.Create returned %+v, want %+v", build_, want)
	}
}

func normalizeBuildTime(bs ...*Build) {
	for _, b := range bs {
		normalizeTime(&b.CreatedAt)
		normalizeTime(&b.StartedAt.Time)
		normalizeTime(&b.EndedAt.Time)
	}
}
<commit_msg>fix name<commit_after>package client

import (
	"net/http"
	"reflect"
	"testing"

	"sourcegraph.com/sourcegraph/api_router"
)

func TestBuildsService_Get(t *testing.T) {
	setup()
	defer teardown()

	want := &Build{BID: 1}

	var called bool
	mux.HandleFunc(urlPath(t, api_router.RepositoryBuild, map[string]string{"RepoURI": "r.com/x", "BID": "1"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "GET")

		writeJSON(w, want)
	})

	build, _, err := client.Builds.Get(BuildSpec{Repo: RepositorySpec{URI: "r.com/x"}, BID: 1}, nil)
	if err != nil {
		t.Errorf("Builds.Get returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	normalizeBuildTime(build, want)
	if !reflect.DeepEqual(build, want) {
		t.Errorf("Builds.Get returned %+v, want %+v", build, want)
	}
}

func TestBuildsService_ListByRepository(t *testing.T) {
	setup()
	defer teardown()

	want := []*Build{{BID: 1}}

	var called bool
	mux.HandleFunc(urlPath(t, api_router.RepositoryBuilds, map[string]string{"RepoURI": "r.com/x"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "GET")

		writeJSON(w, want)
	})

	builds, _, err := client.Builds.ListByRepository(RepositorySpec{URI: "r.com/x"}, nil)
	if err != nil {
		t.Errorf("Builds.ListByRepository returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	normalizeBuildTime(builds...)
	normalizeBuildTime(want...)
	if !reflect.DeepEqual(builds, want) {
		t.Errorf("Builds.ListByRepository returned %+v, want %+v", builds, want)
	}
}

func TestBuildsService_Create(t *testing.T) {
	setup()
	defer teardown()

	config := BuildConfig{Import: true, Queue: true}
	want := &Build{BID: 123, Repo: 456}

	var called bool
	mux.HandleFunc(urlPath(t, api_router.RepositoryBuildsCreate, map[string]string{"RepoURI": "r.com/x"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "POST")
		testBody(t, r, `{"Import":true,"Queue":true}`+"\n")

		writeJSON(w, want)
	})

	build_, _, err := client.Builds.Create(RepositorySpec{URI: "r.com/x"}, config)
	if err != nil {
		t.Errorf("Builds.Create returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	normalizeBuildTime(build_)
	normalizeBuildTime(want)
	if !reflect.DeepEqual(build_, want) {
		t.Errorf("Builds.Create returned %+v, want %+v", build_, want)
	}
}

func normalizeBuildTime(bs ...*Build) {
	for _, b := range bs {
		normalizeTime(&b.CreatedAt)
		normalizeTime(&b.StartedAt.Time)
		normalizeTime(&b.EndedAt.Time)
	}
}
<|endoftext|>


<commit_before>package client

import (
	"os"
	"testing"
	"time"

	"../net"
	"../redlot"
)

var (
	client *Client
	err    error
)

func TestMain(m *testing.M) {
	// clean env
	os.RemoveAll("/tmp/data")
	os.RemoveAll("/tmp/meta")

	os.Exit(func() (r int) {
		options := &redlot.Options{
			DataPath: "/tmp",
		}

		go net.Serve(":9999", options)

		// Wait 1ms to start server.
		time.Sleep(5e6)

		r = m.Run()

		client.Close()
		os.RemoveAll("/tmp/data")
		os.RemoveAll("/tmp/meta")
		return r
	}())
}

func TestNewClient(t *testing.T) {
	o := &Options{
		Addr: "127.0.0.1:9999",
	}
	client, err = NewClient(o)
	if err != nil || client == nil {
		t.Logf("client: %+v, err: %v\n", client, err)
		t.Fail()
	}
}

func TestCmd(t *testing.T) {
	r := client.Cmd("set", "k", "v")
	if r.State != ReplyOK {
		t.Logf("Cmd [set k v] reply error: %s", r.State)
		t.Fail()
	}
	r = client.Cmd("get", "k")
	if r.State != ReplyOK {
		t.Logf("Cmd [get k] reply state error: %s", r.State)
		t.Fail()
	}
	if len(r.Data) != 1 {
		t.Logf("Cmd [get k] reply length error, expect 1, but %d", len(r.Data))
		t.Fail()
	}
	if string(r.Data[0]) != "v" {
		t.Logf("Cmd [get k] reply data error, expect string \"v\" , but %s", string(r.Data[0]))
		t.Fail()
	}

}
<commit_msg>Test sendBuf.<commit_after>package client

import (
	"bytes"
	"os"
	"testing"
	"time"

	"../net"
	"../redlot"
)

var (
	client *Client
	err    error
)

func TestMain(m *testing.M) {
	// clean env
	os.RemoveAll("/tmp/data")
	os.RemoveAll("/tmp/meta")

	os.Exit(func() (r int) {
		options := &redlot.Options{
			DataPath: "/tmp",
		}

		go net.Serve(":9999", options)

		// Wait 1ms to start server.
		time.Sleep(5e6)

		r = m.Run()

		client.Close()
		os.RemoveAll("/tmp/data")
		os.RemoveAll("/tmp/meta")
		return r
	}())
}

func TestNewClient(t *testing.T) {
	o := &Options{
		Addr: "127.0.0.1:9999",
	}
	client, err = NewClient(o)
	if err != nil || client == nil {
		t.Logf("client: %+v, err: %v\n", client, err)
		t.Fail()
	}
}

func TestCmd(t *testing.T) {
	r := client.Cmd("set", "k", "v")
	if r.State != ReplyOK {
		t.Logf("Cmd [set k v] reply error: %s", r.State)
		t.Fail()
	}
	r = client.Cmd("get", "k")
	if r.State != ReplyOK {
		t.Logf("Cmd [get k] reply state error: %s", r.State)
		t.Fail()
	}
	if len(r.Data) != 1 {
		t.Logf("Cmd [get k] reply length error, expect 1, but %d", len(r.Data))
		t.Fail()
	}
	if string(r.Data[0]) != "v" {
		t.Logf("Cmd [get k] reply data error, expect string \"v\" , but %s", string(r.Data[0]))
		t.Fail()
	}

}

func TestSendBuf(t *testing.T) {
	var args []interface{}
	var buf, expect []byte
	var err error

	// test string arg
	args = []interface{}{
		"set",
		"age",
		"19",
	}
	expect = []byte("*3\r\n$3\r\nset\r\n$3\r\nage\r\n$2\r\n19\r\n")

	buf, err = client.sendBuf(args)
	if err != nil {
		t.Logf("expect err is nil, but %s\n", err.Error())
		t.Fail()
	}
	if !bytes.Equal(buf, expect) {
		t.Logf("expect buf is [% #x], but get [% #x]", buf, expect)
		t.Fail()
	}
}
<|endoftext|>


<commit_before>// Package smtpd implements a basic SMTP server.
package smtpd

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"net"
	"os"
	"regexp"
	"strings"
	"time"
)

var (
	rcptToRE   = regexp.MustCompile(`[Tt][Oo]:<(.+)>`)
	mailFromRE = regexp.MustCompile(`[Ff][Rr][Oo][Mm]:<(.*)>`) // Delivery Status Notifications are sent with "MAIL FROM:<>"
)

// Handler function called upon successful receipt of an email.
type Handler func(remoteAddr net.Addr, from string, to []string, data []byte)

// ListenAndServe listens on the TCP network address addr
// and then calls Serve with handler to handle requests
// on incoming connections.
func ListenAndServe(addr string, handler Handler, appname string, hostname string) error {
	srv := &Server{Addr: addr, Handler: handler, Appname: appname, Hostname: hostname}
	return srv.ListenAndServe()
}

// Server is an SMTP server.
type Server struct {
	Addr     string // TCP address to listen on, defaults to ":25" (all addresses, port 25) if empty
	Handler  Handler
	Appname  string
	Hostname string
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.  If
// srv.Addr is blank, ":25" is used.
func (srv *Server) ListenAndServe() error {
	if srv.Addr == "" {
		srv.Addr = ":25"
	}
	if srv.Appname == "" {
		srv.Appname = "smtpd"
	}
	if srv.Hostname == "" {
		srv.Hostname, _ = os.Hostname()
	}
	ln, err := net.Listen("tcp", srv.Addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

// Serve creates a new SMTP session after a network connection is established.
func (srv *Server) Serve(ln net.Listener) error {
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				log.Printf("%s: Accept error: %v", srv.Appname, err)
				continue
			}
			return err
		}
		session, err := srv.newSession(conn)
		if err != nil {
			continue
		}
		go session.serve()
	}
}

type session struct {
	srv        *Server
	conn       net.Conn
	br         *bufio.Reader
	bw         *bufio.Writer
	remoteIP   string // Remote IP address
	remoteHost string // Remote hostname according to reverse DNS lookup
	remoteName string // Remote hostname as supplied with EHLO
}

// Create new session from connection.
func (srv *Server) newSession(conn net.Conn) (s *session, err error) {
	s = &session{
		srv:  srv,
		conn: conn,
		br:   bufio.NewReader(conn),
		bw:   bufio.NewWriter(conn),
	}
	return
}

// Function called to handle connection requests.
func (s *session) serve() {
	defer s.conn.Close()
	var from string
	var to []string
	var buffer bytes.Buffer

	// Get remote end info for the Received header.
	s.remoteIP, _, _ = net.SplitHostPort(s.conn.RemoteAddr().String())
	names, err := net.LookupAddr(s.remoteIP)
	if err == nil && len(names) > 0 {
		s.remoteHost = names[0]
	} else {
		s.remoteHost = "unknown"
	}

	// Send banner.
	s.writef("220 %s %s SMTP Service ready", s.srv.Hostname, s.srv.Appname)

loop:
	for {
		// Attempt to read a line from the socket.
		// On error, assume the client has gone away i.e. return from serve().
		line, err := s.readLine()
		if err != nil {
			break
		}
		verb, args := s.parseLine(line)

		switch verb {
		case "EHLO", "HELO":
			s.remoteName = args
			s.writef("250 %s greets %s", s.srv.Hostname, s.remoteName)

			// RFC 2821 section 4.1.4 specifies that EHLO has the same effect as RSET.
			from = ""
			to = nil
			buffer.Reset()
		case "MAIL":
			match := mailFromRE.FindStringSubmatch(args)
			if match == nil {
				s.writef("501 Syntax error in parameters or arguments (invalid FROM parameter)")
			} else {
				from = match[1]
				s.writef("250 Ok")
			}
			to = nil
			buffer.Reset()
		case "RCPT":
			if from == "" {
				s.writef("503 Bad sequence of commands (MAIL required before RCPT)")
				break
			}

			match := rcptToRE.FindStringSubmatch(args)
			if match == nil {
				s.writef("501 Syntax error in parameters or arguments (invalid TO parameter)")
			} else {
				// RFC 5321 specifies 100 minimum recipients
				if len(to) == 100 {
					s.writef("452 Too many recipients")
				} else {
					to = append(to, match[1])
					s.writef("250 Ok")
				}
			}
		case "DATA":
			if from == "" || to == nil {
				s.writef("503 Bad sequence of commands (MAIL & RCPT required before DATA)")
				break
			}

			s.writef("354 Start mail input; end with <CR><LF>.<CR><LF>")

			// Attempt to read message body from the socket.
			// On error, assume the client has gone away i.e. return from serve().
			data, err := s.readData()
			if err != nil {
				break loop
			}

			// Create Received header & write message body into buffer.
			buffer.Reset()
			buffer.Write(s.makeHeaders(to))
			buffer.Write(data)
			s.writef("250 Ok: queued")

			// Pass mail on to handler.
			if s.srv.Handler != nil {
				go s.srv.Handler(s.conn.RemoteAddr(), from, to, buffer.Bytes())
			}

			// Reset for next mail.
			from = ""
			to = nil
			buffer.Reset()
		case "QUIT":
			s.writef("221 %s %s SMTP Service closing transmission channel", s.srv.Hostname, s.srv.Appname)
			break loop
		case "RSET":
			s.writef("250 Ok")
			from = ""
			to = nil
			buffer.Reset()
		case "NOOP":
			s.writef("250 Ok")
		case "HELP", "VRFY", "EXPN":
			// See RFC 5321 section 4.2.4 for usage of 500 & 502 reply codes
			s.writef("502 Command not implemented")
		default:
			// See RFC 5321 section 4.2.4 for usage of 500 & 502 reply codes
			s.writef("500 Syntax error, command unrecognized")
		}
	}
}

// Wrapper function for writing a complete line to the socket.
func (s *session) writef(format string, args ...interface{}) {
	fmt.Fprintf(s.bw, format+"\r\n", args...)
	s.bw.Flush()
}

// Read a complete line from the socket.
func (s *session) readLine() (string, error) {
	line, err := s.br.ReadString('\n')
	if err != nil {
		return "", err
	}
	line = strings.TrimSpace(line) // Strip trailing \r\n
	return line, err
}

// Parse a line read from the socket.
func (s *session) parseLine(line string) (verb string, args string) {
	if idx := strings.Index(line, " "); idx != -1 {
		verb = strings.ToUpper(line[:idx])
		args = strings.TrimSpace(line[idx+1:])
	} else {
		verb = strings.ToUpper(line)
		args = ""
	}
	return verb, args
}

// Read the message data following a DATA command.
func (s *session) readData() ([]byte, error) {
	var data []byte
	for {
		slice, err := s.br.ReadSlice('\n')
		if err != nil {
			return nil, err
		}
		// Handle end of data denoted by lone period (\r\n.\r\n)
		if bytes.Equal(slice, []byte(".\r\n")) {
			break
		}
		// Remove leading period (RFC 5321 section 4.5.2)
		if slice[0] == '.' {
			slice = slice[1:]
		}
		data = append(data, slice...)
	}
	return data, nil
}

// Create the Received header to comply with RFC 2821 section 3.8.2.
// TODO: Work out what to do with multiple to addresses.
func (s *session) makeHeaders(to []string) []byte {
	var buffer bytes.Buffer
	now := time.Now().Format("Mon, _2 Jan 2006 15:04:05 -0700 (MST)")
	buffer.WriteString(fmt.Sprintf("Received: from %s (%s [%s])\r\n", s.remoteName, s.remoteHost, s.remoteIP))
	buffer.WriteString(fmt.Sprintf("        by %s (%s) with SMTP\r\n", s.srv.Hostname, s.srv.Appname))
	buffer.WriteString(fmt.Sprintf("        for <%s>; %s\r\n", to[0], now))
	return buffer.Bytes()
}
<commit_msg>Use ReadBytes instead of ReadSlice because the docs suggested it.<commit_after>// Package smtpd implements a basic SMTP server.
package smtpd

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"net"
	"os"
	"regexp"
	"strings"
	"time"
)

var (
	rcptToRE   = regexp.MustCompile(`[Tt][Oo]:<(.+)>`)
	mailFromRE = regexp.MustCompile(`[Ff][Rr][Oo][Mm]:<(.*)>`) // Delivery Status Notifications are sent with "MAIL FROM:<>"
)

// Handler function called upon successful receipt of an email.
type Handler func(remoteAddr net.Addr, from string, to []string, data []byte)

// ListenAndServe listens on the TCP network address addr
// and then calls Serve with handler to handle requests
// on incoming connections.
func ListenAndServe(addr string, handler Handler, appname string, hostname string) error {
	srv := &Server{Addr: addr, Handler: handler, Appname: appname, Hostname: hostname}
	return srv.ListenAndServe()
}

// Server is an SMTP server.
type Server struct {
	Addr     string // TCP address to listen on, defaults to ":25" (all addresses, port 25) if empty
	Handler  Handler
	Appname  string
	Hostname string
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.  If
// srv.Addr is blank, ":25" is used.
func (srv *Server) ListenAndServe() error {
	if srv.Addr == "" {
		srv.Addr = ":25"
	}
	if srv.Appname == "" {
		srv.Appname = "smtpd"
	}
	if srv.Hostname == "" {
		srv.Hostname, _ = os.Hostname()
	}
	ln, err := net.Listen("tcp", srv.Addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

// Serve creates a new SMTP session after a network connection is established.
func (srv *Server) Serve(ln net.Listener) error {
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				log.Printf("%s: Accept error: %v", srv.Appname, err)
				continue
			}
			return err
		}
		session, err := srv.newSession(conn)
		if err != nil {
			continue
		}
		go session.serve()
	}
}

type session struct {
	srv        *Server
	conn       net.Conn
	br         *bufio.Reader
	bw         *bufio.Writer
	remoteIP   string // Remote IP address
	remoteHost string // Remote hostname according to reverse DNS lookup
	remoteName string // Remote hostname as supplied with EHLO
}

// Create new session from connection.
func (srv *Server) newSession(conn net.Conn) (s *session, err error) {
	s = &session{
		srv:  srv,
		conn: conn,
		br:   bufio.NewReader(conn),
		bw:   bufio.NewWriter(conn),
	}
	return
}

// Function called to handle connection requests.
func (s *session) serve() {
	defer s.conn.Close()
	var from string
	var to []string
	var buffer bytes.Buffer

	// Get remote end info for the Received header.
	s.remoteIP, _, _ = net.SplitHostPort(s.conn.RemoteAddr().String())
	names, err := net.LookupAddr(s.remoteIP)
	if err == nil && len(names) > 0 {
		s.remoteHost = names[0]
	} else {
		s.remoteHost = "unknown"
	}

	// Send banner.
	s.writef("220 %s %s SMTP Service ready", s.srv.Hostname, s.srv.Appname)

loop:
	for {
		// Attempt to read a line from the socket.
		// On error, assume the client has gone away i.e. return from serve().
		line, err := s.readLine()
		if err != nil {
			break
		}
		verb, args := s.parseLine(line)

		switch verb {
		case "EHLO", "HELO":
			s.remoteName = args
			s.writef("250 %s greets %s", s.srv.Hostname, s.remoteName)

			// RFC 2821 section 4.1.4 specifies that EHLO has the same effect as RSET.
			from = ""
			to = nil
			buffer.Reset()
		case "MAIL":
			match := mailFromRE.FindStringSubmatch(args)
			if match == nil {
				s.writef("501 Syntax error in parameters or arguments (invalid FROM parameter)")
			} else {
				from = match[1]
				s.writef("250 Ok")
			}
			to = nil
			buffer.Reset()
		case "RCPT":
			if from == "" {
				s.writef("503 Bad sequence of commands (MAIL required before RCPT)")
				break
			}

			match := rcptToRE.FindStringSubmatch(args)
			if match == nil {
				s.writef("501 Syntax error in parameters or arguments (invalid TO parameter)")
			} else {
				// RFC 5321 specifies 100 minimum recipients
				if len(to) == 100 {
					s.writef("452 Too many recipients")
				} else {
					to = append(to, match[1])
					s.writef("250 Ok")
				}
			}
		case "DATA":
			if from == "" || to == nil {
				s.writef("503 Bad sequence of commands (MAIL & RCPT required before DATA)")
				break
			}

			s.writef("354 Start mail input; end with <CR><LF>.<CR><LF>")

			// Attempt to read message body from the socket.
			// On error, assume the client has gone away i.e. return from serve().
			data, err := s.readData()
			if err != nil {
				break loop
			}

			// Create Received header & write message body into buffer.
			buffer.Reset()
			buffer.Write(s.makeHeaders(to))
			buffer.Write(data)
			s.writef("250 Ok: queued")

			// Pass mail on to handler.
			if s.srv.Handler != nil {
				go s.srv.Handler(s.conn.RemoteAddr(), from, to, buffer.Bytes())
			}

			// Reset for next mail.
			from = ""
			to = nil
			buffer.Reset()
		case "QUIT":
			s.writef("221 %s %s SMTP Service closing transmission channel", s.srv.Hostname, s.srv.Appname)
			break loop
		case "RSET":
			s.writef("250 Ok")
			from = ""
			to = nil
			buffer.Reset()
		case "NOOP":
			s.writef("250 Ok")
		case "HELP", "VRFY", "EXPN":
			// See RFC 5321 section 4.2.4 for usage of 500 & 502 reply codes
			s.writef("502 Command not implemented")
		default:
			// See RFC 5321 section 4.2.4 for usage of 500 & 502 reply codes
			s.writef("500 Syntax error, command unrecognized")
		}
	}
}

// Wrapper function for writing a complete line to the socket.
func (s *session) writef(format string, args ...interface{}) {
	fmt.Fprintf(s.bw, format+"\r\n", args...)
	s.bw.Flush()
}

// Read a complete line from the socket.
func (s *session) readLine() (string, error) {
	line, err := s.br.ReadString('\n')
	if err != nil {
		return "", err
	}
	line = strings.TrimSpace(line) // Strip trailing \r\n
	return line, err
}

// Parse a line read from the socket.
func (s *session) parseLine(line string) (verb string, args string) {
	if idx := strings.Index(line, " "); idx != -1 {
		verb = strings.ToUpper(line[:idx])
		args = strings.TrimSpace(line[idx+1:])
	} else {
		verb = strings.ToUpper(line)
		args = ""
	}
	return verb, args
}

// Read the message data following a DATA command.
func (s *session) readData() ([]byte, error) {
	var data []byte
	for {
		line, err := s.br.ReadBytes('\n')
		if err != nil {
			return nil, err
		}
		// Handle end of data denoted by lone period (\r\n.\r\n)
		if bytes.Equal(line, []byte(".\r\n")) {
			break
		}
		// Remove leading period (RFC 5321 section 4.5.2)
		if line[0] == '.' {
			line = line[1:]
		}
		data = append(data, line...)

	}
	return data, nil
}

// Create the Received header to comply with RFC 2821 section 3.8.2.
// TODO: Work out what to do with multiple to addresses.
func (s *session) makeHeaders(to []string) []byte {
	var buffer bytes.Buffer
	now := time.Now().Format("Mon, _2 Jan 2006 15:04:05 -0700 (MST)")
	buffer.WriteString(fmt.Sprintf("Received: from %s (%s [%s])\r\n", s.remoteName, s.remoteHost, s.remoteIP))
	buffer.WriteString(fmt.Sprintf("        by %s (%s) with SMTP\r\n", s.srv.Hostname, s.srv.Appname))
	buffer.WriteString(fmt.Sprintf("        for <%s>; %s\r\n", to[0], now))
	return buffer.Bytes()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))

	if err != nil {
		//do nothing
	}
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "putState":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		value := []byte(args[1])
		err := stub.PutState(key, value)
		if err != nil {
			return nil, err
		}
		return value, err
	}

	return nil, nil
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "getTxID":
		txID := stub.GetTxID()
		result := []byte(txID)
		return result, nil
	case "getTxTimestamp":
		time, err := stub.GetTxTimestamp()
		result := []byte(time.String()) //时间转换为字符串，time.String()
		return result, err
	case "getStringArgs":
		strList := stub.GetStringArgs()
		var result string
		for index := 0; index < len(strList); index++ {
			result += "***" + strList[index] + "***"
		}
		return []byte(result), nil

	case "getState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		result, err := stub.GetState(key)

		if err != nil {
			return nil, err
		}
		return result, err
	}
	return nil, nil
}
<commit_msg>demo05.go<commit_after>package main

import (
	"errors"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))

	if err != nil {
		//do nothing
	}
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "putState":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		value := []byte(args[1])
		err := stub.PutState(key, value)
		if err != nil {
			return nil, err
		}
		return value, err
	case "delState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		err := stub.DelState(key)
		if err != nil {
			return nil, err
		}
		result := key + "has been deleted"
		return []byte(result), err
	}

	return nil, nil
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "getTxID":
		txID := stub.GetTxID()
		result := []byte(txID)
		return result, nil
	case "getTxTimestamp":
		time, err := stub.GetTxTimestamp()
		result := []byte(time.String()) //时间转换为字符串，time.String()
		return result, err
	case "getStringArgs":
		strList := stub.GetStringArgs()
		var result string
		for index := 0; index < len(strList); index++ {
			result += "***" + strList[index] + "***"
		}
		return []byte(result), nil

	case "getState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		result, err := stub.GetState(key)

		if err != nil {
			return nil, err
		}
		return result, err
	}
	return nil, nil
}
<|endoftext|>


<commit_before>package plugo

import (
	"errors"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/spf13/cast"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var keyDelim string = "."

// RawConfig is essentially repository for configurations
type RawConfig map[string]interface{}

func (c RawConfig) searchMap(source map[string]interface{}, path []string) interface{} {

	if len(path) == 0 {
		return source
	}

	if next, ok := source[path[0]]; ok {
		switch next.(type) {
		case map[interface{}]interface{}:
			return c.searchMap(cast.ToStringMap(next), path[1:])
		case map[string]interface{}:
			// Type assertion is safe here since it is only reached
			// if the type of `next` is the same as the type being asserted
			return c.searchMap(next.(map[string]interface{}), path[1:])
		default:
			return next
		}
	} else {
		return nil
	}
}

// Given a key, find the value
func (c RawConfig) find(key string) interface{} {
	var val interface{}
	var exists bool

	val, exists = c[key]
	if exists {
		return val
	}
	return nil
}

// Get can retrieve any value given the key to use
// Get returns an interface. For a specific value use one of the Get____ methods.
func (c RawConfig) Get(key string) interface{} {
	path := strings.Split(key, keyDelim)

	val := c.find(strings.ToLower(key))

	if val == nil {
		source := c.find(path[0])
		if source == nil {
			return nil
		}

		if reflect.TypeOf(source).Kind() == reflect.Map {
			val = c.searchMap(cast.ToStringMap(source), path[1:])
		}
	}

	switch val.(type) {
	case bool:
		return cast.ToBool(val)
	case string:
		return cast.ToString(val)
	case int64, int32, int16, int8, int:
		return cast.ToInt(val)
	case float64, float32:
		return cast.ToFloat64(val)
	case time.Time:
		return cast.ToTime(val)
	case time.Duration:
		return cast.ToDuration(val)
	case []string:
		return val
	}
	return val
}

// Returns the value associated with the key as a string
func (c RawConfig) GetString(key string) string {
	return cast.ToString(c.Get(key))
}

// Returns the value associated with the key asa boolean
func (c RawConfig) GetBool(key string) bool {
	return cast.ToBool(c.Get(key))
}

// Returns the value associated with the key as an integer
func (c RawConfig) GetInt(key string) int {
	return cast.ToInt(c.Get(key))
}

// Returns the value associated with the key as a float64
func (c RawConfig) GetFloat64(key string) float64 {
	return cast.ToFloat64(c.Get(key))
}

// Returns the value associated with the key as time
func (c RawConfig) GetTime(key string) time.Time {
	return cast.ToTime(c.Get(key))
}

// Returns the value associated with the key as a duration
func (c RawConfig) GetDuration(key string) time.Duration {
	return cast.ToDuration(c.Get(key))
}

// Returns the value associated with the key as a slice of strings
func (c RawConfig) GetStringSlice(key string) []string {
	return cast.ToStringSlice(c.Get(key))
}

// Returns the value associated with the key as a map of interfaces
func (c RawConfig) GetStringMap(key string) map[string]interface{} {
	return cast.ToStringMap(c.Get(key))
}

// Returns the value associated with the key as a map of strings
func (c RawConfig) GetStringMapString(key string) map[string]string {
	return cast.ToStringMapString(c.Get(key))
}

// Returns the value associated with the key as a map to a slice of strings.
func (c RawConfig) GetStringMapStringSlice(key string) map[string][]string {
	return cast.ToStringMapStringSlice(c.Get(key))
}

type PluginConfig struct {
	Name   string    // Used to lookup the syptom in the plugin registry
	Config RawConfig // Provided to the plugin on load to validate
}

type ConfigLoader struct{}

func (cl *ConfigLoader) Load(data []byte, config interface{}) error {
	err := yaml.Unmarshal(data, config)
	if err != nil {
		return err
	}
	return nil
}
func (cl *ConfigLoader) LoadFromFile(filename string, config interface{}) error {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	return cl.Load(data, config)
}
func (cl *ConfigLoader) ApplyConfig(config interface{}, i interface{}) error {
	return mapstructure.Decode(config, i)
}

// Give me a struct with field tags and i'll validate you, set defaults, etc.
func (cl *ConfigLoader) Validate(iface interface{}) error {
	iValue := reflect.ValueOf(iface).Elem().Interface()
	st := reflect.TypeOf(iValue)
	ps := reflect.ValueOf(iValue)

	// Loop all fields, set their default values if they have them and are empty
	// Fail if mandatory fields are not set and have no value

	for i := 0; i < ps.NumField(); i++ {
		f := st.Field(i)
		field := ps.FieldByName(f.Name)
		dataKind := field.Kind()
		var fValue interface{}

		// Is an exported field, needs to
		// start with an uppercase letter
		fValue = getFieldValue(f.Name, field)
		if !isExportedField(f.Name) {
			continue
		}

		// Fail fast: bad regex on field
		// Validate regex
		regexStr := f.Tag.Get("regex")
		var regex *regexp.Regexp
		var err error
		if regexStr != "" && dataKind != reflect.String {
			return errors.New(fmt.Sprintf("Field '%s' has invalid 'regex' Tag '%s'. Regex can only be applied to string types", f.Name, regexStr))
		} else if regexStr != "" {
			regex, err = regexp.Compile(regexStr)
			if err != nil {
				return errors.New(fmt.Sprintf("Field '%s' has invalid regex '%s': %s", f.Name, regexStr, err.Error()))
			}
		}

		if isZero(field) {
			defaultVal := f.Tag.Get("default")
			if cast.ToBool(f.Tag.Get("required")) == true && defaultVal == "" {
				return errors.New(fmt.Sprintf("Mandatory field '%s' has not been set, and has no provided default", f.Name))
			}

			field = reflect.ValueOf(iface).Elem().FieldByName(f.Name)
			switch dataKind {
			case reflect.Bool:
				field.SetBool(cast.ToBool(defaultVal))
			case reflect.String:
				field.SetString(defaultVal)
			case reflect.Slice, reflect.Array:
				_type := field.Type().Elem()
				_newArr := strings.Split(f.Tag.Get("default"), ",")

				switch _type {
				case reflect.TypeOf(""):
					field.Set(reflect.ValueOf(_newArr))
				case reflect.TypeOf(1):
					// Convert array to int
					intArray := make([]int, len(_newArr))
					var err error
					for j, val := range _newArr {
						intArray[j], err = strconv.Atoi(val)
						if err != nil {

							return errors.New(fmt.Sprintf("Error creating default array for field '$s': %v\n", f.Name, err))
						}
					}
					field.Set(reflect.ValueOf(intArray))
				default:
					return errors.New(fmt.Sprintf("Unsupported slice default type: %v\n", _type))
				}
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				s, err := strconv.ParseInt(defaultVal, 10, 64)
				if err != nil {
					return err
				}
				field.SetInt(s)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				s, err := strconv.ParseUint(defaultVal, 10, 64)
				if err != nil {
					return err
				}
				field.SetUint(s)
			default:
				return errors.New(fmt.Sprintf("Unsupported field '%s' of type: %s", f.Name, dataKind))
			}

		} else {
			if regex != nil {
				if !regex.MatchString(fValue.(string)) {
					return errors.New(fmt.Sprintf("Regex validation failed on field '%s'. /%s/ does not match '%s'", f.Name, regexStr, fValue.(string)))
				}
			}

		}
	}
	return nil
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Func, reflect.Map, reflect.Slice:
		return v.IsNil()
	case reflect.Array:
		z := true
		for i := 0; i < v.Len(); i++ {
			z = z && isZero(v.Index(i))
		}
		return z
	case reflect.Struct:
		z := true
		for i := 0; i < v.NumField(); i++ {
			z = z && isZero(v.Field(i))
		}
		return z
	}
	// Compare other types directly:
	z := reflect.Zero(v.Type())
	return v.Interface() == z.Interface()
}
func isExportedField(name string) bool {
	char := name[0]
	if char >= 65 && char <= 90 {
		return true
	}
	return false
}
func getFieldValue(name string, field reflect.Value) interface{} {
	var val interface{}
	if isExportedField(name) {
		val = field.Interface()
	}
	return val
}
<commit_msg>Logic error: was accidentally capturing all fields - even if not mandatory or don't have a default - so things like non-mandatory structs would fail validation if left out<commit_after>package plugo

import (
	"errors"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/spf13/cast"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var keyDelim string = "."

// RawConfig is essentially repository for configurations
type RawConfig map[string]interface{}

func (c RawConfig) searchMap(source map[string]interface{}, path []string) interface{} {

	if len(path) == 0 {
		return source
	}

	if next, ok := source[path[0]]; ok {
		switch next.(type) {
		case map[interface{}]interface{}:
			return c.searchMap(cast.ToStringMap(next), path[1:])
		case map[string]interface{}:
			// Type assertion is safe here since it is only reached
			// if the type of `next` is the same as the type being asserted
			return c.searchMap(next.(map[string]interface{}), path[1:])
		default:
			return next
		}
	} else {
		return nil
	}
}

// Given a key, find the value
func (c RawConfig) find(key string) interface{} {
	var val interface{}
	var exists bool

	val, exists = c[key]
	if exists {
		return val
	}
	return nil
}

// Get can retrieve any value given the key to use
// Get returns an interface. For a specific value use one of the Get____ methods.
func (c RawConfig) Get(key string) interface{} {
	path := strings.Split(key, keyDelim)

	val := c.find(strings.ToLower(key))

	if val == nil {
		source := c.find(path[0])
		if source == nil {
			return nil
		}

		if reflect.TypeOf(source).Kind() == reflect.Map {
			val = c.searchMap(cast.ToStringMap(source), path[1:])
		}
	}

	switch val.(type) {
	case bool:
		return cast.ToBool(val)
	case string:
		return cast.ToString(val)
	case int64, int32, int16, int8, int:
		return cast.ToInt(val)
	case float64, float32:
		return cast.ToFloat64(val)
	case time.Time:
		return cast.ToTime(val)
	case time.Duration:
		return cast.ToDuration(val)
	case []string:
		return val
	}
	return val
}

// Returns the value associated with the key as a string
func (c RawConfig) GetString(key string) string {
	return cast.ToString(c.Get(key))
}

// Returns the value associated with the key asa boolean
func (c RawConfig) GetBool(key string) bool {
	return cast.ToBool(c.Get(key))
}

// Returns the value associated with the key as an integer
func (c RawConfig) GetInt(key string) int {
	return cast.ToInt(c.Get(key))
}

// Returns the value associated with the key as a float64
func (c RawConfig) GetFloat64(key string) float64 {
	return cast.ToFloat64(c.Get(key))
}

// Returns the value associated with the key as time
func (c RawConfig) GetTime(key string) time.Time {
	return cast.ToTime(c.Get(key))
}

// Returns the value associated with the key as a duration
func (c RawConfig) GetDuration(key string) time.Duration {
	return cast.ToDuration(c.Get(key))
}

// Returns the value associated with the key as a slice of strings
func (c RawConfig) GetStringSlice(key string) []string {
	return cast.ToStringSlice(c.Get(key))
}

// Returns the value associated with the key as a map of interfaces
func (c RawConfig) GetStringMap(key string) map[string]interface{} {
	return cast.ToStringMap(c.Get(key))
}

// Returns the value associated with the key as a map of strings
func (c RawConfig) GetStringMapString(key string) map[string]string {
	return cast.ToStringMapString(c.Get(key))
}

// Returns the value associated with the key as a map to a slice of strings.
func (c RawConfig) GetStringMapStringSlice(key string) map[string][]string {
	return cast.ToStringMapStringSlice(c.Get(key))
}

type PluginConfig struct {
	Name   string    // Used to lookup the syptom in the plugin registry
	Config RawConfig // Provided to the plugin on load to validate
}

type ConfigLoader struct{}

func (cl *ConfigLoader) Load(data []byte, config interface{}) error {
	err := yaml.Unmarshal(data, config)
	if err != nil {
		return err
	}
	return nil
}
func (cl *ConfigLoader) LoadFromFile(filename string, config interface{}) error {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	return cl.Load(data, config)
}
func (cl *ConfigLoader) ApplyConfig(config interface{}, i interface{}) error {
	return mapstructure.Decode(config, i)
}

// Give me a struct with field tags and i'll validate you, set defaults, etc.
func (cl *ConfigLoader) Validate(iface interface{}) error {
	iValue := reflect.ValueOf(iface).Elem().Interface()
	st := reflect.TypeOf(iValue)
	ps := reflect.ValueOf(iValue)

	// Loop all fields, set their default values if they have them and are empty
	// Fail if mandatory fields are not set and have no value

	for i := 0; i < ps.NumField(); i++ {
		f := st.Field(i)
		field := ps.FieldByName(f.Name)
		dataKind := field.Kind()
		var fValue interface{}

		// Is an exported field, needs to
		// start with an uppercase letter
		fValue = getFieldValue(f.Name, field)
		if !isExportedField(f.Name) {
			continue
		}

		// Fail fast: bad regex on field
		// Validate regex
		regexStr := f.Tag.Get("regex")
		var regex *regexp.Regexp
		var err error
		if regexStr != "" && dataKind != reflect.String {
			return errors.New(fmt.Sprintf("Field '%s' has invalid 'regex' Tag '%s'. Regex can only be applied to string types", f.Name, regexStr))
		} else if regexStr != "" {
			regex, err = regexp.Compile(regexStr)
			if err != nil {
				return errors.New(fmt.Sprintf("Field '%s' has invalid regex '%s': %s", f.Name, regexStr, err.Error()))
			}
		}

		required := cast.ToBool(f.Tag.Get("required")) == true
		defaultVal := f.Tag.Get("default")

		if (required == true || defaultVal != "") && isZero(field) {
			if required == true && defaultVal == "" && isZero(field) {
				return errors.New(fmt.Sprintf("Mandatory field '%s' has not been set, and has no provided default", f.Name))
			}

			field = reflect.ValueOf(iface).Elem().FieldByName(f.Name)
			switch dataKind {
			case reflect.Bool:
				field.SetBool(cast.ToBool(defaultVal))
			case reflect.String:
				field.SetString(defaultVal)
			case reflect.Slice, reflect.Array:
				_type := field.Type().Elem()
				_newArr := strings.Split(f.Tag.Get("default"), ",")

				switch _type {
				case reflect.TypeOf(""):
					field.Set(reflect.ValueOf(_newArr))
				case reflect.TypeOf(1):
					// Convert array to int
					intArray := make([]int, len(_newArr))
					var err error
					for j, val := range _newArr {
						intArray[j], err = strconv.Atoi(val)
						if err != nil {

							return errors.New(fmt.Sprintf("Error creating default array for field '$s': %v\n", f.Name, err))
						}
					}
					field.Set(reflect.ValueOf(intArray))
				default:
					return errors.New(fmt.Sprintf("Unsupported slice default type: %v\n", _type))
				}
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				s, err := strconv.ParseInt(defaultVal, 10, 64)
				if err != nil {
					return err
				}
				field.SetInt(s)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				s, err := strconv.ParseUint(defaultVal, 10, 64)
				if err != nil {
					return err
				}
				field.SetUint(s)
			default:
				return errors.New(fmt.Sprintf("Unsupported field '%s' of type: %s", f.Name, dataKind))
			}

		} else {
			if regex != nil {
				if !regex.MatchString(fValue.(string)) {
					return errors.New(fmt.Sprintf("Regex validation failed on field '%s'. /%s/ does not match '%s'", f.Name, regexStr, fValue.(string)))
				}
			}

		}
	}
	return nil
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Func, reflect.Map, reflect.Slice:
		return v.IsNil()
	case reflect.Array:
		z := true
		for i := 0; i < v.Len(); i++ {
			z = z && isZero(v.Index(i))
		}
		return z
	case reflect.Struct:
		z := true
		for i := 0; i < v.NumField(); i++ {
			z = z && isZero(v.Field(i))
		}
		return z
	}
	// Compare other types directly:
	z := reflect.Zero(v.Type())
	return v.Interface() == z.Interface()
}
func isExportedField(name string) bool {
	char := name[0]
	if char >= 65 && char <= 90 {
		return true
	}
	return false
}
func getFieldValue(name string, field reflect.Value) interface{} {
	var val interface{}
	if isExportedField(name) {
		val = field.Interface()
	}
	return val
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"path"
	"path/filepath"
	"syscall"

	"github.com/calavera/dkvolume"
)

type beegfsDriver struct {
	root string
}

func newBeeGFSDriver(root string) beegfsDriver {
	d := beegfsDriver{
		root: root,
	}

	return d
}

func (b beegfsDriver) Create(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Create: %s, %v", r.Name, r.Options)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot create volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return dkvolume.Response{Err: emsg}
	}

	if err := createDest(dest); err != nil {
		return dkvolume.Response{Err: err.Error()}
	}

	return dkvolume.Response{}
}

func (b beegfsDriver) Remove(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Remove: %s", r.Name)
	return dkvolume.Response{}
}

func (b beegfsDriver) Path(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Path: %s", r.Name)
	return dkvolume.Response{Mountpoint: volumeDir(b, r)}
}

func (b beegfsDriver) Mount(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Mount: %s", r.Name)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot mount volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return dkvolume.Response{Err: emsg}
	}

	return dkvolume.Response{Mountpoint: dest}
}

func (b beegfsDriver) Unmount(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Unmount: %s", r.Name)
	return dkvolume.Response{}
}

func volumeDir(b beegfsDriver, r dkvolume.Request) string {
	return filepath.Join(b.root, r.Name)
}

// Check if the parent directory (where the volume will be created)
// is of type 'beegfs' using the BEEGFS_MAGIC value.
func isbeegfs(volumepath string) bool {
	log.Debugf("isbeegfs() for %s", volumepath)
	stat := syscall.Statfs_t{}
	err := syscall.Statfs(path.Dir(volumepath), &stat)
	if err != nil {
		log.Errorf("Could not determine filesystem type for %s: %s", volumepath, err)
		return false
	}

        // BEEGFS_MAGIC 0x19830326
	return stat.Type == int64(428016422)
}

func createDest(dest string) error {
	fstat, err := os.Lstat(dest)

	if os.IsNotExist(err) {
		if err := os.MkdirAll(dest, 0755); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	if fstat != nil && !fstat.IsDir() {
		return fmt.Errorf("%v already exist and it's not a directory", dest)
	}

	return nil
}
<commit_msg>add reminder comment to $self<commit_after>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"path"
	"path/filepath"
	"syscall"

	"github.com/calavera/dkvolume"
)

type beegfsDriver struct {
	root string
}

func newBeeGFSDriver(root string) beegfsDriver {
	d := beegfsDriver{
		root: root,
	}

	return d
}

func (b beegfsDriver) Create(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Create: %s, %v", r.Name, r.Options)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot create volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return dkvolume.Response{Err: emsg}
	}

	if err := createDest(dest); err != nil {
		return dkvolume.Response{Err: err.Error()}
	}

	return dkvolume.Response{}
}

func (b beegfsDriver) Remove(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Remove: %s", r.Name)
	return dkvolume.Response{}
}

func (b beegfsDriver) Path(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Path: %s", r.Name)
	return dkvolume.Response{Mountpoint: volumeDir(b, r)}
}

func (b beegfsDriver) Mount(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Mount: %s", r.Name)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot mount volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return dkvolume.Response{Err: emsg}
	}

	return dkvolume.Response{Mountpoint: dest}
}

func (b beegfsDriver) Unmount(r dkvolume.Request) dkvolume.Response {
	log.Debugf("Unmount: %s", r.Name)
	return dkvolume.Response{}
}

func volumeDir(b beegfsDriver, r dkvolume.Request) string {
	// We should use a per volume type to keep track of their individual roots.
	// Then we can use r.Options["beegfsbase"]
	return filepath.Join(b.root, r.Name)
}

// Check if the parent directory (where the volume will be created)
// is of type 'beegfs' using the BEEGFS_MAGIC value.
func isbeegfs(volumepath string) bool {
	log.Debugf("isbeegfs() for %s", volumepath)
	stat := syscall.Statfs_t{}
	err := syscall.Statfs(path.Dir(volumepath), &stat)
	if err != nil {
		log.Errorf("Could not determine filesystem type for %s: %s", volumepath, err)
		return false
	}

	log.Debugf("Type for %s: %d", volumepath, stat.Type)

	// BEEGFS_MAGIC 0x19830326
	return stat.Type == int64(428016422)
}

func createDest(dest string) error {
	fstat, err := os.Lstat(dest)

	if os.IsNotExist(err) {
		if err := os.MkdirAll(dest, 0755); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	if fstat != nil && !fstat.IsDir() {
		return fmt.Errorf("%v already exist and it's not a directory", dest)
	}

	return nil
}
<|endoftext|>