<commit_before>package commands

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"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/tasklog"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tq"
	"github.com/rubyist/tracerx"
)

func uploadForRefUpdates(ctx *uploadContext, updates []*git.RefUpdate, pushAll bool) error {
	gitscanner, err := ctx.buildGitScanner()
	if err != nil {
		return err
	}

	defer func() {
		gitscanner.Close()
		ctx.ReportErrors()
	}()

	verifyLocksForUpdates(ctx.lockVerifier, updates)
	for _, update := range updates {
		// initialized here to prevent looped defer
		q := ctx.NewQueue(
			tq.RemoteRef(update.Right()),
		)
		err := uploadLeftOrAll(gitscanner, ctx, q, update, pushAll)
		ctx.CollectErrors(q)

		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("ref %s:", update.Left().Name))
		}
	}

	return nil
}

func uploadLeftOrAll(g *lfs.GitScanner, ctx *uploadContext, q *tq.TransferQueue, update *git.RefUpdate, pushAll bool) error {
	cb := ctx.gitScannerCallback(q)
	if pushAll {
		if err := g.ScanRefWithDeleted(update.LeftCommitish(), cb); err != nil {
			return err
		}
	} else {
		if err := g.ScanLeftToRemote(update.LeftCommitish(), cb); err != nil {
			return err
		}
	}
	return ctx.scannerError()
}

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

	logger *tasklog.Logger
	meter  *tq.Meter

	committerName  string
	committerEmail string

	lockVerifier *lockVerifier

	// allowMissing specifies whether pushes containing missing/corrupt
	// pointers should allow pushing Git blobs
	allowMissing bool

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

	// filename => oid
	missing   map[string]string
	corrupt   map[string]string
	otherErrs []error
}

func newUploadContext(dryRun bool) *uploadContext {
	remote := cfg.PushRemote()
	manifest := getTransferManifestOperationRemote("upload", remote)
	ctx := &uploadContext{
		Remote:       remote,
		Manifest:     manifest,
		DryRun:       dryRun,
		uploadedOids: tools.NewStringSet(),
		gitfilter:    lfs.NewGitFilter(cfg),
		lockVerifier: newLockVerifier(manifest),
		allowMissing: cfg.Git.Bool("lfs.allowincompletepush", false),
		missing:      make(map[string]string),
		corrupt:      make(map[string]string),
		otherErrs:    make([]error, 0),
	}

	var sink io.Writer = os.Stdout
	if dryRun {
		sink = ioutil.Discard
	}

	ctx.logger = tasklog.NewLogger(sink,
		tasklog.ForceProgress(cfg.ForceProgress()),
	)
	ctx.meter = buildProgressMeter(ctx.DryRun, tq.Upload)
	ctx.logger.Enqueue(ctx.meter)
	ctx.committerName, ctx.committerEmail = cfg.CurrentCommitter()
	return ctx
}

func (c *uploadContext) NewQueue(options ...tq.Option) *tq.TransferQueue {
	return tq.NewTransferQueue(tq.Upload, c.Manifest, c.Remote, append(options,
		tq.DryRun(c.DryRun),
		tq.WithProgress(c.meter),
	)...)
}

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(nil)
	gitscanner.FoundLockable = func(n string) { c.lockVerifier.LockedByThem(n) }
	gitscanner.PotentialLockables = c.lockVerifier
	return gitscanner, gitscanner.RemoteForPush(c.Remote)
}

func (c *uploadContext) gitScannerCallback(tqueue *tq.TransferQueue) func(*lfs.WrappedPointer, error) {
	return func(p *lfs.WrappedPointer, err error) {
		if err != nil {
			c.addScannerError(err)
		} else {
			c.UploadPointers(tqueue, p)
		}
	}
}

// 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) []*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 c.lockVerifier.LockedByThem(p.Name) {
			// If the verification state is enabled, this failed
			// locks verification means that the push should fail.
			//
			// If the state is disabled, the verification error is
			// silent and the user can upload.
			//
			// If the state is undefined, the verification error is
			// sent as a warning and the user can upload.
			canUpload = !c.lockVerifier.Enabled()
		}

		c.lockVerifier.LockedByUs(p.Name)

		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 uploadables
}

func (c *uploadContext) UploadPointers(q *tq.TransferQueue, 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
	}

	pointers := c.prepareUpload(unfiltered...)
	for _, p := range pointers {
		t, err := c.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) CollectErrors(tqueue *tq.TransferQueue) {
	tqueue.Wait()

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

func (c *uploadContext) ReportErrors() {
	c.meter.Finish()

	for _, err := range c.otherErrs {
		FullError(err)
	}

	if len(c.missing) > 0 || len(c.corrupt) > 0 {
		var action string
		if c.allowMissing {
			action = "missing objects"
		} else {
			action = "failed"
		}

		Print("LFS upload %s:", action)
		for name, oid := range c.missing {
			Print("  (missing) %s (%s)", name, oid)
		}
		for name, oid := range c.corrupt {
			Print("  (corrupt) %s (%s)", name, oid)
		}

		if !c.allowMissing {
			pushMissingHint := []string{
				"hint: Your push was rejected due to missing or corrupt local objects.",
				"hint: You can disable this check with: 'git config lfs.allowincompletepush true'",
			}
			Print(strings.Join(pushMissingHint, "\n"))
			os.Exit(2)
		}
	}

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

	if c.lockVerifier.HasUnownedLocks() {
		Print("Unable to push locked files:")
		for _, unowned := range c.lockVerifier.UnownedLocks() {
			Print("* %s - %s", unowned.Path(), unowned.Owners())
		}

		if c.lockVerifier.Enabled() {
			Exit("ERROR: Cannot update locked files.")
		} else {
			Error("WARNING: The above files would have halted this push.")
		}
	} else if c.lockVerifier.HasOwnedLocks() {
		Print("Consider unlocking your own locked files: (`git lfs unlock <path>`)")
		for _, owned := range c.lockVerifier.OwnedLocks() {
			Print("* %s", owned.Path())
		}
	}
}

var (
	githubHttps, _ = url.Parse("https://github.com")
	githubSsh, _   = url.Parse("ssh://github.com")

	// hostsWithKnownLockingSupport is a list of scheme-less hostnames
	// (without port numbers) that are known to implement the LFS locking
	// API.
	//
	// Additions are welcome.
	hostsWithKnownLockingSupport = []*url.URL{
		githubHttps, githubSsh,
	}
)

func (c *uploadContext) uploadTransfer(p *lfs.WrappedPointer) (*tq.Transfer, error) {
	filename := p.Name
	oid := p.Oid

	localMediaPath, err := c.gitfilter.ObjectPath(oid)
	if err != nil {
		return nil, errors.Wrapf(err, "Error uploading file %s (%s)", filename, oid)
	}

	if len(filename) > 0 {
		if err = c.ensureFile(filename, localMediaPath); err != nil && !errors.IsCleanPointerError(err) {
			return nil, err
		}
	}

	return &tq.Transfer{
		Name: filename,
		Path: localMediaPath,
		Oid:  oid,
		Size: p.Size,
	}, nil
}

// ensureFile makes sure that the cleanPath exists before pushing it.  If it
// does not exist, it attempts to clean it by reading the file at smudgePath.
func (c *uploadContext) ensureFile(smudgePath, cleanPath string) error {
	if _, err := os.Stat(cleanPath); err == nil {
		return nil
	}

	localPath := filepath.Join(cfg.LocalWorkingDir(), smudgePath)
	file, err := os.Open(localPath)
	if err != nil {
		if c.allowMissing {
			return nil
		}
		return err
	}

	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	cleaned, err := c.gitfilter.Clean(file, file.Name(), stat.Size(), nil)
	if cleaned != nil {
		cleaned.Teardown()
	}

	if err != nil {
		return err
	}
	return nil
}

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

	for _, supported := range hostsWithKnownLockingSupport {
		if supported.Scheme == u.Scheme &&
			supported.Hostname() == u.Hostname() &&
			strings.HasPrefix(u.Path, supported.Path) {
			return true
		}
	}
	return false
}

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

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

	_, err := cfg.SetGitLocalKey(key, "false")
	return err
}
<commit_msg>commands/uploader: improve error message on missing object<commit_after>package commands

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"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/tasklog"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tq"
	"github.com/rubyist/tracerx"
)

func uploadForRefUpdates(ctx *uploadContext, updates []*git.RefUpdate, pushAll bool) error {
	gitscanner, err := ctx.buildGitScanner()
	if err != nil {
		return err
	}

	defer func() {
		gitscanner.Close()
		ctx.ReportErrors()
	}()

	verifyLocksForUpdates(ctx.lockVerifier, updates)
	for _, update := range updates {
		// initialized here to prevent looped defer
		q := ctx.NewQueue(
			tq.RemoteRef(update.Right()),
		)
		err := uploadLeftOrAll(gitscanner, ctx, q, update, pushAll)
		ctx.CollectErrors(q)

		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("ref %s:", update.Left().Name))
		}
	}

	return nil
}

func uploadLeftOrAll(g *lfs.GitScanner, ctx *uploadContext, q *tq.TransferQueue, update *git.RefUpdate, pushAll bool) error {
	cb := ctx.gitScannerCallback(q)
	if pushAll {
		if err := g.ScanRefWithDeleted(update.LeftCommitish(), cb); err != nil {
			return err
		}
	} else {
		if err := g.ScanLeftToRemote(update.LeftCommitish(), cb); err != nil {
			return err
		}
	}
	return ctx.scannerError()
}

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

	logger *tasklog.Logger
	meter  *tq.Meter

	committerName  string
	committerEmail string

	lockVerifier *lockVerifier

	// allowMissing specifies whether pushes containing missing/corrupt
	// pointers should allow pushing Git blobs
	allowMissing bool

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

	// filename => oid
	missing   map[string]string
	corrupt   map[string]string
	otherErrs []error
}

func newUploadContext(dryRun bool) *uploadContext {
	remote := cfg.PushRemote()
	manifest := getTransferManifestOperationRemote("upload", remote)
	ctx := &uploadContext{
		Remote:       remote,
		Manifest:     manifest,
		DryRun:       dryRun,
		uploadedOids: tools.NewStringSet(),
		gitfilter:    lfs.NewGitFilter(cfg),
		lockVerifier: newLockVerifier(manifest),
		allowMissing: cfg.Git.Bool("lfs.allowincompletepush", false),
		missing:      make(map[string]string),
		corrupt:      make(map[string]string),
		otherErrs:    make([]error, 0),
	}

	var sink io.Writer = os.Stdout
	if dryRun {
		sink = ioutil.Discard
	}

	ctx.logger = tasklog.NewLogger(sink,
		tasklog.ForceProgress(cfg.ForceProgress()),
	)
	ctx.meter = buildProgressMeter(ctx.DryRun, tq.Upload)
	ctx.logger.Enqueue(ctx.meter)
	ctx.committerName, ctx.committerEmail = cfg.CurrentCommitter()
	return ctx
}

func (c *uploadContext) NewQueue(options ...tq.Option) *tq.TransferQueue {
	return tq.NewTransferQueue(tq.Upload, c.Manifest, c.Remote, append(options,
		tq.DryRun(c.DryRun),
		tq.WithProgress(c.meter),
	)...)
}

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(nil)
	gitscanner.FoundLockable = func(n string) { c.lockVerifier.LockedByThem(n) }
	gitscanner.PotentialLockables = c.lockVerifier
	return gitscanner, gitscanner.RemoteForPush(c.Remote)
}

func (c *uploadContext) gitScannerCallback(tqueue *tq.TransferQueue) func(*lfs.WrappedPointer, error) {
	return func(p *lfs.WrappedPointer, err error) {
		if err != nil {
			c.addScannerError(err)
		} else {
			c.UploadPointers(tqueue, p)
		}
	}
}

// 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) []*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 c.lockVerifier.LockedByThem(p.Name) {
			// If the verification state is enabled, this failed
			// locks verification means that the push should fail.
			//
			// If the state is disabled, the verification error is
			// silent and the user can upload.
			//
			// If the state is undefined, the verification error is
			// sent as a warning and the user can upload.
			canUpload = !c.lockVerifier.Enabled()
		}

		c.lockVerifier.LockedByUs(p.Name)

		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 uploadables
}

func (c *uploadContext) UploadPointers(q *tq.TransferQueue, 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
	}

	pointers := c.prepareUpload(unfiltered...)
	for _, p := range pointers {
		t, err := c.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) CollectErrors(tqueue *tq.TransferQueue) {
	tqueue.Wait()

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

func (c *uploadContext) ReportErrors() {
	c.meter.Finish()

	for _, err := range c.otherErrs {
		FullError(err)
	}

	if len(c.missing) > 0 || len(c.corrupt) > 0 {
		var action string
		if c.allowMissing {
			action = "missing objects"
		} else {
			action = "failed"
		}

		Print("LFS upload %s:", action)
		for name, oid := range c.missing {
			Print("  (missing) %s (%s)", name, oid)
		}
		for name, oid := range c.corrupt {
			Print("  (corrupt) %s (%s)", name, oid)
		}

		if !c.allowMissing {
			pushMissingHint := []string{
				"hint: Your push was rejected due to missing or corrupt local objects.",
				"hint: You can disable this check with: 'git config lfs.allowincompletepush true'",
			}
			Print(strings.Join(pushMissingHint, "\n"))
			os.Exit(2)
		}
	}

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

	if c.lockVerifier.HasUnownedLocks() {
		Print("Unable to push locked files:")
		for _, unowned := range c.lockVerifier.UnownedLocks() {
			Print("* %s - %s", unowned.Path(), unowned.Owners())
		}

		if c.lockVerifier.Enabled() {
			Exit("ERROR: Cannot update locked files.")
		} else {
			Error("WARNING: The above files would have halted this push.")
		}
	} else if c.lockVerifier.HasOwnedLocks() {
		Print("Consider unlocking your own locked files: (`git lfs unlock <path>`)")
		for _, owned := range c.lockVerifier.OwnedLocks() {
			Print("* %s", owned.Path())
		}
	}
}

var (
	githubHttps, _ = url.Parse("https://github.com")
	githubSsh, _   = url.Parse("ssh://github.com")

	// hostsWithKnownLockingSupport is a list of scheme-less hostnames
	// (without port numbers) that are known to implement the LFS locking
	// API.
	//
	// Additions are welcome.
	hostsWithKnownLockingSupport = []*url.URL{
		githubHttps, githubSsh,
	}
)

func (c *uploadContext) uploadTransfer(p *lfs.WrappedPointer) (*tq.Transfer, error) {
	filename := p.Name
	oid := p.Oid

	localMediaPath, err := c.gitfilter.ObjectPath(oid)
	if err != nil {
		return nil, errors.Wrapf(err, "Error uploading file %s (%s)", filename, oid)
	}

	if len(filename) > 0 {
		if err = c.ensureFile(filename, localMediaPath, oid); err != nil && !errors.IsCleanPointerError(err) {
			return nil, err
		}
	}

	return &tq.Transfer{
		Name: filename,
		Path: localMediaPath,
		Oid:  oid,
		Size: p.Size,
	}, nil
}

// ensureFile makes sure that the cleanPath exists before pushing it.  If it
// does not exist, it attempts to clean it by reading the file at smudgePath.
func (c *uploadContext) ensureFile(smudgePath, cleanPath, oid string) error {
	if _, err := os.Stat(cleanPath); err == nil {
		return nil
	}

	localPath := filepath.Join(cfg.LocalWorkingDir(), smudgePath)
	file, err := os.Open(localPath)
	if err != nil {
		if c.allowMissing {
			return nil
		}
		return errors.Wrapf(err, "Unable to find source for object %v (try running git lfs fetch --all)", oid)
	}

	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	cleaned, err := c.gitfilter.Clean(file, file.Name(), stat.Size(), nil)
	if cleaned != nil {
		cleaned.Teardown()
	}

	if err != nil {
		return err
	}
	return nil
}

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

	for _, supported := range hostsWithKnownLockingSupport {
		if supported.Scheme == u.Scheme &&
			supported.Hostname() == u.Hostname() &&
			strings.HasPrefix(u.Path, supported.Path) {
			return true
		}
	}
	return false
}

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

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

	_, err := cfg.SetGitLocalKey(key, "false")
	return err
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"sort"
	"github.com/gin-gonic/gin"
	"github.com/smira/aptly/deb"
	"github.com/smira/aptly/query"
)

// GET /api/snapshots
func apiSnapshotsList(c *gin.Context) {
	SortMethodString := c.Request.URL.Query().Get("sort")

	collection := context.CollectionFactory().SnapshotCollection()
	collection.Sort(SortMethodString)

	result := []*deb.Snapshot{}
	collection.ForEach(func(snapshot *deb.Snapshot) error {
		result = append(result, snapshot)
		return nil
	})

	c.JSON(200, result)
}

// POST /api/mirros/:name/snapshots/:snapname
func apiSnapshotsCreateFromMirror(c *gin.Context) {
	var (
		err       error
		repo     *deb.RemoteRepo
		snapshot *deb.Snapshot
	)

	var b struct {
		Name                string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	collection := context.CollectionFactory().RemoteRepoCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.Lock()
	defer snapshotCollection.Unlock()

	repo, err = collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = repo.CheckLock()
	if err != nil {
		c.Fail(409, err)
		return
	}

	err = collection.LoadComplete(repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		c.Fail(500, err)
		return
	}

	c.JSON(201, snapshot)
}

// POST /api/repos/:name/snapshots/:snapname
func apiSnapshotsCreateEmpty(c *gin.Context) {
	var (
		err       error
		snapshot *deb.Snapshot
	)

	var b struct {
		Name                string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.Lock()
	defer snapshotCollection.Unlock()

	packageList := deb.NewPackageList()

	snapshot = deb.NewSnapshotFromPackageList(b.Name, nil, packageList, "Created as empty")

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		c.Fail(500, err)
		return
	}

	c.JSON(201, snapshot)
}

// POST /api/repos/:name/snapshots/:snapname
func apiSnapshotsCreateFromRepository(c *gin.Context) {
	var (
		err       error
		repo     *deb.LocalRepo
		snapshot *deb.Snapshot
	)

	var b struct {
		Name                string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	collection := context.CollectionFactory().LocalRepoCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.Lock()
	defer snapshotCollection.Unlock()

	repo, err = collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = collection.LoadComplete(repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		c.Fail(500, err)
		return
	}

	c.JSON(201, snapshot)
}

// PUT /api/snapshots/:name
func apiSnapshotsRename(c *gin.Context) {
	var (
		err      error
		snapshot *deb.Snapshot
	)

	var b struct {
		Name    string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	collection := context.CollectionFactory().SnapshotCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
	}

	_, err = context.CollectionFactory().SnapshotCollection().ByName(b.Name)
	if err == nil {
		c.Fail(409, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name))
	}

	snapshot.Name = b.Name
	err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
	if err != nil {
		c.Fail(403, err)
	}

	c.JSON(200, snapshot)
}

// GET /api/snapshots/:name
func apiSnapshotsShow(c *gin.Context) {
	collection := context.CollectionFactory().SnapshotCollection()
	collection.RLock()
	defer collection.RUnlock()

	repo, err := collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	c.JSON(200, repo)
}

// DELETE /api/snapshots/:name
func apiSnapshotsDrop(c *gin.Context) {
	name := c.Params.ByName("name")
	force := c.Request.URL.Query().Get("force") == "1"

	collection := context.CollectionFactory().LocalRepoCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.RLock()
	defer snapshotCollection.RUnlock()

	publishedCollection := context.CollectionFactory().PublishedRepoCollection()
	publishedCollection.RLock()
	defer publishedCollection.RUnlock()

	snapshot, err := snapshotCollection.ByName(name)
	if err != nil {
		c.Fail(404, err)
		return
	}

	published := publishedCollection.BySnapshot(snapshot)

	if len(published) > 0 {
		for _, repo := range published {
			err = publishedCollection.LoadComplete(repo, context.CollectionFactory())
			if err != nil {
				c.Fail(409, err)
				return
			}
		}

		c.Fail(409, fmt.Errorf("unable to drop: snapshot is published"))
		return
	}

	if !force {
		snapshots := snapshotCollection.BySnapshotSource(snapshot)
		if len(snapshots) > 0 {
			c.Fail(409, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override"))
			return
		}
	}

	err = context.CollectionFactory().SnapshotCollection().Drop(snapshot)
	if err != nil {
		c.Fail(409, err)
		return
	}

	c.JSON(200, gin.H{})
}

// GET /api/snapshots/:name
func apiSnapshotsDiff(c *gin.Context) {
	onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"

	collection := context.CollectionFactory().SnapshotCollection()
	collection.RLock()
	defer collection.RUnlock()

	snapshotA, err := collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	snapshotB, err := collection.ByName(c.Params.ByName("withSnapshot"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotA)
	if err != nil {
		c.Fail(500, err)
	}

	err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotB)
	if err != nil {
		c.Fail(500, err)
	}

	// Calculate diff
	diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
	if err != nil {
		c.Fail(500, err)
	}

	result := []deb.PackageDiff{}

	for _, pdiff := range diff {
		if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
			continue
		}

		result = append(result, pdiff)
	}

	c.JSON(200, result)
}

// GET /api/snapshots/:name/packages
func apiSnapshotsSearchPackages(c *gin.Context) {
	collection := context.CollectionFactory().SnapshotCollection()
	collection.RLock()
	defer collection.RUnlock()

	snapshot, err := collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = collection.LoadComplete(snapshot)
	if err != nil {
		c.Fail(400, err)
		return
	}

	reflist := snapshot.RefList()
	result := []*deb.Package{}

	list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
	if err != nil {
		c.Fail(400, err)
		return
	}

	queryS := c.Request.URL.Query().Get("q")
	if queryS != "" {
		q, err := query.Parse(c.Request.URL.Query().Get("q"))
		if err != nil {
			c.Fail(401, err)
			return
		}

		withDeps := c.Request.URL.Query().Get("withDeps") == "1"
		architecturesList := []string{}

		if withDeps {
			if len(context.ArchitecturesList()) > 0 {
				architecturesList = context.ArchitecturesList()
			} else {
				architecturesList = list.Architectures(false)
			}

			sort.Strings(architecturesList)

			if len(architecturesList) == 0 {
				c.Fail(400, fmt.Errorf("unable to determine list of architectures, please specify explicitly"))
				return
			}
		}

		list.PrepareIndex()

		packages, err := list.Filter([]deb.PackageQuery{q}, withDeps,
			nil, context.DependencyOptions(), architecturesList)
		if err != nil {
			c.Fail(400, fmt.Errorf("unable to search: %s", err))
		}

		packages.ForEach(func(p *deb.Package) error {
			result = append(result, p)
			return nil
		})
	} else {
		list.ForEach(func(p *deb.Package) error {
			result = append(result, p)
			return nil
		})
	}

	c.JSON(200, result)
}
<commit_msg>Fix wrong methods comments<commit_after>package api

import (
	"fmt"
	"sort"
	"github.com/gin-gonic/gin"
	"github.com/smira/aptly/deb"
	"github.com/smira/aptly/query"
)

// GET /api/snapshots
func apiSnapshotsList(c *gin.Context) {
	SortMethodString := c.Request.URL.Query().Get("sort")

	collection := context.CollectionFactory().SnapshotCollection()
	collection.Sort(SortMethodString)

	result := []*deb.Snapshot{}
	collection.ForEach(func(snapshot *deb.Snapshot) error {
		result = append(result, snapshot)
		return nil
	})

	c.JSON(200, result)
}

// POST /api/mirrors/:name/snapshots/
func apiSnapshotsCreateFromMirror(c *gin.Context) {
	var (
		err       error
		repo     *deb.RemoteRepo
		snapshot *deb.Snapshot
	)

	var b struct {
		Name                string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	collection := context.CollectionFactory().RemoteRepoCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.Lock()
	defer snapshotCollection.Unlock()

	repo, err = collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = repo.CheckLock()
	if err != nil {
		c.Fail(409, err)
		return
	}

	err = collection.LoadComplete(repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		c.Fail(500, err)
		return
	}

	c.JSON(201, snapshot)
}

// POST /api/snapshots/:name
func apiSnapshotsCreateEmpty(c *gin.Context) {
	var (
		err       error
		snapshot *deb.Snapshot
	)

	var b struct {
		Name                string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.Lock()
	defer snapshotCollection.Unlock()

	packageList := deb.NewPackageList()

	snapshot = deb.NewSnapshotFromPackageList(b.Name, nil, packageList, "Created as empty")

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		c.Fail(500, err)
		return
	}

	c.JSON(201, snapshot)
}

// POST /api/repos/:name/snapshots/:snapname
func apiSnapshotsCreateFromRepository(c *gin.Context) {
	var (
		err       error
		repo     *deb.LocalRepo
		snapshot *deb.Snapshot
	)

	var b struct {
		Name                string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	collection := context.CollectionFactory().LocalRepoCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.Lock()
	defer snapshotCollection.Unlock()

	repo, err = collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = collection.LoadComplete(repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
	if err != nil {
		c.Fail(500, err)
		return
	}

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		c.Fail(500, err)
		return
	}

	c.JSON(201, snapshot)
}

// PUT /api/snapshots/:name
func apiSnapshotsRename(c *gin.Context) {
	var (
		err      error
		snapshot *deb.Snapshot
	)

	var b struct {
		Name    string `binding:"required"`
	}

	if !c.Bind(&b) {
		return
	}

	collection := context.CollectionFactory().SnapshotCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
	}

	_, err = context.CollectionFactory().SnapshotCollection().ByName(b.Name)
	if err == nil {
		c.Fail(409, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name))
	}

	snapshot.Name = b.Name
	err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
	if err != nil {
		c.Fail(403, err)
	}

	c.JSON(200, snapshot)
}

// GET /api/snapshots/:name
func apiSnapshotsShow(c *gin.Context) {
	collection := context.CollectionFactory().SnapshotCollection()
	collection.RLock()
	defer collection.RUnlock()

	repo, err := collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	c.JSON(200, repo)
}

// DELETE /api/snapshots/:name
func apiSnapshotsDrop(c *gin.Context) {
	name := c.Params.ByName("name")
	force := c.Request.URL.Query().Get("force") == "1"

	collection := context.CollectionFactory().LocalRepoCollection()
	collection.Lock()
	defer collection.Unlock()

	snapshotCollection := context.CollectionFactory().SnapshotCollection()
	snapshotCollection.RLock()
	defer snapshotCollection.RUnlock()

	publishedCollection := context.CollectionFactory().PublishedRepoCollection()
	publishedCollection.RLock()
	defer publishedCollection.RUnlock()

	snapshot, err := snapshotCollection.ByName(name)
	if err != nil {
		c.Fail(404, err)
		return
	}

	published := publishedCollection.BySnapshot(snapshot)

	if len(published) > 0 {
		for _, repo := range published {
			err = publishedCollection.LoadComplete(repo, context.CollectionFactory())
			if err != nil {
				c.Fail(409, err)
				return
			}
		}

		c.Fail(409, fmt.Errorf("unable to drop: snapshot is published"))
		return
	}

	if !force {
		snapshots := snapshotCollection.BySnapshotSource(snapshot)
		if len(snapshots) > 0 {
			c.Fail(409, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override"))
			return
		}
	}

	err = context.CollectionFactory().SnapshotCollection().Drop(snapshot)
	if err != nil {
		c.Fail(409, err)
		return
	}

	c.JSON(200, gin.H{})
}

// POST /api/snapshots/:name/diff/:name2
func apiSnapshotsDiff(c *gin.Context) {
	onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"

	collection := context.CollectionFactory().SnapshotCollection()
	collection.RLock()
	defer collection.RUnlock()

	snapshotA, err := collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	snapshotB, err := collection.ByName(c.Params.ByName("withSnapshot"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotA)
	if err != nil {
		c.Fail(500, err)
	}

	err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotB)
	if err != nil {
		c.Fail(500, err)
	}

	// Calculate diff
	diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
	if err != nil {
		c.Fail(500, err)
	}

	result := []deb.PackageDiff{}

	for _, pdiff := range diff {
		if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
			continue
		}

		result = append(result, pdiff)
	}

	c.JSON(200, result)
}

// GET /api/snapshots/:name/packages
func apiSnapshotsSearchPackages(c *gin.Context) {
	collection := context.CollectionFactory().SnapshotCollection()
	collection.RLock()
	defer collection.RUnlock()

	snapshot, err := collection.ByName(c.Params.ByName("name"))
	if err != nil {
		c.Fail(404, err)
		return
	}

	err = collection.LoadComplete(snapshot)
	if err != nil {
		c.Fail(400, err)
		return
	}

	reflist := snapshot.RefList()
	result := []*deb.Package{}

	list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
	if err != nil {
		c.Fail(400, err)
		return
	}

	queryS := c.Request.URL.Query().Get("q")
	if queryS != "" {
		q, err := query.Parse(c.Request.URL.Query().Get("q"))
		if err != nil {
			c.Fail(401, err)
			return
		}

		withDeps := c.Request.URL.Query().Get("withDeps") == "1"
		architecturesList := []string{}

		if withDeps {
			if len(context.ArchitecturesList()) > 0 {
				architecturesList = context.ArchitecturesList()
			} else {
				architecturesList = list.Architectures(false)
			}

			sort.Strings(architecturesList)

			if len(architecturesList) == 0 {
				c.Fail(400, fmt.Errorf("unable to determine list of architectures, please specify explicitly"))
				return
			}
		}

		list.PrepareIndex()

		packages, err := list.Filter([]deb.PackageQuery{q}, withDeps,
			nil, context.DependencyOptions(), architecturesList)
		if err != nil {
			c.Fail(400, fmt.Errorf("unable to search: %s", err))
		}

		packages.ForEach(func(p *deb.Package) error {
			result = append(result, p)
			return nil
		})
	} else {
		list.ForEach(func(p *deb.Package) error {
			result = append(result, p)
			return nil
		})
	}

	c.JSON(200, result)
}
<|endoftext|>


<commit_before>package engine

import (
	"encoding/hex"
	"fmt"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/protocol/go"
	triplesec "github.com/keybase/go-triplesec"
)

type SignupJoinEngine struct {
	signupState *SignupState

	uid            keybase1.UID
	session        string
	csrf           string
	lastPassphrase string
	username       libkb.NormalizedUsername
	ppGen          libkb.PassphraseGeneration

	libkb.Contextified
}

func NewSignupJoinEngine(g *libkb.GlobalContext) *SignupJoinEngine {
	return &SignupJoinEngine{Contextified: libkb.NewContextified(g)}
}

// XXX why is this here?
func CheckUsernameAvailable(g *libkb.GlobalContext, s string) (err error) {
	_, err = g.API.Get(libkb.APIArg{
		Endpoint:    "user/lookup",
		NeedSession: false,
		Args: libkb.HTTPArgs{
			"username": libkb.S{Val: s},
			"fields":   libkb.S{Val: "basics"},
		},
	})
	if err == nil {
		err = libkb.AppStatusError{
			Code: libkb.SCBadSignupUsernameTaken,
			Name: "BAD_SIGNUP_USERNAME_TAKEN",
			Desc: fmt.Sprintf("Username '%s' is taken", s),
		}
	} else if ase, ok := err.(libkb.AppStatusError); ok && ase.Name == "NOT_FOUND" {
		err = nil
	}
	return
}

func (s *SignupJoinEngine) Init() error {
	return nil
}

func (s *SignupJoinEngine) CheckRegistered() (err error) {
	s.G().Log.Debug("+ libkb.SignupJoinEngine::CheckRegistered")
	if cr := s.G().Env.GetConfig(); cr == nil {
		err = fmt.Errorf("No configuration file available")
	} else if u := cr.GetUID(); u.Exists() {
		err = libkb.AlreadyRegisteredError{UID: u}
	}
	s.G().Log.Debug("- libkb.SignupJoinEngine::CheckRegistered -> %s", libkb.ErrToOk(err))
	return err
}

type SignupJoinEngineRunArg struct {
	Username   string
	Email      string
	InviteCode string
	PWHash     []byte
	PWSalt     []byte
	SkipMail   bool
}

func (s *SignupJoinEngine) Post(arg SignupJoinEngineRunArg) (err error) {
	var res *libkb.APIRes
	var ppGenTmp int
	res, err = s.G().API.Post(libkb.APIArg{
		Endpoint: "signup",
		Args: libkb.HTTPArgs{
			"salt":          libkb.S{Val: hex.EncodeToString(arg.PWSalt)},
			"pwh":           libkb.S{Val: hex.EncodeToString(arg.PWHash)},
			"username":      libkb.S{Val: arg.Username},
			"email":         libkb.S{Val: arg.Email},
			"invitation_id": libkb.S{Val: arg.InviteCode},
			"pwh_version":   libkb.I{Val: int(triplesec.Version)},
			"skip_mail":     libkb.B{Val: arg.SkipMail},
		}})
	if err == nil {
		s.username = libkb.NewNormalizedUsername(arg.Username)
		libkb.GetUIDVoid(res.Body.AtKey("uid"), &s.uid, &err)
		res.Body.AtKey("session").GetStringVoid(&s.session, &err)
		res.Body.AtKey("csrf_token").GetStringVoid(&s.csrf, &err)
		res.Body.AtPath("me.basics.passphrase_generation").GetIntVoid(&ppGenTmp, &err)
	}
	if err == nil {
		err = libkb.CheckUIDAgainstUsername(s.uid, arg.Username)
		s.ppGen = libkb.PassphraseGeneration(ppGenTmp)
	}
	return
}

type SignupJoinEngineRunRes struct {
	PassphraseOk bool
	PostOk       bool
	WriteOk      bool
	UID          keybase1.UID
	User         *libkb.User
	Err          error
	PpGen        libkb.PassphraseGeneration
}

func (r SignupJoinEngineRunRes) Error() string {
	return r.Err.Error()
}

func (s *SignupJoinEngine) Run(lctx libkb.LoginContext, arg SignupJoinEngineRunArg) (res SignupJoinEngineRunRes) {
	res.PassphraseOk = true

	if res.Err = s.Post(arg); res.Err != nil {
		return
	}
	res.PostOk = true
	if res.Err = s.WriteOut(lctx, arg.PWSalt); res.Err != nil {
		return
	}
	res.WriteOk = true
	res.UID = s.uid
	res.PpGen = s.ppGen
	return
}

func (s *SignupJoinEngine) WriteOut(lctx libkb.LoginContext, salt []byte) error {
	if err := lctx.LocalSession().Load(); err != nil {
		return err
	}
	if err := lctx.CreateLoginSessionWithSalt(s.username.String(), salt); err != nil {
		return err
	}
	if err := lctx.SaveState(s.session, s.csrf, s.username, s.uid); err != nil {
		return err
	}
	return nil
}

func (s *SignupJoinEngine) PostInviteRequest(arg libkb.InviteRequestArg) error {
	return libkb.PostInviteRequest(arg)
}
<commit_msg>Remove unused signupState field<commit_after>package engine

import (
	"encoding/hex"
	"fmt"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/protocol/go"
	triplesec "github.com/keybase/go-triplesec"
)

type SignupJoinEngine struct {
	uid            keybase1.UID
	session        string
	csrf           string
	lastPassphrase string
	username       libkb.NormalizedUsername
	ppGen          libkb.PassphraseGeneration

	libkb.Contextified
}

func NewSignupJoinEngine(g *libkb.GlobalContext) *SignupJoinEngine {
	return &SignupJoinEngine{Contextified: libkb.NewContextified(g)}
}

// XXX why is this here?
func CheckUsernameAvailable(g *libkb.GlobalContext, s string) (err error) {
	_, err = g.API.Get(libkb.APIArg{
		Endpoint:    "user/lookup",
		NeedSession: false,
		Args: libkb.HTTPArgs{
			"username": libkb.S{Val: s},
			"fields":   libkb.S{Val: "basics"},
		},
	})
	if err == nil {
		err = libkb.AppStatusError{
			Code: libkb.SCBadSignupUsernameTaken,
			Name: "BAD_SIGNUP_USERNAME_TAKEN",
			Desc: fmt.Sprintf("Username '%s' is taken", s),
		}
	} else if ase, ok := err.(libkb.AppStatusError); ok && ase.Name == "NOT_FOUND" {
		err = nil
	}
	return
}

func (s *SignupJoinEngine) Init() error {
	return nil
}

func (s *SignupJoinEngine) CheckRegistered() (err error) {
	s.G().Log.Debug("+ libkb.SignupJoinEngine::CheckRegistered")
	if cr := s.G().Env.GetConfig(); cr == nil {
		err = fmt.Errorf("No configuration file available")
	} else if u := cr.GetUID(); u.Exists() {
		err = libkb.AlreadyRegisteredError{UID: u}
	}
	s.G().Log.Debug("- libkb.SignupJoinEngine::CheckRegistered -> %s", libkb.ErrToOk(err))
	return err
}

type SignupJoinEngineRunArg struct {
	Username   string
	Email      string
	InviteCode string
	PWHash     []byte
	PWSalt     []byte
	SkipMail   bool
}

func (s *SignupJoinEngine) Post(arg SignupJoinEngineRunArg) (err error) {
	var res *libkb.APIRes
	var ppGenTmp int
	res, err = s.G().API.Post(libkb.APIArg{
		Endpoint: "signup",
		Args: libkb.HTTPArgs{
			"salt":          libkb.S{Val: hex.EncodeToString(arg.PWSalt)},
			"pwh":           libkb.S{Val: hex.EncodeToString(arg.PWHash)},
			"username":      libkb.S{Val: arg.Username},
			"email":         libkb.S{Val: arg.Email},
			"invitation_id": libkb.S{Val: arg.InviteCode},
			"pwh_version":   libkb.I{Val: int(triplesec.Version)},
			"skip_mail":     libkb.B{Val: arg.SkipMail},
		}})
	if err == nil {
		s.username = libkb.NewNormalizedUsername(arg.Username)
		libkb.GetUIDVoid(res.Body.AtKey("uid"), &s.uid, &err)
		res.Body.AtKey("session").GetStringVoid(&s.session, &err)
		res.Body.AtKey("csrf_token").GetStringVoid(&s.csrf, &err)
		res.Body.AtPath("me.basics.passphrase_generation").GetIntVoid(&ppGenTmp, &err)
	}
	if err == nil {
		err = libkb.CheckUIDAgainstUsername(s.uid, arg.Username)
		s.ppGen = libkb.PassphraseGeneration(ppGenTmp)
	}
	return
}

type SignupJoinEngineRunRes struct {
	PassphraseOk bool
	PostOk       bool
	WriteOk      bool
	UID          keybase1.UID
	User         *libkb.User
	Err          error
	PpGen        libkb.PassphraseGeneration
}

func (r SignupJoinEngineRunRes) Error() string {
	return r.Err.Error()
}

func (s *SignupJoinEngine) Run(lctx libkb.LoginContext, arg SignupJoinEngineRunArg) (res SignupJoinEngineRunRes) {
	res.PassphraseOk = true

	if res.Err = s.Post(arg); res.Err != nil {
		return
	}
	res.PostOk = true
	if res.Err = s.WriteOut(lctx, arg.PWSalt); res.Err != nil {
		return
	}
	res.WriteOk = true
	res.UID = s.uid
	res.PpGen = s.ppGen
	return
}

func (s *SignupJoinEngine) WriteOut(lctx libkb.LoginContext, salt []byte) error {
	if err := lctx.LocalSession().Load(); err != nil {
		return err
	}
	if err := lctx.CreateLoginSessionWithSalt(s.username.String(), salt); err != nil {
		return err
	}
	if err := lctx.SaveState(s.session, s.csrf, s.username, s.uid); err != nil {
		return err
	}
	return nil
}

func (s *SignupJoinEngine) PostInviteRequest(arg libkb.InviteRequestArg) error {
	return libkb.PostInviteRequest(arg)
}
<|endoftext|>


<commit_before>package service

import (
	"testing"
	"time"

	"github.com/jonboulle/clockwork"
	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/gregor/protocol/gregor1"
)

func rekeySetup(tc libkb.TestContext) (gregor1.UID, *gregorHandler, *RekeyUIHandler) {
	tc.G.SetService()

	kbUID := keybase1.MakeTestUID(1)
	gUID := gregor1.UID(kbUID.ToBytes())
	did, err := libkb.NewDeviceID()
	if err != nil {
		tc.T.Fatal(err)
	}
	tc.G.Env.GetConfigWriter().SetUserConfig(libkb.NewUserConfig(kbUID, "", nil, did), true)

	h, err := newGregorHandler(tc.G)
	if err != nil {
		tc.T.Fatal(err)
	}

	rekeyHandler := NewRekeyUIHandler(tc.G, 0)
	rekeyHandler.alwaysAlive = true
	rekeyHandler.notifyStart = make(chan int, 10)
	rekeyHandler.notifyComplete = make(chan int, 10)
	rekeyHandler.scorer = fakeScoreProblemFolders
	h.PushHandler(rekeyHandler)

	return gUID, h, rekeyHandler
}

func rekeyBroadcast(tc libkb.TestContext, gUID gregor1.UID, h *gregorHandler, body string) {
	msgID := gregor1.MsgID("my_random_id")
	m := gregor1.Message{
		Ibm_: &gregor1.InBandMessage{
			StateUpdate_: &gregor1.StateUpdateMessage{
				Md_: gregor1.Metadata{
					MsgID_: msgID,
					Ctime_: gregor1.ToTime(tc.G.Clock.Now()),
					Uid_:   gUID,
				},
				Creation_: &gregor1.Item{
					Category_: gregor1.Category("kbfs_tlf_rekey_needed"),
					Body_:     gregor1.Body(body),
				},
			},
		},
	}

	if err := h.BroadcastMessage(context.Background(), m); err != nil {
		tc.T.Fatal(err)
	}

}

func TestRekeyNeededMessageNoScores(t *testing.T) {
	t.Skip()
	tc := libkb.SetupTest(t, "gregor", 1)
	defer tc.Cleanup()

	gUID, h, _ := rekeySetup(tc)

	// Test that a broadcast with an empty body works.
	rekeyBroadcast(tc, gUID, h, `{}`)
}

const problemSet = `{
	"user": {
		"uid": "295a7eea607af32040647123732bc819",
		"username": "t_alice"
	},
	"kid": "011212121212121212121212121212121212121212121212121212121212121212120a",
	"tlfs": [
		{
			"tlf": {
				"tlfid": "folder",
				"name": "folder name",
				"writers": ["t_alice"],
				"readers": ["t_alice"],
				"isPrivate": true
			},
			"score": 300,
			"solutions": ["011313131313131313131313131313131313131313131313131313131313131313130a"]
		}
	]
}`

func TestRekeyNeededMessageWithScores(t *testing.T) {
	tc := libkb.SetupTest(t, "gregor", 1)
	defer tc.Cleanup()

	rkeyui := &fakeRekeyUI{}
	router := fakeUIRouter{
		rekeyUI: rkeyui,
	}
	tc.G.SetUIRouter(&router)

	clock := clockwork.NewFakeClock()
	tc.G.Clock = clock

	gUID, h, rekeyHandler := rekeySetup(tc)

	rekeyBroadcast(tc, gUID, h, problemSet)

	select {
	case <-rekeyHandler.notifyStart:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyStart")
	}

	done := make(chan struct{})
	defer close(done)
	go func() {
		for {
			select {
			case <-done:
				return
			default:
				clock.Advance(1 * time.Second)
			}
		}
	}()

	select {
	case <-rekeyHandler.notifyComplete:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyComplete")
	}

	// Test that refresh was called twice.
	if len(rkeyui.refreshArgs) != 2 {
		t.Fatalf("rkeyui refresh calls: %d, expected 2", len(rkeyui.refreshArgs))
	}

	// the first call should contain a TLF
	if len(rkeyui.refreshArgs[0].Tlfs) != 1 {
		t.Errorf("first refresh call, tlf count = %d, expected 1", len(rkeyui.refreshArgs[0].Tlfs))
	}
	// the second call should have updated the scores, and have no more TLFs in it.
	if len(rkeyui.refreshArgs[1].Tlfs) != 0 {
		t.Errorf("second/final refresh call, tlf count = %d, expected 0", len(rkeyui.refreshArgs[1].Tlfs))
	}
}

type fakeRekeyUI struct {
	sessionID   int
	refreshArgs []keybase1.RefreshArg
}

// A rekey is needed, but the user closes the rekey status window.
func TestRekeyNeededUserClose(t *testing.T) {
	tc := libkb.SetupTest(t, "gregor", 1)
	defer tc.Cleanup()

	rkeyui := &fakeRekeyUI{}
	router := fakeUIRouter{
		rekeyUI: rkeyui,
	}
	tc.G.SetUIRouter(&router)

	clock := clockwork.NewFakeClock()
	tc.G.Clock = clock

	gUID, h, rekeyHandler := rekeySetup(tc)

	rekeyBroadcast(tc, gUID, h, problemSet)

	select {
	case <-rekeyHandler.notifyStart:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyStart")
	}

	outcome, err := h.RekeyStatusFinish(context.Background(), rkeyui.sessionID)
	if err != nil {
		t.Fatal(err)
	}
	if outcome != keybase1.Outcome_IGNORED {
		t.Fatalf("RekeyStatusFinish outcome: %v, expected %v", outcome, keybase1.Outcome_IGNORED)
	}

	clock.Advance(3 * time.Second)

	select {
	case <-rekeyHandler.notifyComplete:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyComplete")
	}

	// there should be one call to refresh to bring the window up, but then the RekeyStatusFinish call above
	// should close the window and stop the loop.
	if len(rkeyui.refreshArgs) != 1 {
		t.Fatalf("rkeyui refresh calls: %d, expected 1", len(rkeyui.refreshArgs))
	}

	if len(rkeyui.refreshArgs[0].Tlfs) != 1 {
		t.Errorf("first refresh call, tlf count = %d, expected 1", len(rkeyui.refreshArgs[0].Tlfs))
	}
}

func (f *fakeRekeyUI) DelegateRekeyUI(ctx context.Context) (int, error) {
	f.sessionID++
	return f.sessionID, nil
}

func (f *fakeRekeyUI) Refresh(ctx context.Context, arg keybase1.RefreshArg) error {
	f.refreshArgs = append(f.refreshArgs, arg)
	return nil
}

func fakeScoreProblemFolders(g *libkb.GlobalContext, existing keybase1.ProblemSet) (keybase1.ProblemSet, error) {
	// always return empty ProblemSet
	return keybase1.ProblemSet{}, nil
}
<commit_msg>Wait for notifyRefresh before closing window<commit_after>package service

import (
	"testing"
	"time"

	"github.com/jonboulle/clockwork"
	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/gregor/protocol/gregor1"
)

func rekeySetup(tc libkb.TestContext) (gregor1.UID, *gregorHandler, *RekeyUIHandler) {
	tc.G.SetService()

	kbUID := keybase1.MakeTestUID(1)
	gUID := gregor1.UID(kbUID.ToBytes())
	did, err := libkb.NewDeviceID()
	if err != nil {
		tc.T.Fatal(err)
	}
	tc.G.Env.GetConfigWriter().SetUserConfig(libkb.NewUserConfig(kbUID, "", nil, did), true)

	h, err := newGregorHandler(tc.G)
	if err != nil {
		tc.T.Fatal(err)
	}

	rekeyHandler := NewRekeyUIHandler(tc.G, 0)
	rekeyHandler.alwaysAlive = true
	rekeyHandler.notifyStart = make(chan int, 10)
	rekeyHandler.notifyComplete = make(chan int, 10)
	rekeyHandler.scorer = fakeScoreProblemFolders
	h.PushHandler(rekeyHandler)

	return gUID, h, rekeyHandler
}

func rekeyBroadcast(tc libkb.TestContext, gUID gregor1.UID, h *gregorHandler, body string) {
	msgID := gregor1.MsgID("my_random_id")
	m := gregor1.Message{
		Ibm_: &gregor1.InBandMessage{
			StateUpdate_: &gregor1.StateUpdateMessage{
				Md_: gregor1.Metadata{
					MsgID_: msgID,
					Ctime_: gregor1.ToTime(tc.G.Clock.Now()),
					Uid_:   gUID,
				},
				Creation_: &gregor1.Item{
					Category_: gregor1.Category("kbfs_tlf_rekey_needed"),
					Body_:     gregor1.Body(body),
				},
			},
		},
	}

	if err := h.BroadcastMessage(context.Background(), m); err != nil {
		tc.T.Fatal(err)
	}

}

func TestRekeyNeededMessageNoScores(t *testing.T) {
	t.Skip()
	tc := libkb.SetupTest(t, "gregor", 1)
	defer tc.Cleanup()

	gUID, h, _ := rekeySetup(tc)

	// Test that a broadcast with an empty body works.
	rekeyBroadcast(tc, gUID, h, `{}`)
}

const problemSet = `{
	"user": {
		"uid": "295a7eea607af32040647123732bc819",
		"username": "t_alice"
	},
	"kid": "011212121212121212121212121212121212121212121212121212121212121212120a",
	"tlfs": [
		{
			"tlf": {
				"tlfid": "folder",
				"name": "folder name",
				"writers": ["t_alice"],
				"readers": ["t_alice"],
				"isPrivate": true
			},
			"score": 300,
			"solutions": ["011313131313131313131313131313131313131313131313131313131313131313130a"]
		}
	]
}`

func TestRekeyNeededMessageWithScores(t *testing.T) {
	tc := libkb.SetupTest(t, "gregor", 1)
	defer tc.Cleanup()

	rkeyui := &fakeRekeyUI{}
	router := fakeUIRouter{
		rekeyUI: rkeyui,
	}
	tc.G.SetUIRouter(&router)

	clock := clockwork.NewFakeClock()
	tc.G.Clock = clock

	gUID, h, rekeyHandler := rekeySetup(tc)

	rekeyBroadcast(tc, gUID, h, problemSet)

	select {
	case <-rekeyHandler.notifyStart:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyStart")
	}

	done := make(chan struct{})
	defer close(done)
	go func() {
		for {
			select {
			case <-done:
				return
			default:
				clock.Advance(1 * time.Second)
			}
		}
	}()

	select {
	case <-rekeyHandler.notifyComplete:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyComplete")
	}

	// Test that refresh was called twice.
	if len(rkeyui.refreshArgs) != 2 {
		t.Fatalf("rkeyui refresh calls: %d, expected 2", len(rkeyui.refreshArgs))
	}

	// the first call should contain a TLF
	if len(rkeyui.refreshArgs[0].Tlfs) != 1 {
		t.Errorf("first refresh call, tlf count = %d, expected 1", len(rkeyui.refreshArgs[0].Tlfs))
	}
	// the second call should have updated the scores, and have no more TLFs in it.
	if len(rkeyui.refreshArgs[1].Tlfs) != 0 {
		t.Errorf("second/final refresh call, tlf count = %d, expected 0", len(rkeyui.refreshArgs[1].Tlfs))
	}
}

type fakeRekeyUI struct {
	sessionID     int
	refreshArgs   []keybase1.RefreshArg
	notifyRefresh chan bool
}

// A rekey is needed, but the user closes the rekey status window.
func TestRekeyNeededUserClose(t *testing.T) {
	tc := libkb.SetupTest(t, "gregor", 1)
	defer tc.Cleanup()

	rkeyui := &fakeRekeyUI{}
	rkeyui.notifyRefresh = make(chan bool, 10)
	router := fakeUIRouter{
		rekeyUI: rkeyui,
	}
	tc.G.SetUIRouter(&router)

	clock := clockwork.NewFakeClock()
	tc.G.Clock = clock

	gUID, h, rekeyHandler := rekeySetup(tc)

	rekeyBroadcast(tc, gUID, h, problemSet)

	select {
	case <-rekeyHandler.notifyStart:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyStart")
	}

	// since this is testing that the user closes a rekey status window,
	// wait for the refresh call:
	select {
	case <-rkeyui.notifyRefresh:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyui.notifyRefresh")
	}

	// now call finish
	outcome, err := h.RekeyStatusFinish(context.Background(), rkeyui.sessionID)
	if err != nil {
		t.Fatal(err)
	}
	if outcome != keybase1.Outcome_IGNORED {
		t.Fatalf("RekeyStatusFinish outcome: %v, expected %v", outcome, keybase1.Outcome_IGNORED)
	}

	clock.Advance(3 * time.Second)

	select {
	case <-rekeyHandler.notifyComplete:
	case <-time.After(20 * time.Second):
		t.Fatal("timeout waiting for rekeyHandler.notifyComplete")
	}

	// there should be one call to refresh to bring the window up, but then the RekeyStatusFinish call above
	// should close the window and stop the loop.
	if len(rkeyui.refreshArgs) != 1 {
		t.Fatalf("rkeyui refresh calls: %d, expected 1", len(rkeyui.refreshArgs))
	}

	if len(rkeyui.refreshArgs[0].Tlfs) != 1 {
		t.Errorf("first refresh call, tlf count = %d, expected 1", len(rkeyui.refreshArgs[0].Tlfs))
	}
}

func (f *fakeRekeyUI) DelegateRekeyUI(ctx context.Context) (int, error) {
	f.sessionID++
	return f.sessionID, nil
}

func (f *fakeRekeyUI) Refresh(ctx context.Context, arg keybase1.RefreshArg) error {
	f.refreshArgs = append(f.refreshArgs, arg)
	select {
	case f.notifyRefresh <- true:
	default:
	}
	return nil
}

func fakeScoreProblemFolders(g *libkb.GlobalContext, existing keybase1.ProblemSet) (keybase1.ProblemSet, error) {
	// always return empty ProblemSet
	return keybase1.ProblemSet{}, nil
}
<|endoftext|>


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

package tests

import (
	"bytes"
	"context"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"github.com/gofrs/uuid"
	"github.com/golang/protobuf/jsonpb"
	"github.com/heroiclabs/nakama/api"
	"github.com/heroiclabs/nakama/apigrpc"
	"github.com/heroiclabs/nakama/rtapi"
	"github.com/heroiclabs/nakama/server"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"os"
	"strings"
	"testing"
)

var (
	logger          = NewConsoleLogger(os.Stdout, true)
	config          = server.NewConfig(logger)
	jsonpbMarshaler = &jsonpb.Marshaler{
		EnumsAsInts:  true,
		EmitDefaults: false,
		Indent:       "",
		OrigName:     true,
	}
	jsonpbUnmarshaler = &jsonpb.Unmarshaler{
		AllowUnknownFields: false,
	}
)

type DummyMessageRouter struct{}

func (d *DummyMessageRouter) SendToPresenceIDs(*zap.Logger, []*server.PresenceID, bool, uint8, *rtapi.Envelope) {
}
func (d *DummyMessageRouter) SendToStream(*zap.Logger, server.PresenceStream, *rtapi.Envelope) {}

type DummySession struct {
	messages []*rtapi.Envelope
	uid      uuid.UUID
}

func (d *DummySession) Logger() *zap.Logger {
	return logger
}
func (d *DummySession) ID() uuid.UUID {
	return uuid.Must(uuid.NewV4())
}
func (d *DummySession) UserID() uuid.UUID {
	return d.uid
}
func (d *DummySession) Username() string {
	return ""
}
func (d *DummySession) SetUsername(string) {}
func (d *DummySession) Expiry() int64 {
	return int64(0)
}
func (d *DummySession) Consume(func(logger *zap.Logger, session server.Session, envelope *rtapi.Envelope) bool) {
}
func (d *DummySession) Format() server.SessionFormat {
	return server.SessionFormatJson
}
func (d *DummySession) ClientIP() string {
	return ""
}
func (d *DummySession) ClientPort() string {
	return ""
}
func (d *DummySession) Context() context.Context {
	return context.Background()
}
func (d *DummySession) Send(isStream bool, mode uint8, envelope *rtapi.Envelope) error {
	d.messages = append(d.messages, envelope)
	return nil
}
func (d *DummySession) SendBytes(isStream bool, mode uint8, payload []byte) error {
	envelope := &rtapi.Envelope{}
	jsonpbUnmarshaler.Unmarshal(bytes.NewReader(payload), envelope)
	d.messages = append(d.messages, envelope)
	return nil
}

func (d *DummySession) Close() {}

type loggerEnabler struct{}

func (l *loggerEnabler) Enabled(level zapcore.Level) bool {
	return true
}

func NewConsoleLogger(output *os.File, verbose bool) *zap.Logger {
	consoleEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		EncodeLevel:    zapcore.CapitalColorLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	})

	core := zapcore.NewCore(consoleEncoder, output, &loggerEnabler{})
	options := []zap.Option{zap.AddStacktrace(zap.ErrorLevel)}

	return zap.New(core, options...)
}

func NewDB(t *testing.T) *sql.DB {
	db, err := sql.Open("postgres", "postgresql://root@127.0.0.1:26257/nakama?sslmode=disable")
	if err != nil {
		t.Fatal("Error connecting to database", err)
	}
	err = db.Ping()
	if err != nil {
		t.Fatal("Error pinging database", err)
	}
	return db
}

func GenerateString() string {
	return uuid.Must(uuid.NewV4()).String()
}

func InsertUser(t *testing.T, db *sql.DB, uid uuid.UUID) {
	if _, err := db.Exec(`
INSERT INTO users (id, username)
VALUES ($1, $2)
ON CONFLICT(id) DO NOTHING`, uid, uid.String()); err != nil {
		t.Fatal("Could not insert new user.", err)
	}
}

func NewAPIServer(t *testing.T, runtime *server.Runtime) (*server.ApiServer, *server.Pipeline) {
	db := NewDB(t)
	router := &DummyMessageRouter{}
	tracker := &server.LocalTracker{}
	pipeline := server.NewPipeline(logger, config, db, jsonpbMarshaler, jsonpbUnmarshaler, nil, nil, nil, tracker, router, runtime)
	apiServer := server.StartApiServer(logger, logger, db, jsonpbMarshaler, jsonpbUnmarshaler, config, nil, nil, nil, nil, nil, nil, tracker, router, pipeline, runtime)
	return apiServer, pipeline
}

func NewSession(t *testing.T, customID string) (*grpc.ClientConn, apigrpc.NakamaClient, *api.Session, context.Context) {
	ctx := context.Background()
	outgoingCtx := metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{
		"authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("defaultkey:")),
	}))
	conn, err := grpc.DialContext(outgoingCtx, "localhost:7349", grpc.WithInsecure())
	if err != nil {
		t.Fatal(err)
	}

	client := apigrpc.NewNakamaClient(conn)
	session, err := client.AuthenticateCustom(outgoingCtx, &api.AuthenticateCustomRequest{
		Account: &api.AccountCustom{
			Id: customID,
		},
		Username: GenerateString(),
	})
	if err != nil {
		t.Fatal(err)
	}

	return conn, client, session, outgoingCtx
}

func NewAuthenticatedAPIClient(t *testing.T, customID string) (*grpc.ClientConn, apigrpc.NakamaClient, *api.Session, context.Context) {
	conn, _, session, _ := NewSession(t, customID)
	conn.Close()

	ctx := context.Background()
	outgoingCtx := metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{
		"authorization": "Bearer " + session.Token,
	}))
	conn, err := grpc.DialContext(outgoingCtx, "localhost:7349", grpc.WithInsecure())
	if err != nil {
		t.Fatal(err)
	}

	client := apigrpc.NewNakamaClient(conn)
	return conn, client, session, outgoingCtx
}

func UserIDFromSession(session *api.Session) (uuid.UUID, error) {
	parts := strings.Split(session.Token, ".")
	content, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return uuid.Nil, err
	}

	data := make(map[string]interface{}, 0)
	err = json.Unmarshal(content, &data)
	if err != nil {
		return uuid.Nil, err
	}

	return uuid.FromString(data["uid"].(string))
}
<commit_msg>Update mock struct in tests.<commit_after>// Copyright 2017 The Nakama Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tests

import (
	"bytes"
	"context"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"github.com/gofrs/uuid"
	"github.com/golang/protobuf/jsonpb"
	"github.com/heroiclabs/nakama/api"
	"github.com/heroiclabs/nakama/apigrpc"
	"github.com/heroiclabs/nakama/rtapi"
	"github.com/heroiclabs/nakama/server"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"os"
	"strings"
	"testing"
)

var (
	logger          = NewConsoleLogger(os.Stdout, true)
	config          = server.NewConfig(logger)
	jsonpbMarshaler = &jsonpb.Marshaler{
		EnumsAsInts:  true,
		EmitDefaults: false,
		Indent:       "",
		OrigName:     true,
	}
	jsonpbUnmarshaler = &jsonpb.Unmarshaler{
		AllowUnknownFields: false,
	}
)

type DummyMessageRouter struct{}

func (d *DummyMessageRouter) SendDeferred(*zap.Logger, bool, uint8, []*server.DeferredMessage) {
	panic("unused")
}

func (d *DummyMessageRouter) SendToPresenceIDs(*zap.Logger, []*server.PresenceID, bool, uint8, *rtapi.Envelope) {
}
func (d *DummyMessageRouter) SendToStream(*zap.Logger, server.PresenceStream, *rtapi.Envelope) {}

type DummySession struct {
	messages []*rtapi.Envelope
	uid      uuid.UUID
}

func (d *DummySession) Logger() *zap.Logger {
	return logger
}
func (d *DummySession) ID() uuid.UUID {
	return uuid.Must(uuid.NewV4())
}
func (d *DummySession) UserID() uuid.UUID {
	return d.uid
}
func (d *DummySession) Username() string {
	return ""
}
func (d *DummySession) SetUsername(string) {}
func (d *DummySession) Expiry() int64 {
	return int64(0)
}
func (d *DummySession) Consume(func(logger *zap.Logger, session server.Session, envelope *rtapi.Envelope) bool) {
}
func (d *DummySession) Format() server.SessionFormat {
	return server.SessionFormatJson
}
func (d *DummySession) ClientIP() string {
	return ""
}
func (d *DummySession) ClientPort() string {
	return ""
}
func (d *DummySession) Context() context.Context {
	return context.Background()
}
func (d *DummySession) Send(isStream bool, mode uint8, envelope *rtapi.Envelope) error {
	d.messages = append(d.messages, envelope)
	return nil
}
func (d *DummySession) SendBytes(isStream bool, mode uint8, payload []byte) error {
	envelope := &rtapi.Envelope{}
	jsonpbUnmarshaler.Unmarshal(bytes.NewReader(payload), envelope)
	d.messages = append(d.messages, envelope)
	return nil
}

func (d *DummySession) Close() {}

type loggerEnabler struct{}

func (l *loggerEnabler) Enabled(level zapcore.Level) bool {
	return true
}

func NewConsoleLogger(output *os.File, verbose bool) *zap.Logger {
	consoleEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		EncodeLevel:    zapcore.CapitalColorLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	})

	core := zapcore.NewCore(consoleEncoder, output, &loggerEnabler{})
	options := []zap.Option{zap.AddStacktrace(zap.ErrorLevel)}

	return zap.New(core, options...)
}

func NewDB(t *testing.T) *sql.DB {
	db, err := sql.Open("postgres", "postgresql://root@127.0.0.1:26257/nakama?sslmode=disable")
	if err != nil {
		t.Fatal("Error connecting to database", err)
	}
	err = db.Ping()
	if err != nil {
		t.Fatal("Error pinging database", err)
	}
	return db
}

func GenerateString() string {
	return uuid.Must(uuid.NewV4()).String()
}

func InsertUser(t *testing.T, db *sql.DB, uid uuid.UUID) {
	if _, err := db.Exec(`
INSERT INTO users (id, username)
VALUES ($1, $2)
ON CONFLICT(id) DO NOTHING`, uid, uid.String()); err != nil {
		t.Fatal("Could not insert new user.", err)
	}
}

func NewAPIServer(t *testing.T, runtime *server.Runtime) (*server.ApiServer, *server.Pipeline) {
	db := NewDB(t)
	router := &DummyMessageRouter{}
	tracker := &server.LocalTracker{}
	pipeline := server.NewPipeline(logger, config, db, jsonpbMarshaler, jsonpbUnmarshaler, nil, nil, nil, tracker, router, runtime)
	apiServer := server.StartApiServer(logger, logger, db, jsonpbMarshaler, jsonpbUnmarshaler, config, nil, nil, nil, nil, nil, nil, tracker, router, pipeline, runtime)
	return apiServer, pipeline
}

func NewSession(t *testing.T, customID string) (*grpc.ClientConn, apigrpc.NakamaClient, *api.Session, context.Context) {
	ctx := context.Background()
	outgoingCtx := metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{
		"authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("defaultkey:")),
	}))
	conn, err := grpc.DialContext(outgoingCtx, "localhost:7349", grpc.WithInsecure())
	if err != nil {
		t.Fatal(err)
	}

	client := apigrpc.NewNakamaClient(conn)
	session, err := client.AuthenticateCustom(outgoingCtx, &api.AuthenticateCustomRequest{
		Account: &api.AccountCustom{
			Id: customID,
		},
		Username: GenerateString(),
	})
	if err != nil {
		t.Fatal(err)
	}

	return conn, client, session, outgoingCtx
}

func NewAuthenticatedAPIClient(t *testing.T, customID string) (*grpc.ClientConn, apigrpc.NakamaClient, *api.Session, context.Context) {
	conn, _, session, _ := NewSession(t, customID)
	conn.Close()

	ctx := context.Background()
	outgoingCtx := metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{
		"authorization": "Bearer " + session.Token,
	}))
	conn, err := grpc.DialContext(outgoingCtx, "localhost:7349", grpc.WithInsecure())
	if err != nil {
		t.Fatal(err)
	}

	client := apigrpc.NewNakamaClient(conn)
	return conn, client, session, outgoingCtx
}

func UserIDFromSession(session *api.Session) (uuid.UUID, error) {
	parts := strings.Split(session.Token, ".")
	content, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return uuid.Nil, err
	}

	data := make(map[string]interface{}, 0)
	err = json.Unmarshal(content, &data)
	if err != nil {
		return uuid.Nil, err
	}

	return uuid.FromString(data["uid"].(string))
}
<|endoftext|>


<commit_before>package gobusters3

import (
	"bufio"
	"bytes"
	"context"
	"encoding/xml"
	"fmt"
	"net/http"
	"regexp"
	"strings"
	"text/tabwriter"

	"github.com/OJ/gobuster/v3/libgobuster"
)

// GobusterS3 is the main type to implement the interface
type GobusterS3 struct {
	options     *OptionsS3
	globalopts  *libgobuster.Options
	http        *libgobuster.HTTPClient
	bucketRegex *regexp.Regexp
}

// NewGobusterS3 creates a new initialized GobusterS3
func NewGobusterS3(cont context.Context, globalopts *libgobuster.Options, opts *OptionsS3) (*GobusterS3, error) {
	if globalopts == nil {
		return nil, fmt.Errorf("please provide valid global options")
	}

	if opts == nil {
		return nil, fmt.Errorf("please provide valid plugin options")
	}

	g := GobusterS3{
		options:    opts,
		globalopts: globalopts,
	}

	basicOptions := libgobuster.BasicHTTPOptions{
		Proxy:     opts.Proxy,
		Timeout:   opts.Timeout,
		UserAgent: opts.UserAgent,
	}

	httpOpts := libgobuster.HTTPOptions{
		BasicHTTPOptions: basicOptions,
		// needed so we can list bucket contents
		FollowRedirect: true,
	}

	h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
	if err != nil {
		return nil, err
	}
	g.http = h
	g.bucketRegex = regexp.MustCompile(`^[a-z0-9\-.]{3,63}$`)

	return &g, nil
}

// Name should return the name of the plugin
func (s *GobusterS3) Name() string {
	return "S3 bucket enumeration"
}

// RequestsPerRun returns the number of requests this plugin makes per single wordlist item
func (s *GobusterS3) RequestsPerRun() int {
	return 1
}

// PreRun is the pre run implementation of GobusterS3
func (s *GobusterS3) PreRun() error {
	return nil
}

// Run is the process implementation of GobusterS3
func (s *GobusterS3) Run(word string, resChannel chan<- libgobuster.Result) error {
	// only check for valid bucket names
	if !s.isValidBucketName(word) {
		return nil
	}

	bucketURL := fmt.Sprintf("https://%s.s3.amazonaws.com/?max-keys=%d", word, s.options.MaxFilesToList)
	status, _, _, body, err := s.http.Request(bucketURL, libgobuster.RequestOptions{ReturnBody: true})
	if err != nil {
		return err
	}

	if status == nil || body == nil {
		return nil
	}

	// looks like 404 and 400 are the only negative status codes
	found := false
	switch *status {
	case http.StatusBadRequest:
	case http.StatusNotFound:
		found = false
	case http.StatusOK:
		// listing enabled
		found = true
		// parse xml
	default:
		// default to found as we use negative status codes
		found = true
	}

	// nothing found, bail out
	// may add the result later if we want to enable verbose output
	if !found {
		return nil
	}

	extraStr := ""
	if s.globalopts.Verbose {
		// get status
		if bytes.Contains(body, []byte("<Error>")) {
			awsError := AWSError{}
			err := xml.Unmarshal(body, &awsError)
			if err != nil {
				return fmt.Errorf("could not parse error xml: %w", err)
			}
			// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList
			extraStr = fmt.Sprintf("Error: %s (%s)", awsError.Message, awsError.Code)
		} else if bytes.Contains(body, []byte("<ListBucketResult ")) {
			// bucket listing enabled
			awsListing := AWSListing{}
			err := xml.Unmarshal(body, &awsListing)
			if err != nil {
				return fmt.Errorf("could not parse result xml: %w", err)
			}
			extraStr = "Bucket Listing enabled: "
			for _, x := range awsListing.Contents {
				extraStr += fmt.Sprintf("%s (%db), ", x.Key, x.Size)
			}
			extraStr = strings.TrimRight(extraStr, ", ")
		}
	}

	resChannel <- Result{
		Found:      found,
		BucketName: word,
		Status:     extraStr,
	}

	return nil
}

// GetConfigString returns the string representation of the current config
func (s *GobusterS3) GetConfigString() (string, error) {
	var buffer bytes.Buffer
	bw := bufio.NewWriter(&buffer)
	tw := tabwriter.NewWriter(bw, 0, 5, 3, ' ', 0)
	o := s.options

	if _, err := fmt.Fprintf(tw, "[+] Threads:\t%d\n", s.globalopts.Threads); err != nil {
		return "", err
	}

	if s.globalopts.Delay > 0 {
		if _, err := fmt.Fprintf(tw, "[+] Delay:\t%s\n", s.globalopts.Delay); err != nil {
			return "", err
		}
	}

	wordlist := "stdin (pipe)"
	if s.globalopts.Wordlist != "-" {
		wordlist = s.globalopts.Wordlist
	}
	if _, err := fmt.Fprintf(tw, "[+] Wordlist:\t%s\n", wordlist); err != nil {
		return "", err
	}

	if s.globalopts.PatternFile != "" {
		if _, err := fmt.Fprintf(tw, "[+] Patterns:\t%s (%d entries)\n", s.globalopts.PatternFile, len(s.globalopts.Patterns)); err != nil {
			return "", err
		}
	}

	if o.Proxy != "" {
		if _, err := fmt.Fprintf(tw, "[+] Proxy:\t%s\n", o.Proxy); err != nil {
			return "", err
		}
	}

	if o.UserAgent != "" {
		if _, err := fmt.Fprintf(tw, "[+] User Agent:\t%s\n", o.UserAgent); err != nil {
			return "", err
		}
	}

	if _, err := fmt.Fprintf(tw, "[+] Timeout:\t%s\n", o.Timeout.String()); err != nil {
		return "", err
	}

	if s.globalopts.Verbose {
		if _, err := fmt.Fprintf(tw, "[+] Verbose:\ttrue\n"); err != nil {
			return "", err
		}
	}

	if _, err := fmt.Fprintf(tw, "[+] Maximum files to list:\t%d\n", o.MaxFilesToList); err != nil {
		return "", err
	}

	if err := tw.Flush(); err != nil {
		return "", fmt.Errorf("error on tostring: %w", err)
	}

	if err := bw.Flush(); err != nil {
		return "", fmt.Errorf("error on tostring: %w", err)
	}

	return strings.TrimSpace(buffer.String()), nil
}

// https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
func (s *GobusterS3) isValidBucketName(bucketName string) bool {
	if !s.bucketRegex.MatchString(bucketName) {
		return false
	}
	if strings.HasSuffix(bucketName, "-") ||
		strings.HasPrefix(bucketName, ".") ||
		strings.HasPrefix(bucketName, "-") ||
		strings.Contains(bucketName, "..") ||
		strings.Contains(bucketName, ".-") ||
		strings.Contains(bucketName, "-.") {
		return false
	}
	return true
}
<commit_msg>forgot one file<commit_after>package gobusters3

import (
	"bufio"
	"bytes"
	"context"
	"encoding/xml"
	"fmt"
	"net/http"
	"regexp"
	"strings"
	"text/tabwriter"

	"github.com/OJ/gobuster/v3/libgobuster"
)

// GobusterS3 is the main type to implement the interface
type GobusterS3 struct {
	options     *OptionsS3
	globalopts  *libgobuster.Options
	http        *libgobuster.HTTPClient
	bucketRegex *regexp.Regexp
}

// NewGobusterS3 creates a new initialized GobusterS3
func NewGobusterS3(cont context.Context, globalopts *libgobuster.Options, opts *OptionsS3) (*GobusterS3, error) {
	if globalopts == nil {
		return nil, fmt.Errorf("please provide valid global options")
	}

	if opts == nil {
		return nil, fmt.Errorf("please provide valid plugin options")
	}

	g := GobusterS3{
		options:    opts,
		globalopts: globalopts,
	}

	basicOptions := libgobuster.BasicHTTPOptions{
		Proxy:           opts.Proxy,
		Timeout:         opts.Timeout,
		UserAgent:       opts.UserAgent,
		NoTLSValidation: opts.NoTLSValidation,
	}

	httpOpts := libgobuster.HTTPOptions{
		BasicHTTPOptions: basicOptions,
		// needed so we can list bucket contents
		FollowRedirect: true,
	}

	h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
	if err != nil {
		return nil, err
	}
	g.http = h
	g.bucketRegex = regexp.MustCompile(`^[a-z0-9\-.]{3,63}$`)

	return &g, nil
}

// Name should return the name of the plugin
func (s *GobusterS3) Name() string {
	return "S3 bucket enumeration"
}

// RequestsPerRun returns the number of requests this plugin makes per single wordlist item
func (s *GobusterS3) RequestsPerRun() int {
	return 1
}

// PreRun is the pre run implementation of GobusterS3
func (s *GobusterS3) PreRun() error {
	return nil
}

// Run is the process implementation of GobusterS3
func (s *GobusterS3) Run(word string, resChannel chan<- libgobuster.Result) error {
	// only check for valid bucket names
	if !s.isValidBucketName(word) {
		return nil
	}

	bucketURL := fmt.Sprintf("https://%s.s3.amazonaws.com/?max-keys=%d", word, s.options.MaxFilesToList)
	status, _, _, body, err := s.http.Request(bucketURL, libgobuster.RequestOptions{ReturnBody: true})
	if err != nil {
		return err
	}

	if status == nil || body == nil {
		return nil
	}

	// looks like 404 and 400 are the only negative status codes
	found := false
	switch *status {
	case http.StatusBadRequest:
	case http.StatusNotFound:
		found = false
	case http.StatusOK:
		// listing enabled
		found = true
		// parse xml
	default:
		// default to found as we use negative status codes
		found = true
	}

	// nothing found, bail out
	// may add the result later if we want to enable verbose output
	if !found {
		return nil
	}

	extraStr := ""
	if s.globalopts.Verbose {
		// get status
		if bytes.Contains(body, []byte("<Error>")) {
			awsError := AWSError{}
			err := xml.Unmarshal(body, &awsError)
			if err != nil {
				return fmt.Errorf("could not parse error xml: %w", err)
			}
			// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList
			extraStr = fmt.Sprintf("Error: %s (%s)", awsError.Message, awsError.Code)
		} else if bytes.Contains(body, []byte("<ListBucketResult ")) {
			// bucket listing enabled
			awsListing := AWSListing{}
			err := xml.Unmarshal(body, &awsListing)
			if err != nil {
				return fmt.Errorf("could not parse result xml: %w", err)
			}
			extraStr = "Bucket Listing enabled: "
			for _, x := range awsListing.Contents {
				extraStr += fmt.Sprintf("%s (%db), ", x.Key, x.Size)
			}
			extraStr = strings.TrimRight(extraStr, ", ")
		}
	}

	resChannel <- Result{
		Found:      found,
		BucketName: word,
		Status:     extraStr,
	}

	return nil
}

// GetConfigString returns the string representation of the current config
func (s *GobusterS3) GetConfigString() (string, error) {
	var buffer bytes.Buffer
	bw := bufio.NewWriter(&buffer)
	tw := tabwriter.NewWriter(bw, 0, 5, 3, ' ', 0)
	o := s.options

	if _, err := fmt.Fprintf(tw, "[+] Threads:\t%d\n", s.globalopts.Threads); err != nil {
		return "", err
	}

	if s.globalopts.Delay > 0 {
		if _, err := fmt.Fprintf(tw, "[+] Delay:\t%s\n", s.globalopts.Delay); err != nil {
			return "", err
		}
	}

	wordlist := "stdin (pipe)"
	if s.globalopts.Wordlist != "-" {
		wordlist = s.globalopts.Wordlist
	}
	if _, err := fmt.Fprintf(tw, "[+] Wordlist:\t%s\n", wordlist); err != nil {
		return "", err
	}

	if s.globalopts.PatternFile != "" {
		if _, err := fmt.Fprintf(tw, "[+] Patterns:\t%s (%d entries)\n", s.globalopts.PatternFile, len(s.globalopts.Patterns)); err != nil {
			return "", err
		}
	}

	if o.Proxy != "" {
		if _, err := fmt.Fprintf(tw, "[+] Proxy:\t%s\n", o.Proxy); err != nil {
			return "", err
		}
	}

	if o.UserAgent != "" {
		if _, err := fmt.Fprintf(tw, "[+] User Agent:\t%s\n", o.UserAgent); err != nil {
			return "", err
		}
	}

	if _, err := fmt.Fprintf(tw, "[+] Timeout:\t%s\n", o.Timeout.String()); err != nil {
		return "", err
	}

	if s.globalopts.Verbose {
		if _, err := fmt.Fprintf(tw, "[+] Verbose:\ttrue\n"); err != nil {
			return "", err
		}
	}

	if _, err := fmt.Fprintf(tw, "[+] Maximum files to list:\t%d\n", o.MaxFilesToList); err != nil {
		return "", err
	}

	if err := tw.Flush(); err != nil {
		return "", fmt.Errorf("error on tostring: %w", err)
	}

	if err := bw.Flush(); err != nil {
		return "", fmt.Errorf("error on tostring: %w", err)
	}

	return strings.TrimSpace(buffer.String()), nil
}

// https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
func (s *GobusterS3) isValidBucketName(bucketName string) bool {
	if !s.bucketRegex.MatchString(bucketName) {
		return false
	}
	if strings.HasSuffix(bucketName, "-") ||
		strings.HasPrefix(bucketName, ".") ||
		strings.HasPrefix(bucketName, "-") ||
		strings.Contains(bucketName, "..") ||
		strings.Contains(bucketName, ".-") ||
		strings.Contains(bucketName, "-.") {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "time"
    "strconv"
    "github.com/JokerQyou/rpi"
    "github.com/JokerQyou/rpi/pcd8544"
    "github.com/kidoman/embd"
    "github.com/kidoman/embd/sensor/bmp085"

    _ "github.com/kidoman/embd/host/rpi"
)

const (
    SCLK = 0
    DIN = 1
    DC = 2
    CS = 3
    RST = 4
    CONTRAST = 60
)

func init() {
    rpi.WiringPiSetup()
    pcd8544.LCDInit(SCLK, DIN, DC, CS, RST, CONTRAST)
    pcd8544.LCDclear()
    pcd8544.LCDdisplay()

}

func gpio_cleanup() {
    pcd8544.LCDclear()
    pcd8544.LCDdisplay()

    rpi.PinMode(SCLK, rpi.INPUT)
    rpi.PinMode(DIN, rpi.INPUT)
    rpi.PinMode(DC, rpi.INPUT)
    rpi.PinMode(CS, rpi.INPUT)
    rpi.PinMode(RST, rpi.INPUT)
}

func get_time() (string, string) {
    t := time.Now()
    return t.Format("15:04:05"), t.Format("01-02 Mon")
}

func main() {
    if err := embd.InitI2C(); err != nil {
        panic(err)
    }
    defer embd.CloseI2C()

    bus := embd.NewI2CBus(1)
    baro := bmp085.New(bus)
    defer baro.Close()

    keep_running := true
    for keep_running {
        // Get temperature
        temp, err := baro.Temperature()
        if err != nil {
            gpio_cleanup()
            panic(err)
        }
        temp_str := fmt.Sprint(strconv.FormatFloat(temp, 'f', 2, 64), " C")

        pcd8544.LCDdrawrect(6 - 1, 6 - 1, pcd8544.LCDWIDTH - 6, pcd8544.LCDHEIGHT - 6, pcd8544.BLACK)
        time_str, date_str := get_time()
        pcd8544.LCDdrawstring(20, 12, time_str)
        pcd8544.LCDdrawstring(18, 24, date_str)
        pcd8544.LCDdrawstring(20, 36, temp_str)
        pcd8544.LCDdisplay()
        // wait for 1 sec
        time.Sleep(time.Second)
    }
}
<commit_msg>Go through gofmt and goimports before saving<commit_after>package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/JokerQyou/rpi"
	"github.com/JokerQyou/rpi/pcd8544"
	"github.com/kidoman/embd"
	"github.com/kidoman/embd/sensor/bmp085"

	_ "github.com/kidoman/embd/host/rpi"
)

const (
	SCLK     = 0
	DIN      = 1
	DC       = 2
	CS       = 3
	RST      = 4
	CONTRAST = 60
)

func init() {
	rpi.WiringPiSetup()
	pcd8544.LCDInit(SCLK, DIN, DC, CS, RST, CONTRAST)
	pcd8544.LCDclear()
	pcd8544.LCDdisplay()

}

func gpio_cleanup() {
	pcd8544.LCDclear()
	pcd8544.LCDdisplay()

	rpi.PinMode(SCLK, rpi.INPUT)
	rpi.PinMode(DIN, rpi.INPUT)
	rpi.PinMode(DC, rpi.INPUT)
	rpi.PinMode(CS, rpi.INPUT)
	rpi.PinMode(RST, rpi.INPUT)
}

func get_time() (string, string) {
	t := time.Now()
	return t.Format("15:04:05"), t.Format("01-02 Mon")
}

func main() {
	if err := embd.InitI2C(); err != nil {
		panic(err)
	}
	defer embd.CloseI2C()

	bus := embd.NewI2CBus(1)
	baro := bmp085.New(bus)
	defer baro.Close()

	keep_running := true
	for keep_running {
		// Get temperature
		temp, err := baro.Temperature()
		if err != nil {
			gpio_cleanup()
			panic(err)
		}
		temp_str := fmt.Sprint(strconv.FormatFloat(temp, 'f', 2, 64), " C")

		pcd8544.LCDdrawrect(6-1, 6-1, pcd8544.LCDWIDTH-6, pcd8544.LCDHEIGHT-6, pcd8544.BLACK)
		time_str, date_str := get_time()
		pcd8544.LCDdrawstring(20, 12, time_str)
		pcd8544.LCDdrawstring(18, 24, date_str)
		pcd8544.LCDdrawstring(20, 36, temp_str)
		pcd8544.LCDdisplay()
		// wait for 1 sec
		time.Sleep(time.Second)
	}
}
<|endoftext|>


<commit_before>package birthdays

import (
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"time"

	"github.com/asdine/storm"
	"github.com/asdine/storm/q"
	"github.com/bearbin/go-age"
	"github.com/tucnak/telebot"

	"github.com/focusshifter/muxgoob/registry"
)

type BirthdaysPlugin struct {
}

type BirthdayNotify struct {
	ID       int    `storm:"id,increment"`
	Username string `storm:"index"`
	Year     int
}

var db *storm.DB
var rng *rand.Rand
var birthdays map[string]time.Time

func init() {
	registry.RegisterPlugin(&BirthdaysPlugin{})
}

func (p *BirthdaysPlugin) Start(sharedDb *storm.DB) {
	db = sharedDb
	rng = rand.New(rand.NewSource(time.Now().UnixNano()))

	birthdays = make(map[string]time.Time)

	loc := registry.Config.TimeLoc

	for username, birthday := range registry.Config.Birthdays {
		t, _ := time.ParseInLocation("2006-01-02", birthday, loc)
		birthdays[username] = t
	}
}

func (p *BirthdaysPlugin) Process(message *telebot.Message) {
	todaysBirthday(message)
	nextBirthday(message)
}

func nextBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	birthdayExp := regexp.MustCompile(`(?i)^\!(др|birthda(y|ys))$`)

	switch {
	case birthdayExp.MatchString(message.Text):
		cur := time.Now().In(loc)
		curDay := cur.YearDay()

		diff := time.Date(cur.Year(), time.December, 31, 0, 0, 0, 0, time.Local).YearDay()
		curDiff := diff
		curBirthday := ""
		curUsername := ""

		for username, birthday := range birthdays {
			birthdayDay := time.Date(cur.Year(), birthday.Month(), birthday.Day(), 0, 0, 0, 0, time.Local).YearDay()
			diff = birthdayDay - curDay
			if diff > 0 {
				if diff == curDiff {

				} else if diff < curDiff {
					curDiff = diff
					curUsername = username
					curBirthday = birthday.Format("02.01")
				}
			}
		}

		if curUsername != "" {
			bot.Send(message.Chat, "Prepare the 🎂 for @"+curUsername+" on "+curBirthday, &telebot.SendOptions{})
		} else {
			bot.Send(message.Chat, "No upcoming birthdays", &telebot.SendOptions{})
		}
	}
}

func todaysBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	cur := time.Now().In(loc)

	for username, birthday := range birthdays {
		if cur.Month() == birthday.Month() && cur.Day() == birthday.Day() && notMentioned(username, cur.Year(), message) {
			age := strconv.Itoa(age.AgeAt(birthday, cur))
			bot.Send(message.Chat, "Hooray! 🎉 @"+username+" is turning "+age+"! 🎂", &telebot.SendOptions{})
		}
	}
}

func notMentioned(username string, year int, message *telebot.Message) bool {
	chat := db.From(strconv.FormatInt(message.Chat.ID, 10))

	count, _ := chat.Select(q.And(q.Eq("Username", username), q.Eq("Year", year))).Count(&BirthdayNotify{})

	if count > 0 {
		return false
	}

	log.Println("Brithday: notify " + username)

	newNotify := BirthdayNotify{Username: username, Year: year}
	chat.Save(&newNotify)

	return true
}
<commit_msg>Fixed multibirthdays<commit_after>package birthdays

import (
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"time"

	"github.com/asdine/storm"
	"github.com/asdine/storm/q"
	"github.com/bearbin/go-age"
	"github.com/tucnak/telebot"

	"github.com/focusshifter/muxgoob/registry"
)

type BirthdaysPlugin struct {
}

type BirthdayNotify struct {
	ID       int    `storm:"id,increment"`
	Username string `storm:"index"`
	Year     int
}

var db *storm.DB
var rng *rand.Rand
var birthdays map[string]time.Time

func init() {
	registry.RegisterPlugin(&BirthdaysPlugin{})
}

func (p *BirthdaysPlugin) Start(sharedDb *storm.DB) {
	db = sharedDb
	rng = rand.New(rand.NewSource(time.Now().UnixNano()))

	birthdays = make(map[string]time.Time)

	loc := registry.Config.TimeLoc

	for username, birthday := range registry.Config.Birthdays {
		t, _ := time.ParseInLocation("2006-01-02", birthday, loc)
		birthdays[username] = t
	}
}

func (p *BirthdaysPlugin) Process(message *telebot.Message) {
	todaysBirthday(message)
	nextBirthday(message)
}

func nextBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	birthdayExp := regexp.MustCompile(`(?i)^\!(др|birthda(y|ys))$`)

	switch {
	case birthdayExp.MatchString(message.Text):
		cur := time.Now().In(loc)
		curDay := cur.YearDay()

		diff := time.Date(cur.Year(), time.December, 31, 0, 0, 0, 0, time.Local).YearDay()
		curDiff := diff
		curBirthday := ""
		curUsername := ""

		for username, birthday := range birthdays {
			birthdayDay := time.Date(cur.Year(), birthday.Month(), birthday.Day(), 0, 0, 0, 0, time.Local).YearDay()
			diff = birthdayDay - curDay
			if diff > 0 {
				if diff == curDiff {
					curUsername += ", @" + username
				} else if diff < curDiff {
					curDiff = diff
					curUsername = username
					curBirthday = birthday.Format("02.01")
				}
			}
		}

		if curUsername != "" {
			bot.Send(message.Chat, "Prepare the 🎂 for @"+curUsername+" on "+curBirthday, &telebot.SendOptions{})
		} else {
			bot.Send(message.Chat, "No upcoming birthdays", &telebot.SendOptions{})
		}
	}
}

func todaysBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	cur := time.Now().In(loc)

	for username, birthday := range birthdays {
		if cur.Month() == birthday.Month() && cur.Day() == birthday.Day() && notMentioned(username, cur.Year(), message) {
			age := strconv.Itoa(age.AgeAt(birthday, cur))
			bot.Send(message.Chat, "Hooray! 🎉 @"+username+" is turning "+age+"! 🎂", &telebot.SendOptions{})
		}
	}
}

func notMentioned(username string, year int, message *telebot.Message) bool {
	chat := db.From(strconv.FormatInt(message.Chat.ID, 10))

	count, _ := chat.Select(q.And(q.Eq("Username", username), q.Eq("Year", year))).Count(&BirthdayNotify{})

	if count > 0 {
		return false
	}

	log.Println("Brithday: notify " + username)

	newNotify := BirthdayNotify{Username: username, Year: year}
	chat.Save(&newNotify)

	return true
}
<|endoftext|>