<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fs

import (
	"fmt"
	"reflect"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/fs/inode"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

type fileSystem struct {
	fuseutil.NotImplementedFileSystem

	/////////////////////////
	// Dependencies
	/////////////////////////

	clock  timeutil.Clock
	bucket gcs.Bucket

	/////////////////////////
	// Mutable state
	/////////////////////////

	// When acquiring this lock, the caller must hold no inode or dir handle
	// locks.
	mu syncutil.InvariantMutex

	// The user and group owning everything in the file system.
	//
	// GUARDED_BY(Mu)
	uid uint32
	gid uint32

	// The collection of live inodes, keyed by inode ID. No ID less than
	// fuse.RootInodeID is ever used.
	//
	// TODO(jacobsa): Implement ForgetInode support in the fuse package, then
	// implement the method here and clean up these maps.
	//
	// INVARIANT: All values are of type *inode.DirInode or *inode.FileInode
	// INVARIANT: For all keys k, k >= fuse.RootInodeID
	// INVARIANT: For all keys k, inodes[k].ID() == k
	// INVARIANT: inodes[fuse.RootInodeID] is of type *inode.DirInode
	//
	// GUARDED_BY(mu)
	inodes map[fuse.InodeID]inode.Inode

	// The next inode ID to hand out. We assume that this will never overflow,
	// since even if we were handing out inode IDs at 4 GHz, it would still take
	// over a century to do so.
	//
	// INVARIANT: For all keys k in inodes, k < nextInodeID
	//
	// GUARDED_BY(mu)
	nextInodeID fuse.InodeID

	// An index of all directory inodes by Name().
	//
	// INVARIANT: For each key k, isDirName(k)
	// INVARIANT: For each key k, dirIndex[k].Name() == k
	// INVARIANT: The values are all and only the values of the inodes map of
	// type *inode.DirInode.
	//
	// GUARDED_BY(mu)
	dirIndex map[string]*inode.DirInode

	// An index of all file inodes by (Name(), SourceGeneration()) pairs.
	//
	// INVARIANT: For each key k, !isDirName(k)
	// INVARIANT: For each key k, fileIndex[k].Name() == k.name
	// INVARIANT: For each key k, fileIndex[k].SourceGeneration() == k.gen
	// INVARIANT: The values are all and only the values of the inodes map of
	// type *inode.FileInode.
	//
	// GUARDED_BY(mu)
	fileIndex map[nameAndGen]*inode.FileInode

	// The collection of live handles, keyed by handle ID.
	//
	// INVARIANT: All values are of type *dirHandle
	//
	// GUARDED_BY(mu)
	handles map[fuse.HandleID]interface{}

	// The next handle ID to hand out. We assume that this will never overflow.
	//
	// INVARIANT: For all keys k in handles, k < nextHandleID
	//
	// GUARDED_BY(mu)
	nextHandleID fuse.HandleID
}

type nameAndGen struct {
	name string
	gen  int64
}

// Create a fuse file system whose root directory is the root of the supplied
// bucket. The supplied clock will be used for cache invalidation, modification
// times, etc.
func NewFileSystem(
	clock timeutil.Clock,
	bucket gcs.Bucket) (ffs fuse.FileSystem, err error) {
	// Set up the basic struct.
	fs := &fileSystem{
		clock:       clock,
		bucket:      bucket,
		inodes:      make(map[fuse.InodeID]inode.Inode),
		nextInodeID: fuse.RootInodeID + 1,
		dirIndex:    make(map[string]*inode.DirInode),
		fileIndex:   make(map[nameAndGen]*inode.FileInode),
		handles:     make(map[fuse.HandleID]interface{}),
	}

	// Set up the root inode.
	root := inode.NewDirInode(bucket, fuse.RootInodeID, "")
	fs.inodes[fuse.RootInodeID] = root
	fs.dirIndex[""] = root

	// Set up invariant checking.
	fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)

	ffs = fs
	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func isDirName(name string) bool {
	return name == "" || name[len(name)-1] == '/'
}

func (fs *fileSystem) checkInvariants() {
	// Check inode keys.
	for id, _ := range fs.inodes {
		if id < fuse.RootInodeID || id >= fs.nextInodeID {
			panic(fmt.Sprintf("Illegal inode ID: %v", id))
		}
	}

	// Check the root inode.
	_ = fs.inodes[fuse.RootInodeID].(*inode.DirInode)

	// Check each inode, and the indexes over them. Keep a count of each type
	// seen.
	dirsSeen := 0
	filesSeen := 0
	for id, in := range fs.inodes {
		// Check the ID.
		if in.ID() != id {
			panic(fmt.Sprintf("ID mismatch: %v vs. %v", in.ID(), id))
		}

		// Check type-specific stuff.
		switch typed := in.(type) {
		case *inode.DirInode:
			dirsSeen++

			if !isDirName(typed.Name()) {
				panic(fmt.Sprintf("Unexpected directory name: %s", typed.Name()))
			}

			if fs.dirIndex[typed.Name()] != typed {
				panic(fmt.Sprintf("dirIndex mismatch: %s", typed.Name()))
			}

		case *inode.FileInode:
			filesSeen++

			if isDirName(typed.Name()) {
				panic(fmt.Sprintf("Unexpected file name: %s", typed.Name()))
			}

			nandg := nameAndGen{typed.Name(), typed.SourceGeneration()}
			if fs.fileIndex[nandg] != typed {
				panic(
					fmt.Sprintf(
						"fileIndex mismatch: %s, %v",
						typed.Name(),
						typed.SourceGeneration()))
			}

		default:
			panic(fmt.Sprintf("Unexpected inode type: %v", reflect.TypeOf(in)))
		}
	}

	// Make sure that the indexes are exhaustive.
	if len(fs.dirIndex) != dirsSeen {
		panic(
			fmt.Sprintf(
				"dirIndex length mismatch: %v vs. %v",
				len(fs.dirIndex),
				dirsSeen))
	}

	if len(fs.fileIndex) != filesSeen {
		panic(
			fmt.Sprintf(
				"fileIndex length mismatch: %v vs. %v",
				len(fs.fileIndex),
				dirsSeen))
	}

	// Check handles.
	for id, h := range fs.handles {
		if id >= fs.nextHandleID {
			panic(fmt.Sprintf("Illegal handle ID: %v", id))
		}

		_ = h.(*dirHandle)
	}
}

// Find the given inode and return it with its lock held for reading. Panic if
// it doesn't exist or is the wrong type.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCK_FUNCTION(in.mu)
func (fs *fileSystem) getDirForReadingOrDie(
	id fuse.InodeID) (in *inode.DirInode) {
	in = fs.inodes[id].(*inode.DirInode)
	in.Mu.RLock()
	return
}

// Get attributes for the given directory inode, fixing up ownership information.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCK_FUNCTION(d.mu)
func (fs *fileSystem) getAttributes(
	ctx context.Context,
	d *inode.DirInode) (attrs fuse.InodeAttributes, err error) {
	attrs, err = d.Attributes(ctx)
	if err != nil {
		return
	}

	attrs.Uid = fs.uid
	attrs.Gid = fs.gid

	return
}

// Find a directory inode for the given object record. Create one if there
// isn't already one available.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
func (fs *fileSystem) lookUpOrCreateDirInode(
	ctx context.Context,
	o *storage.Object) (in *inode.DirInode, err error) {
	// Do we already have an inode for this name?
	if in = fs.dirIndex[o.Name]; in != nil {
		return
	}

	// Mint an ID.
	id := fs.nextInodeID
	fs.nextInodeID++

	// Create and index an inode.
	in = inode.NewDirInode(fs.bucket, id, o.Name)
	fs.inodes[id] = in
	fs.dirIndex[in.Name()] = in

	return
}

// Find a file inode for the given object record. Create one if there isn't
// already one available.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
func (fs *fileSystem) lookUpOrCreateFileInode(
	ctx context.Context,
	o *storage.Object) (in *inode.FileInode, err error) {
	nandg := nameAndGen{
		name: o.Name,
		gen:  o.Generation,
	}

	// Do we already have an inode for this (name, generation) pair?
	if in = fs.fileIndex[nandg]; in != nil {
		return
	}

	// Mint an ID.
	id := fs.nextInodeID
	fs.nextInodeID++

	// Create and index an inode.
	in = inode.NewFileInode(fs.bucket, id, o)
	fs.inodes[id] = in
	fs.fileIndex[nandg] = in

	return
}

////////////////////////////////////////////////////////////////////////
// fuse.FileSystem methods
////////////////////////////////////////////////////////////////////////

func (fs *fileSystem) Init(
	ctx context.Context,
	req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
	resp = &fuse.InitResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Store the mounting user's info for later.
	fs.uid = req.Header.Uid
	fs.gid = req.Header.Gid

	return
}

func (fs *fileSystem) LookUpInode(
	ctx context.Context,
	req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
	resp = &fuse.LookUpInodeResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Find the parent directory in question.
	parent := fs.inodes[req.Parent].(*inode.DirInode)

	// Find a record for the child with the given name.
	o, err := parent.LookUpChild(ctx, req.Name)
	if err != nil {
		return
	}

	// Is the child a directory or a file?
	var in inode.Inode
	if isDirName(o.Name) {
		in, err = fs.lookUpOrCreateDirInode(ctx, o)
	} else {
		in, err = fs.lookUpOrCreateFileInode(ctx, o)
	}

	if err != nil {
		return
	}

	// Fill out the response.
	resp.Entry.Child = in.ID()
	if resp.Entry.Attributes, err = fs.getAttributes(ctx, in); err != nil {
		return
	}

	return
}

func (fs *fileSystem) GetInodeAttributes(
	ctx context.Context,
	req *fuse.GetInodeAttributesRequest) (
	resp *fuse.GetInodeAttributesResponse, err error) {
	resp = &fuse.GetInodeAttributesResponse{}

	fs.mu.RLock()
	defer fs.mu.RUnlock()

	// Find the inode.
	in := fs.inodes[req.Inode]

	// Grab its attributes.
	switch typed := in.(type) {
	case *inode.DirInode:
		resp.Attributes, err = fs.getAttributes(ctx, typed)
		if err != nil {
			err = fmt.Errorf("DirInode.Attributes: %v", err)
			return
		}

	default:
		panic(
			fmt.Sprintf(
				"Unknown inode type for ID %v: %v",
				req.Inode,
				reflect.TypeOf(in)))
	}

	return
}

func (fs *fileSystem) OpenDir(
	ctx context.Context,
	req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
	resp = &fuse.OpenDirResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the inode still exists and is a directory. If not, something has
	// screwed up because the VFS layer shouldn't have let us forget the inode
	// before opening it.
	in := fs.getDirForReadingOrDie(req.Inode)
	defer in.Mu.RUnlock()

	// Allocate a handle.
	handleID := fs.nextHandleID
	fs.nextHandleID++

	fs.handles[handleID] = newDirHandle(in)
	resp.Handle = handleID

	return
}

func (fs *fileSystem) ReadDir(
	ctx context.Context,
	req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
	fs.mu.RLock()
	defer fs.mu.RUnlock()

	// Find the handle.
	dh := fs.handles[req.Handle].(*dirHandle)
	dh.Mu.Lock()
	defer dh.Mu.Unlock()

	// Serve the request.
	resp, err = dh.ReadDir(ctx, req)

	return
}

func (fs *fileSystem) ReleaseDirHandle(
	ctx context.Context,
	req *fuse.ReleaseDirHandleRequest) (
	resp *fuse.ReleaseDirHandleResponse, err error) {
	resp = &fuse.ReleaseDirHandleResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Sanity check that this handle exists and is of the correct type.
	_ = fs.handles[req.Handle].(*dirHandle)

	// Clear the entry from the map.
	delete(fs.handles, req.Handle)

	return
}
<commit_msg>Fixed an annotation.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fs

import (
	"fmt"
	"reflect"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/fs/inode"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

type fileSystem struct {
	fuseutil.NotImplementedFileSystem

	/////////////////////////
	// Dependencies
	/////////////////////////

	clock  timeutil.Clock
	bucket gcs.Bucket

	/////////////////////////
	// Mutable state
	/////////////////////////

	// When acquiring this lock, the caller must hold no inode or dir handle
	// locks.
	mu syncutil.InvariantMutex

	// The user and group owning everything in the file system.
	//
	// GUARDED_BY(Mu)
	uid uint32
	gid uint32

	// The collection of live inodes, keyed by inode ID. No ID less than
	// fuse.RootInodeID is ever used.
	//
	// TODO(jacobsa): Implement ForgetInode support in the fuse package, then
	// implement the method here and clean up these maps.
	//
	// INVARIANT: All values are of type *inode.DirInode or *inode.FileInode
	// INVARIANT: For all keys k, k >= fuse.RootInodeID
	// INVARIANT: For all keys k, inodes[k].ID() == k
	// INVARIANT: inodes[fuse.RootInodeID] is of type *inode.DirInode
	//
	// GUARDED_BY(mu)
	inodes map[fuse.InodeID]inode.Inode

	// The next inode ID to hand out. We assume that this will never overflow,
	// since even if we were handing out inode IDs at 4 GHz, it would still take
	// over a century to do so.
	//
	// INVARIANT: For all keys k in inodes, k < nextInodeID
	//
	// GUARDED_BY(mu)
	nextInodeID fuse.InodeID

	// An index of all directory inodes by Name().
	//
	// INVARIANT: For each key k, isDirName(k)
	// INVARIANT: For each key k, dirIndex[k].Name() == k
	// INVARIANT: The values are all and only the values of the inodes map of
	// type *inode.DirInode.
	//
	// GUARDED_BY(mu)
	dirIndex map[string]*inode.DirInode

	// An index of all file inodes by (Name(), SourceGeneration()) pairs.
	//
	// INVARIANT: For each key k, !isDirName(k)
	// INVARIANT: For each key k, fileIndex[k].Name() == k.name
	// INVARIANT: For each key k, fileIndex[k].SourceGeneration() == k.gen
	// INVARIANT: The values are all and only the values of the inodes map of
	// type *inode.FileInode.
	//
	// GUARDED_BY(mu)
	fileIndex map[nameAndGen]*inode.FileInode

	// The collection of live handles, keyed by handle ID.
	//
	// INVARIANT: All values are of type *dirHandle
	//
	// GUARDED_BY(mu)
	handles map[fuse.HandleID]interface{}

	// The next handle ID to hand out. We assume that this will never overflow.
	//
	// INVARIANT: For all keys k in handles, k < nextHandleID
	//
	// GUARDED_BY(mu)
	nextHandleID fuse.HandleID
}

type nameAndGen struct {
	name string
	gen  int64
}

// Create a fuse file system whose root directory is the root of the supplied
// bucket. The supplied clock will be used for cache invalidation, modification
// times, etc.
func NewFileSystem(
	clock timeutil.Clock,
	bucket gcs.Bucket) (ffs fuse.FileSystem, err error) {
	// Set up the basic struct.
	fs := &fileSystem{
		clock:       clock,
		bucket:      bucket,
		inodes:      make(map[fuse.InodeID]inode.Inode),
		nextInodeID: fuse.RootInodeID + 1,
		dirIndex:    make(map[string]*inode.DirInode),
		fileIndex:   make(map[nameAndGen]*inode.FileInode),
		handles:     make(map[fuse.HandleID]interface{}),
	}

	// Set up the root inode.
	root := inode.NewDirInode(bucket, fuse.RootInodeID, "")
	fs.inodes[fuse.RootInodeID] = root
	fs.dirIndex[""] = root

	// Set up invariant checking.
	fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)

	ffs = fs
	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func isDirName(name string) bool {
	return name == "" || name[len(name)-1] == '/'
}

func (fs *fileSystem) checkInvariants() {
	// Check inode keys.
	for id, _ := range fs.inodes {
		if id < fuse.RootInodeID || id >= fs.nextInodeID {
			panic(fmt.Sprintf("Illegal inode ID: %v", id))
		}
	}

	// Check the root inode.
	_ = fs.inodes[fuse.RootInodeID].(*inode.DirInode)

	// Check each inode, and the indexes over them. Keep a count of each type
	// seen.
	dirsSeen := 0
	filesSeen := 0
	for id, in := range fs.inodes {
		// Check the ID.
		if in.ID() != id {
			panic(fmt.Sprintf("ID mismatch: %v vs. %v", in.ID(), id))
		}

		// Check type-specific stuff.
		switch typed := in.(type) {
		case *inode.DirInode:
			dirsSeen++

			if !isDirName(typed.Name()) {
				panic(fmt.Sprintf("Unexpected directory name: %s", typed.Name()))
			}

			if fs.dirIndex[typed.Name()] != typed {
				panic(fmt.Sprintf("dirIndex mismatch: %s", typed.Name()))
			}

		case *inode.FileInode:
			filesSeen++

			if isDirName(typed.Name()) {
				panic(fmt.Sprintf("Unexpected file name: %s", typed.Name()))
			}

			nandg := nameAndGen{typed.Name(), typed.SourceGeneration()}
			if fs.fileIndex[nandg] != typed {
				panic(
					fmt.Sprintf(
						"fileIndex mismatch: %s, %v",
						typed.Name(),
						typed.SourceGeneration()))
			}

		default:
			panic(fmt.Sprintf("Unexpected inode type: %v", reflect.TypeOf(in)))
		}
	}

	// Make sure that the indexes are exhaustive.
	if len(fs.dirIndex) != dirsSeen {
		panic(
			fmt.Sprintf(
				"dirIndex length mismatch: %v vs. %v",
				len(fs.dirIndex),
				dirsSeen))
	}

	if len(fs.fileIndex) != filesSeen {
		panic(
			fmt.Sprintf(
				"fileIndex length mismatch: %v vs. %v",
				len(fs.fileIndex),
				dirsSeen))
	}

	// Check handles.
	for id, h := range fs.handles {
		if id >= fs.nextHandleID {
			panic(fmt.Sprintf("Illegal handle ID: %v", id))
		}

		_ = h.(*dirHandle)
	}
}

// Find the given inode and return it with its lock held for reading. Panic if
// it doesn't exist or is the wrong type.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCK_FUNCTION(in.mu)
func (fs *fileSystem) getDirForReadingOrDie(
	id fuse.InodeID) (in *inode.DirInode) {
	in = fs.inodes[id].(*inode.DirInode)
	in.Mu.RLock()
	return
}

// Get attributes for the inode, fixing up ownership information.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCKS_REQUIRED(d.mu)
func (fs *fileSystem) getAttributes(
	ctx context.Context,
	d *inode.DirInode) (attrs fuse.InodeAttributes, err error) {
	attrs, err = d.Attributes(ctx)
	if err != nil {
		return
	}

	attrs.Uid = fs.uid
	attrs.Gid = fs.gid

	return
}

// Find a directory inode for the given object record. Create one if there
// isn't already one available.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
func (fs *fileSystem) lookUpOrCreateDirInode(
	ctx context.Context,
	o *storage.Object) (in *inode.DirInode, err error) {
	// Do we already have an inode for this name?
	if in = fs.dirIndex[o.Name]; in != nil {
		return
	}

	// Mint an ID.
	id := fs.nextInodeID
	fs.nextInodeID++

	// Create and index an inode.
	in = inode.NewDirInode(fs.bucket, id, o.Name)
	fs.inodes[id] = in
	fs.dirIndex[in.Name()] = in

	return
}

// Find a file inode for the given object record. Create one if there isn't
// already one available.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
func (fs *fileSystem) lookUpOrCreateFileInode(
	ctx context.Context,
	o *storage.Object) (in *inode.FileInode, err error) {
	nandg := nameAndGen{
		name: o.Name,
		gen:  o.Generation,
	}

	// Do we already have an inode for this (name, generation) pair?
	if in = fs.fileIndex[nandg]; in != nil {
		return
	}

	// Mint an ID.
	id := fs.nextInodeID
	fs.nextInodeID++

	// Create and index an inode.
	in = inode.NewFileInode(fs.bucket, id, o)
	fs.inodes[id] = in
	fs.fileIndex[nandg] = in

	return
}

////////////////////////////////////////////////////////////////////////
// fuse.FileSystem methods
////////////////////////////////////////////////////////////////////////

func (fs *fileSystem) Init(
	ctx context.Context,
	req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
	resp = &fuse.InitResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Store the mounting user's info for later.
	fs.uid = req.Header.Uid
	fs.gid = req.Header.Gid

	return
}

func (fs *fileSystem) LookUpInode(
	ctx context.Context,
	req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
	resp = &fuse.LookUpInodeResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Find the parent directory in question.
	parent := fs.inodes[req.Parent].(*inode.DirInode)

	// Find a record for the child with the given name.
	o, err := parent.LookUpChild(ctx, req.Name)
	if err != nil {
		return
	}

	// Is the child a directory or a file?
	var in inode.Inode
	if isDirName(o.Name) {
		in, err = fs.lookUpOrCreateDirInode(ctx, o)
	} else {
		in, err = fs.lookUpOrCreateFileInode(ctx, o)
	}

	if err != nil {
		return
	}

	// Fill out the response.
	resp.Entry.Child = in.ID()
	if resp.Entry.Attributes, err = fs.getAttributes(ctx, in); err != nil {
		return
	}

	return
}

func (fs *fileSystem) GetInodeAttributes(
	ctx context.Context,
	req *fuse.GetInodeAttributesRequest) (
	resp *fuse.GetInodeAttributesResponse, err error) {
	resp = &fuse.GetInodeAttributesResponse{}

	fs.mu.RLock()
	defer fs.mu.RUnlock()

	// Find the inode.
	in := fs.inodes[req.Inode]

	// Grab its attributes.
	switch typed := in.(type) {
	case *inode.DirInode:
		resp.Attributes, err = fs.getAttributes(ctx, typed)
		if err != nil {
			err = fmt.Errorf("DirInode.Attributes: %v", err)
			return
		}

	default:
		panic(
			fmt.Sprintf(
				"Unknown inode type for ID %v: %v",
				req.Inode,
				reflect.TypeOf(in)))
	}

	return
}

func (fs *fileSystem) OpenDir(
	ctx context.Context,
	req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
	resp = &fuse.OpenDirResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the inode still exists and is a directory. If not, something has
	// screwed up because the VFS layer shouldn't have let us forget the inode
	// before opening it.
	in := fs.getDirForReadingOrDie(req.Inode)
	defer in.Mu.RUnlock()

	// Allocate a handle.
	handleID := fs.nextHandleID
	fs.nextHandleID++

	fs.handles[handleID] = newDirHandle(in)
	resp.Handle = handleID

	return
}

func (fs *fileSystem) ReadDir(
	ctx context.Context,
	req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
	fs.mu.RLock()
	defer fs.mu.RUnlock()

	// Find the handle.
	dh := fs.handles[req.Handle].(*dirHandle)
	dh.Mu.Lock()
	defer dh.Mu.Unlock()

	// Serve the request.
	resp, err = dh.ReadDir(ctx, req)

	return
}

func (fs *fileSystem) ReleaseDirHandle(
	ctx context.Context,
	req *fuse.ReleaseDirHandleRequest) (
	resp *fuse.ReleaseDirHandleResponse, err error) {
	resp = &fuse.ReleaseDirHandleResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Sanity check that this handle exists and is of the correct type.
	_ = fs.handles[req.Handle].(*dirHandle)

	// Clear the entry from the map.
	delete(fs.handles, req.Handle)

	return
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package kusttest_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"sigs.k8s.io/kustomize/api/filesys"
	"sigs.k8s.io/kustomize/api/ifc"
	pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
	"sigs.k8s.io/kustomize/api/konfig"
	fLdr "sigs.k8s.io/kustomize/api/loader"
	"sigs.k8s.io/kustomize/api/provider"
	"sigs.k8s.io/kustomize/api/resmap"
	valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
	"sigs.k8s.io/kustomize/api/types"
)

// HarnessEnhanced manages a full plugin environment for tests.
type HarnessEnhanced struct {
	// An instance of *testing.T, and a filesystem (likely in-memory)
	// for loading test data - plugin config, resources to transform, etc.
	Harness

	// plugintestEnv holds the plugin compiler and data needed to
	// create compilation sub-processes.
	pte *pluginTestEnv

	// rf creates Resources from byte streams.
	rf *resmap.Factory

	// A file loader using the Harness.fSys to read test data.
	ldr ifc.Loader

	// If true, wipe the ifc.loader root (not the plugin loader root)
	// as part of cleanup.
	shouldWipeLdrRoot bool

	// A plugin loader that loads plugins from a (real) file system.
	pl *pLdr.Loader
}

func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
	r := makeBaseEnhancedHarness(t)
	r.Harness = MakeHarnessWithFs(t, filesys.MakeFsInMemory())
	// Point the Harness's file loader to the root ('/')
	// of the in-memory file system.
	r.ResetLoaderRoot(filesys.Separator)
	return r
}

func MakeEnhancedHarnessWithTmpRoot(t *testing.T) *HarnessEnhanced {
	r := makeBaseEnhancedHarness(t)
	fSys := filesys.MakeFsOnDisk()
	r.Harness = MakeHarnessWithFs(t, fSys)
	tmpDir, err := ioutil.TempDir("", "kust-testing-")
	if err != nil {
		panic("test harness cannot make tmp dir: " + err.Error())
	}
	r.ldr, err = fLdr.NewLoader(fLdr.RestrictionRootOnly, tmpDir, fSys)
	if err != nil {
		panic("test harness cannot make ldr at tmp dir: " + err.Error())
	}
	r.shouldWipeLdrRoot = true
	return r
}

func makeBaseEnhancedHarness(t *testing.T) *HarnessEnhanced {
	rf := resmap.NewFactory(
		provider.NewDefaultDepProvider().GetResourceFactory())
	return &HarnessEnhanced{
		pte: newPluginTestEnv(t).set(),
		rf:  rf,
		pl: pLdr.NewLoader(
			types.EnabledPluginConfig(types.BploLoadFromFileSys),
			rf,
			// Plugin configs are always located on disk,
			// regardless of the test harness's FS
			filesys.MakeFsOnDisk())}
}

func (th *HarnessEnhanced) ErrIfNoHelm() error {
	_, err := exec.LookPath(th.GetPluginConfig().HelmConfig.Command)
	return err
}

func (th *HarnessEnhanced) GetRoot() string {
	return th.ldr.Root()
}

func (th *HarnessEnhanced) MkDir(path string) string {
	dir := filepath.Join(th.ldr.Root(), path)
	th.GetFSys().Mkdir(dir)
	return dir
}

func (th *HarnessEnhanced) Reset() {
	if th.shouldWipeLdrRoot {
		if !strings.HasPrefix(th.ldr.Root(), os.TempDir()) {
			// sanity check.
			panic("something strange about th.ldr.Root() = " + th.ldr.Root())
		}
		os.RemoveAll(th.ldr.Root())
	}
	th.pte.reset()
}

func (th *HarnessEnhanced) GetPluginConfig() *types.PluginConfig {
	return th.pl.Config()
}

func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
	return th.BuildGoPlugin(konfig.BuiltinPluginPackage, "", k)
}

func (th *HarnessEnhanced) BuildGoPlugin(g, v, k string) *HarnessEnhanced {
	th.pte.prepareGoPlugin(g, v, k)
	return th
}

func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced {
	th.pte.prepareExecPlugin(g, v, k)
	return th
}

// ResetLoaderRoot interprets its argument as an absolute directory path.
// It creates the directory, and creates the harness's file loader
// rooted in that directory.
func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
	if err := th.fSys.Mkdir(root); err != nil {
		th.t.Fatal(err)
	}
	ldr, err := fLdr.NewLoader(
		fLdr.RestrictionRootOnly, root, th.fSys)
	if err != nil {
		th.t.Fatalf("Unable to make loader: %v", err)
	}
	th.ldr = ldr
}

func (th *HarnessEnhanced) LoadAndRunGenerator(
	config string) resmap.ResMap {
	res, err := th.rf.RF().FromBytes([]byte(config))
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	g, err := th.pl.LoadGenerator(
		th.ldr, valtest_test.MakeFakeValidator(), res)
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	rm, err := g.Generate()
	if err != nil {
		th.t.Fatalf("generate err: %v", err)
	}
	rm.RemoveBuildAnnotations()
	return rm
}

func (th *HarnessEnhanced) LoadAndRunTransformer(
	config, input string) resmap.ResMap {
	resMap, err := th.RunTransformer(config, input)
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	return resMap
}

func (th *HarnessEnhanced) RunTransformerAndCheckResult(
	config, input, expected string) {
	resMap := th.LoadAndRunTransformer(config, input)
	th.AssertActualEqualsExpectedNoIdAnnotations(resMap, expected)
}

func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(
	config, input string) error {
	_, err := th.RunTransformer(config, input)
	return err
}

type AssertFunc func(t *testing.T, err error)

func (th *HarnessEnhanced) RunTransformerAndCheckError(
	config, input string, assertFn AssertFunc) {
	_, err := th.RunTransformer(config, input)
	assertFn(th.t, err)
}

func (th *HarnessEnhanced) RunTransformer(
	config, input string) (resmap.ResMap, error) {
	resMap, err := th.rf.NewResMapFromBytes([]byte(input))
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	return th.RunTransformerFromResMap(config, resMap)
}

func (th *HarnessEnhanced) RunTransformerFromResMap(
	config string, resMap resmap.ResMap) (resmap.ResMap, error) {
	transConfig, err := th.rf.RF().FromBytes([]byte(config))
	if err != nil {
		th.t.Logf("config: '%s'", config)
		th.t.Fatalf("Err: %v", err)
	}
	g, err := th.pl.LoadTransformer(
		th.ldr, valtest_test.MakeFakeValidator(), transConfig)
	if err != nil {
		return nil, err
	}
	err = g.Transform(resMap)
	return resMap, err
}
<commit_msg>Fix api tests on MacOS<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package kusttest_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"sigs.k8s.io/kustomize/api/filesys"
	"sigs.k8s.io/kustomize/api/ifc"
	pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
	"sigs.k8s.io/kustomize/api/konfig"
	fLdr "sigs.k8s.io/kustomize/api/loader"
	"sigs.k8s.io/kustomize/api/provider"
	"sigs.k8s.io/kustomize/api/resmap"
	valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
	"sigs.k8s.io/kustomize/api/types"
)

// HarnessEnhanced manages a full plugin environment for tests.
type HarnessEnhanced struct {
	// An instance of *testing.T, and a filesystem (likely in-memory)
	// for loading test data - plugin config, resources to transform, etc.
	Harness

	// plugintestEnv holds the plugin compiler and data needed to
	// create compilation sub-processes.
	pte *pluginTestEnv

	// rf creates Resources from byte streams.
	rf *resmap.Factory

	// A file loader using the Harness.fSys to read test data.
	ldr ifc.Loader

	// If true, wipe the ifc.loader root (not the plugin loader root)
	// as part of cleanup.
	shouldWipeLdrRoot bool

	// A plugin loader that loads plugins from a (real) file system.
	pl *pLdr.Loader
}

func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
	r := makeBaseEnhancedHarness(t)
	r.Harness = MakeHarnessWithFs(t, filesys.MakeFsInMemory())
	// Point the Harness's file loader to the root ('/')
	// of the in-memory file system.
	r.ResetLoaderRoot(filesys.Separator)
	return r
}

func MakeEnhancedHarnessWithTmpRoot(t *testing.T) *HarnessEnhanced {
	r := makeBaseEnhancedHarness(t)
	fSys := filesys.MakeFsOnDisk()
	r.Harness = MakeHarnessWithFs(t, fSys)
	tmpDir, err := ioutil.TempDir("", "kust-testing-")
	if err != nil {
		panic("test harness cannot make tmp dir: " + err.Error())
	}
	r.ldr, err = fLdr.NewLoader(fLdr.RestrictionRootOnly, tmpDir, fSys)
	if err != nil {
		panic("test harness cannot make ldr at tmp dir: " + err.Error())
	}
	r.shouldWipeLdrRoot = true
	return r
}

func makeBaseEnhancedHarness(t *testing.T) *HarnessEnhanced {
	rf := resmap.NewFactory(
		provider.NewDefaultDepProvider().GetResourceFactory())
	return &HarnessEnhanced{
		pte: newPluginTestEnv(t).set(),
		rf:  rf,
		pl: pLdr.NewLoader(
			types.EnabledPluginConfig(types.BploLoadFromFileSys),
			rf,
			// Plugin configs are always located on disk,
			// regardless of the test harness's FS
			filesys.MakeFsOnDisk())}
}

func (th *HarnessEnhanced) ErrIfNoHelm() error {
	_, err := exec.LookPath(th.GetPluginConfig().HelmConfig.Command)
	return err
}

func (th *HarnessEnhanced) GetRoot() string {
	return th.ldr.Root()
}

func (th *HarnessEnhanced) MkDir(path string) string {
	dir := filepath.Join(th.ldr.Root(), path)
	th.GetFSys().Mkdir(dir)
	return dir
}

func (th *HarnessEnhanced) Reset() {
	if th.shouldWipeLdrRoot {
		root, _ := filepath.EvalSymlinks(th.ldr.Root())
		tmpdir, _ := filepath.EvalSymlinks(os.TempDir())

		if !strings.HasPrefix(root, tmpdir) {
			// sanity check.
			panic("something strange about th.ldr.Root() = " + th.ldr.Root())
		}
		os.RemoveAll(th.ldr.Root())
	}
	th.pte.reset()
}

func (th *HarnessEnhanced) GetPluginConfig() *types.PluginConfig {
	return th.pl.Config()
}

func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
	return th.BuildGoPlugin(konfig.BuiltinPluginPackage, "", k)
}

func (th *HarnessEnhanced) BuildGoPlugin(g, v, k string) *HarnessEnhanced {
	th.pte.prepareGoPlugin(g, v, k)
	return th
}

func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced {
	th.pte.prepareExecPlugin(g, v, k)
	return th
}

// ResetLoaderRoot interprets its argument as an absolute directory path.
// It creates the directory, and creates the harness's file loader
// rooted in that directory.
func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
	if err := th.fSys.Mkdir(root); err != nil {
		th.t.Fatal(err)
	}
	ldr, err := fLdr.NewLoader(
		fLdr.RestrictionRootOnly, root, th.fSys)
	if err != nil {
		th.t.Fatalf("Unable to make loader: %v", err)
	}
	th.ldr = ldr
}

func (th *HarnessEnhanced) LoadAndRunGenerator(
	config string) resmap.ResMap {
	res, err := th.rf.RF().FromBytes([]byte(config))
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	g, err := th.pl.LoadGenerator(
		th.ldr, valtest_test.MakeFakeValidator(), res)
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	rm, err := g.Generate()
	if err != nil {
		th.t.Fatalf("generate err: %v", err)
	}
	rm.RemoveBuildAnnotations()
	return rm
}

func (th *HarnessEnhanced) LoadAndRunTransformer(
	config, input string) resmap.ResMap {
	resMap, err := th.RunTransformer(config, input)
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	return resMap
}

func (th *HarnessEnhanced) RunTransformerAndCheckResult(
	config, input, expected string) {
	resMap := th.LoadAndRunTransformer(config, input)
	th.AssertActualEqualsExpectedNoIdAnnotations(resMap, expected)
}

func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(
	config, input string) error {
	_, err := th.RunTransformer(config, input)
	return err
}

type AssertFunc func(t *testing.T, err error)

func (th *HarnessEnhanced) RunTransformerAndCheckError(
	config, input string, assertFn AssertFunc) {
	_, err := th.RunTransformer(config, input)
	assertFn(th.t, err)
}

func (th *HarnessEnhanced) RunTransformer(
	config, input string) (resmap.ResMap, error) {
	resMap, err := th.rf.NewResMapFromBytes([]byte(input))
	if err != nil {
		th.t.Fatalf("Err: %v", err)
	}
	return th.RunTransformerFromResMap(config, resMap)
}

func (th *HarnessEnhanced) RunTransformerFromResMap(
	config string, resMap resmap.ResMap) (resmap.ResMap, error) {
	transConfig, err := th.rf.RF().FromBytes([]byte(config))
	if err != nil {
		th.t.Logf("config: '%s'", config)
		th.t.Fatalf("Err: %v", err)
	}
	g, err := th.pl.LoadTransformer(
		th.ldr, valtest_test.MakeFakeValidator(), transConfig)
	if err != nil {
		return nil, err
	}
	err = g.Transform(resMap)
	return resMap, err
}
<|endoftext|>


<commit_before>package handler

import (
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/drone/drone/server/database"
	"github.com/drone/drone/server/pubsub"
	"github.com/drone/drone/server/session"
	"github.com/drone/drone/shared/model"
	"github.com/gorilla/pat"

	"github.com/gorilla/websocket"
)

const (
	// Time allowed to write the message to the client.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the client.
	pongWait = 60 * time.Second

	// Send pings to client with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

type WsHandler struct {
	pubsub  *pubsub.PubSub
	commits database.CommitManager
	perms   database.PermManager
	repos   database.RepoManager
	sess    session.Session
}

func NewWsHandler(repos database.RepoManager, commits database.CommitManager, perms database.PermManager, sess session.Session, pubsub *pubsub.PubSub) *WsHandler {
	return &WsHandler{pubsub, commits, perms, repos, sess}
}

// WsUser will upgrade the connection to a Websocket and will stream
// all events to the browser pertinent to the authenticated user. If the user
// is not authenticated, only public events are streamed.
func (h *WsHandler) WsUser(w http.ResponseWriter, r *http.Request) error {
	// get the user form the session
	user := h.sess.UserCookie(r)

	// upgrade the websocket
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return badRequest{err}
	}

	// register a channel for global events
	channel := h.pubsub.Register("_global")
	sub := channel.Subscribe()

	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		sub.Close()
		ws.Close()
	}()

	go func() {
		for {
			select {
			case msg := <-sub.Read():
				work, ok := msg.(*model.Request)
				if !ok {
					break
				}

				// user must have read access to the repository
				// in order to pass this message along
				if ok, _ := h.perms.Member(user, work.Repo); !ok {
					break
				}

				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteJSON(work)
				if err != nil {
					ws.Close()
					return
				}
			case <-sub.CloseNotify():
				ws.Close()
				return
			case <-ticker.C:
				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteMessage(websocket.PingMessage, []byte{})
				if err != nil {
					ws.Close()
					return
				}
			}
		}
	}()

	readWebsocket(ws)
	return nil

}

// WsConsole will upgrade the connection to a Websocket and will stream
// the build output to the browser.
func (h *WsHandler) WsConsole(w http.ResponseWriter, r *http.Request) error {
	var commitID, _ = strconv.Atoi(r.FormValue(":id"))

	commit, err := h.commits.Find(int64(commitID))
	if err != nil {
		return notFound{err}
	}
	repo, err := h.repos.Find(commit.RepoID)
	if err != nil {
		return notFound{err}
	}
	user := h.sess.UserCookie(r)
	if ok, _ := h.perms.Read(user, repo); !ok {
		return notFound{err}
	}

	// find a channel that we can subscribe to
	// and listen for stream updates.
	channel := h.pubsub.Lookup(commit.ID)
	if channel == nil {
		return notFound{}
	}
	sub := channel.Subscribe()
	defer sub.Close()

	// upgrade the websocket
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return badRequest{err}
	}

	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		ws.Close()
	}()

	go func() {
		for {
			select {
			case msg := <-sub.Read():
				data, ok := msg.([]byte)
				if !ok {
					break
				}
				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteMessage(websocket.TextMessage, data)
				if err != nil {
					log.Printf("websocket for commit %d closed. Err: %s\n", commitID, err)
					ws.Close()
					return
				}
			case <-sub.CloseNotify():
				log.Printf("websocket for commit %d closed by client\n", commitID)
				ws.Close()
				return
			case <-ticker.C:
				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteMessage(websocket.PingMessage, []byte{})
				if err != nil {
					log.Printf("websocket for commit %d closed. Err: %s\n", commitID, err)
					ws.Close()
					return
				}
			}
		}
	}()

	readWebsocket(ws)
	return nil
}

// readWebsocket will block while reading the websocket data
func readWebsocket(ws *websocket.Conn) {
	defer ws.Close()
	ws.SetReadLimit(512)
	ws.SetReadDeadline(time.Now().Add(pongWait))
	ws.SetPongHandler(func(string) error {
		ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})
	for {
		_, _, err := ws.ReadMessage()
		if err != nil {
			break
		}
	}
}

// Ping is a method that is being used for internal testing and
// will be removed prior to release
func (h *WsHandler) Ping(w http.ResponseWriter, r *http.Request) error {
	channel := h.pubsub.Register("_global")
	msg := model.Request{
		Repo:   &model.Repo{ID: 1, Private: false, Host: "github.com", Owner: "drone", Name: "drone"},
		Commit: &model.Commit{ID: 1, Status: "Started", Branch: "master", Sha: "113f4917ff9174945388d86395f902cd154074cb", Message: "Remove branches by SCM hook", Author: "bradrydzewski", Gravatar: "8c58a0be77ee441bb8f8595b7f1b4e87"},
	}
	channel.Publish(&msg)
	w.WriteHeader(http.StatusOK)
	return nil
}

func (h *WsHandler) Register(r *pat.Router) {
	r.Post("/ws/ping", errorHandler(h.Ping))
	r.Get("/ws/user", errorHandler(h.WsUser))
	r.Get("/ws/stdout/{id}", errorHandler(h.WsConsole))
}
<commit_msg>updated websockets to send global messages to any use w/ read access<commit_after>package handler

import (
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/drone/drone/server/database"
	"github.com/drone/drone/server/pubsub"
	"github.com/drone/drone/server/session"
	"github.com/drone/drone/shared/model"
	"github.com/gorilla/pat"

	"github.com/gorilla/websocket"
)

const (
	// Time allowed to write the message to the client.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the client.
	pongWait = 60 * time.Second

	// Send pings to client with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

type WsHandler struct {
	pubsub  *pubsub.PubSub
	commits database.CommitManager
	perms   database.PermManager
	repos   database.RepoManager
	sess    session.Session
}

func NewWsHandler(repos database.RepoManager, commits database.CommitManager, perms database.PermManager, sess session.Session, pubsub *pubsub.PubSub) *WsHandler {
	return &WsHandler{pubsub, commits, perms, repos, sess}
}

// WsUser will upgrade the connection to a Websocket and will stream
// all events to the browser pertinent to the authenticated user. If the user
// is not authenticated, only public events are streamed.
func (h *WsHandler) WsUser(w http.ResponseWriter, r *http.Request) error {
	// get the user form the session
	user := h.sess.UserCookie(r)

	// upgrade the websocket
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return badRequest{err}
	}

	// register a channel for global events
	channel := h.pubsub.Register("_global")
	sub := channel.Subscribe()

	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		sub.Close()
		ws.Close()
	}()

	go func() {
		for {
			select {
			case msg := <-sub.Read():
				work, ok := msg.(*model.Request)
				if !ok {
					break
				}

				// user must have read access to the repository
				// in order to pass this message along
				if role := h.perms.Find(user, work.Repo); role.Read {
					break
				}

				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteJSON(work)
				if err != nil {
					ws.Close()
					return
				}
			case <-sub.CloseNotify():
				ws.Close()
				return
			case <-ticker.C:
				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteMessage(websocket.PingMessage, []byte{})
				if err != nil {
					ws.Close()
					return
				}
			}
		}
	}()

	readWebsocket(ws)
	return nil

}

// WsConsole will upgrade the connection to a Websocket and will stream
// the build output to the browser.
func (h *WsHandler) WsConsole(w http.ResponseWriter, r *http.Request) error {
	var commitID, _ = strconv.Atoi(r.FormValue(":id"))

	commit, err := h.commits.Find(int64(commitID))
	if err != nil {
		return notFound{err}
	}
	repo, err := h.repos.Find(commit.RepoID)
	if err != nil {
		return notFound{err}
	}
	user := h.sess.UserCookie(r)
	if ok, _ := h.perms.Read(user, repo); !ok {
		return notFound{err}
	}

	// find a channel that we can subscribe to
	// and listen for stream updates.
	channel := h.pubsub.Lookup(commit.ID)
	if channel == nil {
		return notFound{}
	}
	sub := channel.Subscribe()
	defer sub.Close()

	// upgrade the websocket
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return badRequest{err}
	}

	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		ws.Close()
	}()

	go func() {
		for {
			select {
			case msg := <-sub.Read():
				data, ok := msg.([]byte)
				if !ok {
					break
				}
				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteMessage(websocket.TextMessage, data)
				if err != nil {
					log.Printf("websocket for commit %d closed. Err: %s\n", commitID, err)
					ws.Close()
					return
				}
			case <-sub.CloseNotify():
				log.Printf("websocket for commit %d closed by client\n", commitID)
				ws.Close()
				return
			case <-ticker.C:
				ws.SetWriteDeadline(time.Now().Add(writeWait))
				err := ws.WriteMessage(websocket.PingMessage, []byte{})
				if err != nil {
					log.Printf("websocket for commit %d closed. Err: %s\n", commitID, err)
					ws.Close()
					return
				}
			}
		}
	}()

	readWebsocket(ws)
	return nil
}

// readWebsocket will block while reading the websocket data
func readWebsocket(ws *websocket.Conn) {
	defer ws.Close()
	ws.SetReadLimit(512)
	ws.SetReadDeadline(time.Now().Add(pongWait))
	ws.SetPongHandler(func(string) error {
		ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})
	for {
		_, _, err := ws.ReadMessage()
		if err != nil {
			break
		}
	}
}

// Ping is a method that is being used for internal testing and
// will be removed prior to release
func (h *WsHandler) Ping(w http.ResponseWriter, r *http.Request) error {
	channel := h.pubsub.Register("_global")
	msg := model.Request{
		Repo:   &model.Repo{ID: 1, Private: false, Host: "github.com", Owner: "drone", Name: "drone"},
		Commit: &model.Commit{ID: 1, Status: "Started", Branch: "master", Sha: "113f4917ff9174945388d86395f902cd154074cb", Message: "Remove branches by SCM hook", Author: "bradrydzewski", Gravatar: "8c58a0be77ee441bb8f8595b7f1b4e87"},
	}
	channel.Publish(&msg)
	w.WriteHeader(http.StatusOK)
	return nil
}

func (h *WsHandler) Register(r *pat.Router) {
	r.Post("/ws/ping", errorHandler(h.Ping))
	r.Get("/ws/user", errorHandler(h.WsUser))
	r.Get("/ws/stdout/{id}", errorHandler(h.WsConsole))
}
<|endoftext|>


<commit_before>package ui

import (
	"sync"

	"github.com/nsf/termbox-go"
)

// Labels is a map of each allowed key press (rune) to it's full string label.
var Labels = map[rune]string{
	'a': "Visual imagination",
	'd': "Language thought",
	'e': "Language voice",
	'q': "Visual memory",
	's': "Auditory imagination",
	'w': "Auditory memory",
}

const title = `           _ _
  _ __  __| | |_
 | '  \/ _  |  _|
 |_|_|_\__,_|\__|

`

var (
	// Version holds the program version.
	Version   = "devel"
	mu        sync.Mutex
	inputs    []*Input
	keys      []*KeyLabel
	statusBar *StatusBar
	config    Config
)

const (
	inputLabelWidth      = 10
	inputWidth           = 10
	inputMinutesBufWidth = 3
	inputHzBufWidth      = 5
	keyLabelWidth        = 3
	keyWidth             = 21
	statusBarWidth       = 60
	statusBarDefaultText = "Press 'space' to start capturing keys, 'Esc' to quit."
)

// Init must be called before any other function. It initializes
// configuration and termbox.
func Init() error {
	if err := initConfig(); err != nil {
		return err
	}
	if err := initTermbox(); err != nil {
		return err
	}
	return nil
}

// Loading configuration from config.json
func initConfig() error {
	config = Config{}
	return config.Load()
}

// Initializing termbox
func initTermbox() error {
	err := termbox.Init()
	if err != nil {
		return err
	}
	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
	termbox.SetOutputMode(termbox.OutputNormal)
	termbox.Clear(termbox.ColorWhite, termbox.ColorDefault)
	return nil
}

// Close should be deferred after initialization. It finalizes termbox library.
func Close() {
	mu.Lock()
	termbox.Close()
	mu.Unlock()
}

// DrawAll draws the title, the inputs, the key labels and the status bar.
// It should also be called when a resize event is received.
func DrawAll() {
	inputs = nil
	keys = nil
	statusBar = nil
	_, keysY := drawTitle(0, 0, Version)
	keysX, sbY := drawInputs(0, keysY+1)
	_, _ = drawKeys(keysX+3, keysY+1)
	_, _ = drawStatusBar(0, sbY+1)
}

func drawTitle(x, y int, version string) (maxX, maxY int) {
	titleX, titleY := text(x, y, title)
	if version != "devel" {
		version = "v" + version
	}
	// draw version
	text(titleX+1, titleY-2, version)
	return titleX, titleY
}

func drawStatusBar(x, y int) (maxX, maxY int) {
	sbw := statusBarWidth
	statusBar = NewStatusBar(x, y, sbw, statusBarDefaultText)
	statusBar.Draw()
	return statusBar.MaxX(), statusBar.MaxY()
}

func drawInputs(x, y int) (maxX, maxY int) {
	const lw = inputLabelWidth
	const w = inputWidth
	in1 := NewInput(x, y+0, lw, "Mode", w, 0, config.ModeS(), false, InputSwitch, configMode)
	in2 := NewInput(x, y+2, lw, "TotalTime", w, inputMinutesBufWidth, config.TotalTimeS(), true, InputNumericInt, configTotalTime)
	in3 := NewInput(x, y+4, lw, "Offset", w, inputMinutesBufWidth, config.OffsetS(), true, InputNumericInt, configOffset)
	in4 := NewInput(x, y+6, lw, "BaseHz", w, inputHzBufWidth, config.BaseHzS(), true, InputNumericFloat, configBaseHz)
	in5 := NewInput(x, y+8, lw, "StartHz", w, inputHzBufWidth, config.StartHzS(), true, InputNumericFloat, configStartHz)
	in6 := NewInput(x, y+10, lw, "EndHz", w, inputHzBufWidth, config.EndHzS(), true, InputNumericFloat, configEndHz)
	inputs = nil
	inputs = append(inputs, in1, in2, in3, in4, in5, in6)
	for _, in := range inputs {
		in.Draw()
	}
	return in6.MaxX(), in6.MaxY()
}

// ReloadInputs updates each input with the values of a new configuration.
// It should be called after receiving a valid value from en enabled input.
func ReloadInputs(c Config) {
	inputs[0].T = c.ModeS()
	inputs[1].T = c.TotalTimeS()
	inputs[2].T = c.OffsetS()
	inputs[3].T = c.BaseHzS()
	inputs[4].T = c.StartHzS()
	inputs[5].T = c.EndHzS()
	for _, in := range inputs {
		in.ClearBuf()
		in.ResetText()
	}
}

func drawKeys(x, y int) (maxX, maxY int) {
	const lw = keyLabelWidth
	const w = keyWidth
	k1 := NewKeyLabel(x, y+0, lw, rtoa('q'), w, Labels['q'], false)
	k2 := NewKeyLabel(x, y+2, lw, rtoa('a'), w, Labels['a'], true)
	k3 := NewKeyLabel(x, y+4, lw, rtoa('w'), w, Labels['w'], true)
	k4 := NewKeyLabel(x, y+6, lw, rtoa('s'), w, Labels['s'], true)
	k5 := NewKeyLabel(x, y+8, lw, rtoa('e'), w, Labels['e'], true)
	k6 := NewKeyLabel(x, y+10, lw, rtoa('d'), w, Labels['d'], true)
	keys = append(keys, k1, k2, k3, k4, k5, k6)
	for _, k := range keys {
		k.Draw()
	}
	return k6.MaxX(), k6.MaxY()
}

// Cell wraps a termbox cell. A single conceptual entity on the screen. A
// screen considered a 2d array of cells. Each cell also holds a reference
// to an input if it exists, thus by clicking a cell that contains an input
// we can easily access it.
type Cell struct {
	Input *Input
	termbox.Cell
}

func cells() [][]Cell {
	mx, my := termbox.Size()
	cellBuffer := termbox.CellBuffer()
	cells := make([][]Cell, mx)
	for k := range cells {
		cells[k] = make([]Cell, my)
	}
	i, j := 0, 0
	for _, c := range cellBuffer {
		if i == mx {
			j++
			i = 0
		}
		cells[i][j].Ch = c.Ch
		cells[i][j].Fg = c.Fg
		cells[i][j].Bg = c.Bg
		i++
	}
	return cells
}

func registerInputs(cells [][]Cell) [][]Cell {
	for _, in := range inputs {
		for x := in.TextStartX(); x <= in.TextEndX(); x++ {
			cells[x][in.TextY()-1].Input = in
			cells[x][in.TextY()+0].Input = in
			cells[x][in.TextY()+1].Input = in
		}
	}
	return cells
}

// Cells initializes all cells on screen and registers the inputs on the cells
// that should have them.
func Cells() [][]Cell {
	c := cells()
	return registerInputs(c)
}

// GetCell returns a cell based on the x, y points of the screen.
func GetCell(x, y int) Cell {
	c := Cells()
	return c[x][y]
}

// Scan scans the cells of the screen that are contained at a certain y,
// between startX and endX. It returns the content of the scanned cells as
// a string.
func Scan(startX, endX, y int) string {
	c := Cells()
	var runes []rune
	for x := startX; x <= endX; x++ {
		runes = append(runes, c[x][y].Ch)
	}
	return string(runes)
}

// KeyLabel holds a label with each allowed key press and right next, the
// coresponding text that describes the label.
type KeyLabel struct {
	X      int
	Y      int
	LabelW int    // label width
	LabelT string // label text
	W      int    // rest of width
	T      string // text describing the key
	a      bool
	S      bool
}

// NewKeyLabel creates a new KeyLabel.
func NewKeyLabel(x, y, labelW int, labelT string, w int, t string, a bool) *KeyLabel {
	return &KeyLabel{x, y, labelW, labelT, w, t, a, false}
}

// MaxX returns the maximum x that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxX() int {
	return kl.X + kl.LabelW + 3 + kl.W
}

// MaxY returns the maximum y that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxY() int {
	return kl.Y + 2
}

// Draw draws the KeyLabel.
func (kl KeyLabel) Draw() {
	x := kl.X
	y := kl.Y
	lw := kl.LabelW
	lt := kl.LabelT
	w := kl.W
	t := kl.T

	fill(x, y+0, 1, 1, '┌')
	fill(x, y+1, 1, 1, '│')
	fill(x, y+2, 1, 1, '└')
	fill(x+1, y+0, lw, 1, '─')

	text(x+1, y+1, lt)
	fill(x+1, y+2, lw, 1, '─')
	fill(x+lw+1, y+0, 1, 1, '─')
	fill(x+lw+1, y+1, 1, 1, ' ')
	fill(x+lw+1, y+2, 1, 1, '─')
	fill(x+lw+2, y+0, w, 1, '─')
	text(x+lw+2, y+1, t)
	fill(x+lw+2, y+2, w, 1, '─')
	fill(x+lw+2+w, y+0, 1, 1, '┐')
	fill(x+lw+2+w, y+1, 1, 1, '│')
	fill(x+lw+2+w, y+2, 1, 1, '┘')
	if kl.a {
		fill(x, y+0, 1, 1, '├')
		fill(x+lw+2+w, y+0, 1, 1, '┤')
	}
}

// StatusBar holds the data for drawing a status bar with a specified width
// and text. It also has space to the left for the timer.
type StatusBar struct {
	X          int
	Y          int
	Width      int
	Text       string
	timerWidth int
}

// NewStatusBar returns a new StatusBar.
func NewStatusBar(x, y, width int, text string) *StatusBar {
	return &StatusBar{x, y, width, text, 6}
}

// MaxX returns the maximum x that a StatusBar can reach on the screen.
func (sb StatusBar) MaxX() int {
	return sb.X + sb.timerWidth + 2 + sb.Width
}

// MaxY returns the maximum y that a StatusBar can reach on the screen.
func (sb StatusBar) MaxY() int {
	return sb.Y + 2
}

// Draw draws the StatusBar.
func (sb StatusBar) Draw() {
	x := sb.X
	y := sb.Y
	w := sb.Width
	t := sb.Text
	tw := sb.timerWidth

	// unicode box drawing chars around the edit box
	fill(x, y+0, 1, 1, '╔')
	fill(x, y+1, 1, 1, '║')
	fill(x, y+2, 1, 1, '╚')
	fill(x+1, y+0, tw, 1, '═')
	fill(x+1, y+2, tw, 1, '═')
	fill(x+tw+1, y+0, 1, 1, '╤')
	fill(x+tw+1, y+1, 1, 1, '│')
	fill(x+tw+1, y+2, 1, 1, '╧')
	fill(x+tw+2, y+0, w, 1, '═')
	fill(x+tw+2, y+2, w, 1, '═')
	fill(x+tw+2+w, y+0, 1, 1, '╗')
	fill(x+tw+2+w, y+1, 1, 1, '║')
	fill(x+tw+2+w, y+2, 1, 1, '╝')
	text(x+tw+2, y+1, t)

	flush()
}

// UpdateTimer updates the timer of the status bar by a specified amount of
// seconds.
func (sb StatusBar) UpdateTimer(seconds int) {
	text(sb.X+1, sb.Y+1, FormatTimer(seconds))
	flush()
}

// UpdateText updates the text of the status bar.
func (sb StatusBar) UpdateText(t string) {
	//Text(sb.X+sb.timerWidth+2, sb.Y+1, text[0:sb.Width]) // text[0:sb.Width] panics for some reason
	fill(sb.X+sb.timerWidth+2, sb.Y+1, sb.Width, 1, ' ')
	text(sb.X+sb.timerWidth+2, sb.Y+1, t)
	flush()
}

// UpdateTimer is a helper function that updates the status bar's timer.
func UpdateTimer(seconds int) {
	if statusBar != nil {
		statusBar.UpdateTimer(seconds)
	}
}

// ResetTimer clears the status bar's timer.
func ResetTimer() {
	if statusBar != nil {
		fill(statusBar.X+1, statusBar.Y+1, statusBar.timerWidth, 1, ' ')
		flush()
	}
}

// UpdateText is a helper function that updates the status bar's text.
func UpdateText(text string) {
	if statusBar != nil {
		statusBar.UpdateText(text)
	}
}

// ResetText clears the status bar's text.
func ResetText() {
	if statusBar != nil {
		statusBar.UpdateText(statusBarDefaultText)
	}
}

// Capture represents a captured key press at a specific second of the timer
// along with the value of Hz that was recorded.
type Capture struct {
	Value   rune
	Seconds int
	Hz      float64
}

// Label returns the description of the captured key value.
func (c *Capture) Label() string {
	return Labels[c.Value]
}

// Timestamp returns the timestamp of when a capture happened.
func (c *Capture) Timestamp() string {
	return FormatTimer(c.Seconds)
}
<commit_msg>Check error<commit_after>package ui

import (
	"sync"

	"github.com/nsf/termbox-go"
)

// Labels is a map of each allowed key press (rune) to it's full string label.
var Labels = map[rune]string{
	'a': "Visual imagination",
	'd': "Language thought",
	'e': "Language voice",
	'q': "Visual memory",
	's': "Auditory imagination",
	'w': "Auditory memory",
}

const title = `           _ _
  _ __  __| | |_
 | '  \/ _  |  _|
 |_|_|_\__,_|\__|

`

var (
	// Version holds the program version.
	Version   = "devel"
	mu        sync.Mutex
	inputs    []*Input
	keys      []*KeyLabel
	statusBar *StatusBar
	config    Config
)

const (
	inputLabelWidth      = 10
	inputWidth           = 10
	inputMinutesBufWidth = 3
	inputHzBufWidth      = 5
	keyLabelWidth        = 3
	keyWidth             = 21
	statusBarWidth       = 60
	statusBarDefaultText = "Press 'space' to start capturing keys, 'Esc' to quit."
)

// Init must be called before any other function. It initializes
// configuration and termbox.
func Init() error {
	if err := initConfig(); err != nil {
		return err
	}
	if err := initTermbox(); err != nil {
		return err
	}
	return nil
}

// Loading configuration from config.json
func initConfig() error {
	config = Config{}
	return config.Load()
}

// Initializing termbox
func initTermbox() error {
	err := termbox.Init()
	if err != nil {
		return err
	}
	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
	termbox.SetOutputMode(termbox.OutputNormal)
	return termbox.Clear(termbox.ColorWhite, termbox.ColorDefault)
}

// Close should be deferred after initialization. It finalizes termbox library.
func Close() {
	mu.Lock()
	termbox.Close()
	mu.Unlock()
}

// DrawAll draws the title, the inputs, the key labels and the status bar.
// It should also be called when a resize event is received.
func DrawAll() {
	inputs = nil
	keys = nil
	statusBar = nil
	_, keysY := drawTitle(0, 0, Version)
	keysX, sbY := drawInputs(0, keysY+1)
	_, _ = drawKeys(keysX+3, keysY+1)
	_, _ = drawStatusBar(0, sbY+1)
}

func drawTitle(x, y int, version string) (maxX, maxY int) {
	titleX, titleY := text(x, y, title)
	if version != "devel" {
		version = "v" + version
	}
	// draw version
	text(titleX+1, titleY-2, version)
	return titleX, titleY
}

func drawStatusBar(x, y int) (maxX, maxY int) {
	sbw := statusBarWidth
	statusBar = NewStatusBar(x, y, sbw, statusBarDefaultText)
	statusBar.Draw()
	return statusBar.MaxX(), statusBar.MaxY()
}

func drawInputs(x, y int) (maxX, maxY int) {
	const lw = inputLabelWidth
	const w = inputWidth
	in1 := NewInput(x, y+0, lw, "Mode", w, 0, config.ModeS(), false, InputSwitch, configMode)
	in2 := NewInput(x, y+2, lw, "TotalTime", w, inputMinutesBufWidth, config.TotalTimeS(), true, InputNumericInt, configTotalTime)
	in3 := NewInput(x, y+4, lw, "Offset", w, inputMinutesBufWidth, config.OffsetS(), true, InputNumericInt, configOffset)
	in4 := NewInput(x, y+6, lw, "BaseHz", w, inputHzBufWidth, config.BaseHzS(), true, InputNumericFloat, configBaseHz)
	in5 := NewInput(x, y+8, lw, "StartHz", w, inputHzBufWidth, config.StartHzS(), true, InputNumericFloat, configStartHz)
	in6 := NewInput(x, y+10, lw, "EndHz", w, inputHzBufWidth, config.EndHzS(), true, InputNumericFloat, configEndHz)
	inputs = nil
	inputs = append(inputs, in1, in2, in3, in4, in5, in6)
	for _, in := range inputs {
		in.Draw()
	}
	return in6.MaxX(), in6.MaxY()
}

// ReloadInputs updates each input with the values of a new configuration.
// It should be called after receiving a valid value from en enabled input.
func ReloadInputs(c Config) {
	inputs[0].T = c.ModeS()
	inputs[1].T = c.TotalTimeS()
	inputs[2].T = c.OffsetS()
	inputs[3].T = c.BaseHzS()
	inputs[4].T = c.StartHzS()
	inputs[5].T = c.EndHzS()
	for _, in := range inputs {
		in.ClearBuf()
		in.ResetText()
	}
}

func drawKeys(x, y int) (maxX, maxY int) {
	const lw = keyLabelWidth
	const w = keyWidth
	k1 := NewKeyLabel(x, y+0, lw, rtoa('q'), w, Labels['q'], false)
	k2 := NewKeyLabel(x, y+2, lw, rtoa('a'), w, Labels['a'], true)
	k3 := NewKeyLabel(x, y+4, lw, rtoa('w'), w, Labels['w'], true)
	k4 := NewKeyLabel(x, y+6, lw, rtoa('s'), w, Labels['s'], true)
	k5 := NewKeyLabel(x, y+8, lw, rtoa('e'), w, Labels['e'], true)
	k6 := NewKeyLabel(x, y+10, lw, rtoa('d'), w, Labels['d'], true)
	keys = append(keys, k1, k2, k3, k4, k5, k6)
	for _, k := range keys {
		k.Draw()
	}
	return k6.MaxX(), k6.MaxY()
}

// Cell wraps a termbox cell. A single conceptual entity on the screen. A
// screen considered a 2d array of cells. Each cell also holds a reference
// to an input if it exists, thus by clicking a cell that contains an input
// we can easily access it.
type Cell struct {
	Input *Input
	termbox.Cell
}

func cells() [][]Cell {
	mx, my := termbox.Size()
	cellBuffer := termbox.CellBuffer()
	cells := make([][]Cell, mx)
	for k := range cells {
		cells[k] = make([]Cell, my)
	}
	i, j := 0, 0
	for _, c := range cellBuffer {
		if i == mx {
			j++
			i = 0
		}
		cells[i][j].Ch = c.Ch
		cells[i][j].Fg = c.Fg
		cells[i][j].Bg = c.Bg
		i++
	}
	return cells
}

func registerInputs(cells [][]Cell) [][]Cell {
	for _, in := range inputs {
		for x := in.TextStartX(); x <= in.TextEndX(); x++ {
			cells[x][in.TextY()-1].Input = in
			cells[x][in.TextY()+0].Input = in
			cells[x][in.TextY()+1].Input = in
		}
	}
	return cells
}

// Cells initializes all cells on screen and registers the inputs on the cells
// that should have them.
func Cells() [][]Cell {
	c := cells()
	return registerInputs(c)
}

// GetCell returns a cell based on the x, y points of the screen.
func GetCell(x, y int) Cell {
	c := Cells()
	return c[x][y]
}

// Scan scans the cells of the screen that are contained at a certain y,
// between startX and endX. It returns the content of the scanned cells as
// a string.
func Scan(startX, endX, y int) string {
	c := Cells()
	var runes []rune
	for x := startX; x <= endX; x++ {
		runes = append(runes, c[x][y].Ch)
	}
	return string(runes)
}

// KeyLabel holds a label with each allowed key press and right next, the
// coresponding text that describes the label.
type KeyLabel struct {
	X      int
	Y      int
	LabelW int    // label width
	LabelT string // label text
	W      int    // rest of width
	T      string // text describing the key
	a      bool
	S      bool
}

// NewKeyLabel creates a new KeyLabel.
func NewKeyLabel(x, y, labelW int, labelT string, w int, t string, a bool) *KeyLabel {
	return &KeyLabel{x, y, labelW, labelT, w, t, a, false}
}

// MaxX returns the maximum x that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxX() int {
	return kl.X + kl.LabelW + 3 + kl.W
}

// MaxY returns the maximum y that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxY() int {
	return kl.Y + 2
}

// Draw draws the KeyLabel.
func (kl KeyLabel) Draw() {
	x := kl.X
	y := kl.Y
	lw := kl.LabelW
	lt := kl.LabelT
	w := kl.W
	t := kl.T

	fill(x, y+0, 1, 1, '┌')
	fill(x, y+1, 1, 1, '│')
	fill(x, y+2, 1, 1, '└')
	fill(x+1, y+0, lw, 1, '─')

	text(x+1, y+1, lt)
	fill(x+1, y+2, lw, 1, '─')
	fill(x+lw+1, y+0, 1, 1, '─')
	fill(x+lw+1, y+1, 1, 1, ' ')
	fill(x+lw+1, y+2, 1, 1, '─')
	fill(x+lw+2, y+0, w, 1, '─')
	text(x+lw+2, y+1, t)
	fill(x+lw+2, y+2, w, 1, '─')
	fill(x+lw+2+w, y+0, 1, 1, '┐')
	fill(x+lw+2+w, y+1, 1, 1, '│')
	fill(x+lw+2+w, y+2, 1, 1, '┘')
	if kl.a {
		fill(x, y+0, 1, 1, '├')
		fill(x+lw+2+w, y+0, 1, 1, '┤')
	}
}

// StatusBar holds the data for drawing a status bar with a specified width
// and text. It also has space to the left for the timer.
type StatusBar struct {
	X          int
	Y          int
	Width      int
	Text       string
	timerWidth int
}

// NewStatusBar returns a new StatusBar.
func NewStatusBar(x, y, width int, text string) *StatusBar {
	return &StatusBar{x, y, width, text, 6}
}

// MaxX returns the maximum x that a StatusBar can reach on the screen.
func (sb StatusBar) MaxX() int {
	return sb.X + sb.timerWidth + 2 + sb.Width
}

// MaxY returns the maximum y that a StatusBar can reach on the screen.
func (sb StatusBar) MaxY() int {
	return sb.Y + 2
}

// Draw draws the StatusBar.
func (sb StatusBar) Draw() {
	x := sb.X
	y := sb.Y
	w := sb.Width
	t := sb.Text
	tw := sb.timerWidth

	// unicode box drawing chars around the edit box
	fill(x, y+0, 1, 1, '╔')
	fill(x, y+1, 1, 1, '║')
	fill(x, y+2, 1, 1, '╚')
	fill(x+1, y+0, tw, 1, '═')
	fill(x+1, y+2, tw, 1, '═')
	fill(x+tw+1, y+0, 1, 1, '╤')
	fill(x+tw+1, y+1, 1, 1, '│')
	fill(x+tw+1, y+2, 1, 1, '╧')
	fill(x+tw+2, y+0, w, 1, '═')
	fill(x+tw+2, y+2, w, 1, '═')
	fill(x+tw+2+w, y+0, 1, 1, '╗')
	fill(x+tw+2+w, y+1, 1, 1, '║')
	fill(x+tw+2+w, y+2, 1, 1, '╝')
	text(x+tw+2, y+1, t)

	flush()
}

// UpdateTimer updates the timer of the status bar by a specified amount of
// seconds.
func (sb StatusBar) UpdateTimer(seconds int) {
	text(sb.X+1, sb.Y+1, FormatTimer(seconds))
	flush()
}

// UpdateText updates the text of the status bar.
func (sb StatusBar) UpdateText(t string) {
	//Text(sb.X+sb.timerWidth+2, sb.Y+1, text[0:sb.Width]) // text[0:sb.Width] panics for some reason
	fill(sb.X+sb.timerWidth+2, sb.Y+1, sb.Width, 1, ' ')
	text(sb.X+sb.timerWidth+2, sb.Y+1, t)
	flush()
}

// UpdateTimer is a helper function that updates the status bar's timer.
func UpdateTimer(seconds int) {
	if statusBar != nil {
		statusBar.UpdateTimer(seconds)
	}
}

// ResetTimer clears the status bar's timer.
func ResetTimer() {
	if statusBar != nil {
		fill(statusBar.X+1, statusBar.Y+1, statusBar.timerWidth, 1, ' ')
		flush()
	}
}

// UpdateText is a helper function that updates the status bar's text.
func UpdateText(text string) {
	if statusBar != nil {
		statusBar.UpdateText(text)
	}
}

// ResetText clears the status bar's text.
func ResetText() {
	if statusBar != nil {
		statusBar.UpdateText(statusBarDefaultText)
	}
}

// Capture represents a captured key press at a specific second of the timer
// along with the value of Hz that was recorded.
type Capture struct {
	Value   rune
	Seconds int
	Hz      float64
}

// Label returns the description of the captured key value.
func (c *Capture) Label() string {
	return Labels[c.Value]
}

// Timestamp returns the timestamp of when a capture happened.
func (c *Capture) Timestamp() string {
	return FormatTimer(c.Seconds)
}
<|endoftext|>


<commit_before>package ui

import (
	"time"

	"github.com/dcbishop/jkl/editor"
	"github.com/dcbishop/jkl/service"
)

// UI handles input and displaying the app.
type UI interface {
	service.Service
	Input
	Redraw(editor editor.Editor)
}

// Input implementers provide a chanel that generates events.
type Input interface {
	Events() <-chan Event
}

// Event holds information about an event.
type Event struct {
	Data interface{}
}

// FakeUI for disabling output, injecting input and testing.
type FakeUI struct {
	state     service.State
	EventChan chan Event
	Quit      chan bool
}

// NewFakeUI constructs a new FakeUI.
func NewFakeUI() FakeUI {
	fui := FakeUI{}
	return fui
}

// Run starts the service. Will block until Stop() is called.
func (ui *FakeUI) Run() {
	if ui.state.SetRunning() != nil {
		panic("Fake UI already running.")
	}

	ui.EventChan = make(chan Event)
	ui.Quit = make(chan bool)

	ui.loopUntilQuit()

	ui.state.SetStopped()
}

// Stop the service
func (ui *FakeUI) Stop() {
	if !ui.Running() {
		return
	}
	close(ui.Quit)
	service.WaitUntilStopped(ui, time.Second)
}

// Running returns the running state of the service
func (ui *FakeUI) Running() bool {
	return ui.state.Running()
}

// Events returns the channel that produces events
func (ui *FakeUI) Events() <-chan Event {
	return ui.EventChan
}

// Redraw updates the display
func (ui *FakeUI) Redraw(editor editor.Editor) {
}

func (ui *FakeUI) loopUntilQuit() {
loop:
	for {
		select {
		case <-ui.Quit:
			break loop
		}
	}
}
<commit_msg>Fix a race condition by initializing FakeUI in constructor.<commit_after>package ui

import (
	"time"

	"github.com/dcbishop/jkl/editor"
	"github.com/dcbishop/jkl/service"
)

// UI handles input and displaying the app.
type UI interface {
	service.Service
	Input
	Redraw(editor editor.Editor)
}

// Input implementers provide a chanel that generates events.
type Input interface {
	Events() <-chan Event
}

// Event holds information about an event.
type Event struct {
	Data interface{}
}

// FakeUI for disabling output, injecting input and testing.
type FakeUI struct {
	state     service.State
	eventChan chan Event
	quit      chan interface{}
}

// NewFakeUI constructs a new FakeUI.
func NewFakeUI() FakeUI {
	fui := FakeUI{}
	fui.initialize()
	return fui
}

// Run starts the service. Will block until Stop() is called.
func (ui *FakeUI) Run() {
	if ui.state.SetRunning() != nil {
		panic("Fake UI already running.")
	}

	ui.initialize()
	ui.loopUntilQuit()
	ui.state.SetStopped()
}

func (ui *FakeUI) initialize() {
	if ui.quit == nil {
		ui.quit = make(chan interface{})
	}
	if ui.eventChan == nil {
		ui.eventChan = make(chan Event)
	}
}

// Stop the service
func (ui *FakeUI) Stop() {
	if !ui.Running() {
		return
	}

	ui.quit <- true
	service.WaitUntilStopped(ui, time.Second)
}

// Running returns the running state of the service
func (ui *FakeUI) Running() bool {
	return ui.state.Running()
}

// Events returns the channel that produces events
func (ui *FakeUI) Events() <-chan Event {
	return ui.eventChan
}

// Redraw updates the display
func (ui *FakeUI) Redraw(editor editor.Editor) {
}

func (ui *FakeUI) loopUntilQuit() {
loop:
	for {
		select {
		case <-ui.quit:
			break loop
		}
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/justinas/alice"
	"github.com/rs/zerolog/hlog"
	"golang.org/x/oauth2"

	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/auth"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/service"
)

const (
	// App ID header key
	appIDHeaderKey string = "X-APP-ID"
	// API key header key
	apiKeyHeaderKey string = "X-API-KEY"
	// Authorization provider header key
	authProviderHeaderKey string = "X-AUTH-PROVIDER"
)

// jsonContentTypeResponseHandler middleware is used to add the
// application/json Content-Type Header for responses
func (s *Server) jsonContentTypeResponseHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add(contentTypeHeaderKey, appJSONContentTypeHeaderVal)
			h.ServeHTTP(w, r) // call original
		})
}

// appHandler middleware is used to parse the request app id and api key
// from the X-APP-ID and X-API-KEY headers, retrieve and validate
// their veracity, retrieve the App details from the datastore and
// finally set the App to the request context.
func (s *Server) appHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve the context from the http.Request
		ctx := r.Context()

		appExtlID, err := xHeader(defaultRealm, r.Header, appIDHeaderKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		apiKey, err := xHeader(defaultRealm, r.Header, apiKeyHeaderKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		a, err := s.FindAppService.FindAppByAPIKey(ctx, defaultRealm, appExtlID, apiKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// add access token to context
		ctx = app.CtxWithApp(ctx, a)

		// call original, adding access token to request context
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}

// userHandler middleware is used to parse the request authorization
// provider and authorization headers (X-AUTH-PROVIDER + Authorization),
// retrieve and validate their veracity, retrieve the User details from
// the datastore and finally set the User to the request context.
func (s *Server) userHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve the context from the http.Request
		ctx := r.Context()

		var (
			a     app.App
			u     user.User
			token oauth2.Token
			err   error
		)
		a, err = app.FromRequest(r)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		providerVal, err := xHeader(defaultRealm, r.Header, authProviderHeaderKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}
		provider := auth.NewProvider(providerVal)

		token, err = authHeader(defaultRealm, r.Header)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		params := service.FindUserParams{
			Realm:    defaultRealm,
			App:      a,
			Provider: provider,
			Token:    token,
		}

		u, err = s.FindUserService.FindUserByOauth2Token(ctx, params)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// add User to context
		ctx = user.CtxWithUser(ctx, u)

		// call original, adding User to request context
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}

// authorizeUserHandler middleware is used authorize a User for a request path and http method
func (s *Server) authorizeUserHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve user from request context
		adt, err := audit.FromRequest(r)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// authorize user can access the path/method
		err = s.AuthorizeService.Authorize(lgr, r, adt)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		h.ServeHTTP(w, r) // call original
	})
}

// LoggerChain returns a middleware chain (via alice.Chain)
// initialized with all the standard middleware handlers for logging. The logger
// will be added to the request context for subsequent use with pre-populated
// fields, including the request method, url, status, size, duration, remote IP,
// user agent, referer. A unique Request ID is also added to the logger, context
// and response headers.
func (s *Server) loggerChain() alice.Chain {
	ac := alice.New(hlog.NewHandler(s.Logger),
		hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
			hlog.FromRequest(r).Info().
				Str("method", r.Method).
				Stringer("url", r.URL).
				Int("status", status).
				Int("size", size).
				Dur("duration", duration).
				Msg("request logged")
		}),
		hlog.RemoteAddrHandler("remote_ip"),
		hlog.UserAgentHandler("user_agent"),
		hlog.RefererHandler("referer"),
		hlog.RequestIDHandler("request_id", "Request-Id"),
	)

	return ac
}

// xHeader parses and returns the header value given the key. It is
// used to validate various header values as part of authentication
func xHeader(realm string, header http.Header, key string) (v string, err error) {
	// Pull the header value from the Header map given the key
	headerValue, ok := header[http.CanonicalHeaderKey(key)]
	if !ok {
		return "", errs.E(errs.Unauthenticated, errs.Realm(realm), fmt.Sprintf("unauthenticated: no %s header sent", key))

	}

	// too many values sent - should only be one value
	if len(headerValue) > 1 {
		return "", errs.E(errs.Unauthenticated, errs.Realm(realm), fmt.Sprintf("%s header value > 1", key))
	}

	// retrieve header value from map
	v = headerValue[0]

	// remove all leading/trailing white space
	v = strings.TrimSpace(v)

	// should not be empty
	if v == "" {
		return "", errs.E(errs.Unauthenticated, errs.Realm(realm), fmt.Sprintf("unauthenticated: %s header value not found", key))
	}

	return
}

// authHeader parses/validates the Authorization header and returns an Oauth2 token
func authHeader(realm string, header http.Header) (oauth2.Token, error) {
	// Pull the token from the Authorization header by retrieving the
	// value from the Header map with "Authorization" as the key
	//
	// format: Authorization: Bearer
	headerValue, ok := header["Authorization"]
	if !ok {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "unauthenticated: no Authorization header sent")
	}

	// too many values sent - spec allows for only one token
	if len(headerValue) > 1 {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "header value > 1")
	}

	// retrieve token from map
	token := headerValue[0]

	// Oauth2 should have "Bearer " as the prefix as the authentication scheme
	hasBearer := strings.HasPrefix(token, auth.BearerTokenType+" ")
	if !hasBearer {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "unauthenticated: Bearer authentication scheme not found")
	}

	// remove "Bearer " authentication scheme from header value
	token = strings.TrimPrefix(token, auth.BearerTokenType+" ")

	// remove all leading/trailing white space
	token = strings.TrimSpace(token)

	// token should not be empty
	if token == "" {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "unauthenticated: Authorization header sent with Bearer scheme, but no token found")
	}

	return oauth2.Token{AccessToken: token, TokenType: auth.BearerTokenType}, nil
}
<commit_msg>allow existing users and newly registered users<commit_after>package server

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/justinas/alice"
	"github.com/rs/zerolog/hlog"
	"golang.org/x/oauth2"

	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/auth"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/service"
)

const (
	// App ID header key
	appIDHeaderKey string = "X-APP-ID"
	// API key header key
	apiKeyHeaderKey string = "X-API-KEY"
	// Authorization provider header key
	authProviderHeaderKey string = "X-AUTH-PROVIDER"
)

// jsonContentTypeResponseHandler middleware is used to add the
// application/json Content-Type Header for responses
func (s *Server) jsonContentTypeResponseHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add(contentTypeHeaderKey, appJSONContentTypeHeaderVal)
			h.ServeHTTP(w, r) // call original
		})
}

// appHandler middleware is used to parse the request app id and api key
// from the X-APP-ID and X-API-KEY headers, retrieve and validate
// their veracity, retrieve the App details from the datastore and
// finally set the App to the request context.
func (s *Server) appHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve the context from the http.Request
		ctx := r.Context()

		appExtlID, err := xHeader(defaultRealm, r.Header, appIDHeaderKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		apiKey, err := xHeader(defaultRealm, r.Header, apiKeyHeaderKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		a, err := s.FindAppService.FindAppByAPIKey(ctx, defaultRealm, appExtlID, apiKey)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// add access token to context
		ctx = app.CtxWithApp(ctx, a)

		// call original, adding access token to request context
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}

// userHandler middleware is used to parse the request authorization
// provider and authorization headers (X-AUTH-PROVIDER + Authorization respectively),
// retrieve and validate their veracity, retrieve the User details from
// the Oauth2 provider as well as the datastore and finally set the User
// to the request context.
func (s *Server) userHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve the context from the http.Request
		ctx := r.Context()

		u, err := newUser(ctx, s.FindUserService, r, true)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// add User to context
		ctx = user.CtxWithUser(ctx, u)

		// call original, adding User to request context
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}

// newUserHandler middleware is used to parse the request authorization
// provider and authorization headers (X-AUTH-PROVIDER + Authorization respectively),
// retrieve and validate their veracity, retrieve the User details from
// the Oauth2 provider and finally set the User to the request context.
func (s *Server) newUserHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve the context from the http.Request
		ctx := r.Context()

		u, err := newUser(ctx, s.FindUserService, r, false)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// add User to context
		ctx = user.CtxWithUser(ctx, u)

		// call original, adding User to request context
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}

func newUser(ctx context.Context, s FindUserService, r *http.Request, retrieveFromDB bool) (user.User, error) {

	var (
		a     app.App
		u     user.User
		token oauth2.Token
		err   error
	)
	a, err = app.FromRequest(r)
	if err != nil {
		return user.User{}, err
	}

	providerVal, err := xHeader(defaultRealm, r.Header, authProviderHeaderKey)
	if err != nil {
		return user.User{}, err
	}
	provider := auth.NewProvider(providerVal)

	token, err = authHeader(defaultRealm, r.Header)
	if err != nil {
		return user.User{}, err
	}

	params := service.FindUserParams{
		Realm:          defaultRealm,
		App:            a,
		Provider:       provider,
		Token:          token,
		RetrieveFromDB: retrieveFromDB,
	}

	u, err = s.FindUserByOauth2Token(ctx, params)
	if err != nil {
		return user.User{}, err
	}

	return u, nil
}

// authorizeUserHandler middleware is used authorize a User for a request path and http method
func (s *Server) authorizeUserHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lgr := *hlog.FromRequest(r)

		// retrieve user from request context
		adt, err := audit.FromRequest(r)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		// authorize user can access the path/method
		err = s.AuthorizeService.Authorize(lgr, r, adt)
		if err != nil {
			errs.HTTPErrorResponse(w, lgr, err)
			return
		}

		h.ServeHTTP(w, r) // call original
	})
}

// LoggerChain returns a middleware chain (via alice.Chain)
// initialized with all the standard middleware handlers for logging. The logger
// will be added to the request context for subsequent use with pre-populated
// fields, including the request method, url, status, size, duration, remote IP,
// user agent, referer. A unique Request ID is also added to the logger, context
// and response headers.
func (s *Server) loggerChain() alice.Chain {
	ac := alice.New(hlog.NewHandler(s.Logger),
		hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
			hlog.FromRequest(r).Info().
				Str("method", r.Method).
				Stringer("url", r.URL).
				Int("status", status).
				Int("size", size).
				Dur("duration", duration).
				Msg("request logged")
		}),
		hlog.RemoteAddrHandler("remote_ip"),
		hlog.UserAgentHandler("user_agent"),
		hlog.RefererHandler("referer"),
		hlog.RequestIDHandler("request_id", "Request-Id"),
	)

	return ac
}

// xHeader parses and returns the header value given the key. It is
// used to validate various header values as part of authentication
func xHeader(realm string, header http.Header, key string) (v string, err error) {
	// Pull the header value from the Header map given the key
	headerValue, ok := header[http.CanonicalHeaderKey(key)]
	if !ok {
		return "", errs.E(errs.Unauthenticated, errs.Realm(realm), fmt.Sprintf("unauthenticated: no %s header sent", key))

	}

	// too many values sent - should only be one value
	if len(headerValue) > 1 {
		return "", errs.E(errs.Unauthenticated, errs.Realm(realm), fmt.Sprintf("%s header value > 1", key))
	}

	// retrieve header value from map
	v = headerValue[0]

	// remove all leading/trailing white space
	v = strings.TrimSpace(v)

	// should not be empty
	if v == "" {
		return "", errs.E(errs.Unauthenticated, errs.Realm(realm), fmt.Sprintf("unauthenticated: %s header value not found", key))
	}

	return
}

// authHeader parses/validates the Authorization header and returns an Oauth2 token
func authHeader(realm string, header http.Header) (oauth2.Token, error) {
	// Pull the token from the Authorization header by retrieving the
	// value from the Header map with "Authorization" as the key
	//
	// format: Authorization: Bearer
	headerValue, ok := header["Authorization"]
	if !ok {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "unauthenticated: no Authorization header sent")
	}

	// too many values sent - spec allows for only one token
	if len(headerValue) > 1 {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "header value > 1")
	}

	// retrieve token from map
	token := headerValue[0]

	// Oauth2 should have "Bearer " as the prefix as the authentication scheme
	hasBearer := strings.HasPrefix(token, auth.BearerTokenType+" ")
	if !hasBearer {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "unauthenticated: Bearer authentication scheme not found")
	}

	// remove "Bearer " authentication scheme from header value
	token = strings.TrimPrefix(token, auth.BearerTokenType+" ")

	// remove all leading/trailing white space
	token = strings.TrimSpace(token)

	// token should not be empty
	if token == "" {
		return oauth2.Token{}, errs.E(errs.Unauthenticated, errs.Realm(realm), "unauthenticated: Authorization header sent with Bearer scheme, but no token found")
	}

	return oauth2.Token{AccessToken: token, TokenType: auth.BearerTokenType}, nil
}
<|endoftext|>


<commit_before>package tree

import "github.com/catorpilor/leetcode/utils"

func increasingBST(root *utils.TreeNode) *utils.TreeNode {
    return useDFS(root)
}

// useDFS time complexity O(N), space complexity O(lgN)
func useDFS(root *utils.TreeNode) *utils.TreeNode {
    if root == nil || root.Left == nil && root.Right == nil {
        return root
    }
    var lh, lt *utils.TreeNode
    if root.Left != nil {
        lh, lt = helper(root.Left)
    }
    var rh *utils.TreeNode
    if root.Right != nil {
        rh, _ = helper(root.Right)
    }
    if lt != nil {
        lt.Right = root
    } else {
        lh = root
    }
    // rh, _ := helper(root.Right)
    root.Left = nil
    root.Right = rh
    return lh
}

func helper(root *utils.TreeNode) (head, tail *utils.TreeNode) {
    if root.Left == nil {
        head = root
        if root.Right != nil {
            lh, lt := helper(root.Right)
            root.Right = lh
            tail = lt
        } else {
            tail = root
        }
    } else {
        var lt *utils.TreeNode
        head, lt = helper(root.Left)
        lt.Right = root
        root.Left = nil
        if root.Right == nil {
            tail = root
        } else {
            rh, rt := helper(root.Right)
            root.Right = rh
            tail = rt
        }
    }
    return
}
<commit_msg>refactor and add some comments<commit_after>package tree

import "github.com/catorpilor/leetcode/utils"

func increasingBST(root *utils.TreeNode) *utils.TreeNode {
    return useDFS(root)
}

// useDFS time complexity O(N), space complexity O(lgN)
func useDFS(root *utils.TreeNode) *utils.TreeNode {
    if root == nil || root.Left == nil && root.Right == nil {
        return root
    }
    var lh, lt *utils.TreeNode
    if root.Left != nil {
        lh, lt = helper(root.Left)
    }
    var rh *utils.TreeNode
    if root.Right != nil {
        rh, _ = helper(root.Right)
    }
    if lt != nil {
        lt.Right = root
    } else {
        // root.Left is nil, so root remains the root
        lh = root
    }
    // rh, _ := helper(root.Right)
    root.Left = nil
    root.Right = rh
    return lh
}

func helper(root *utils.TreeNode) (head, tail *utils.TreeNode) {
    if root.Left == nil {
        head = root
    } else {
        var lt *utils.TreeNode
        head, lt = helper(root.Left)
        lt.Right = root
        // rearrange the tree to avoid TLE
        root.Left = nil
    }
    if root.Right != nil {
        lh, lt := helper(root.Right)
        root.Right = lh
        tail = lt
    } else {
        tail = root
    }
    return
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

// Package utils 一些常用的函数集合。
package utils

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"os"
	"path/filepath"
	"reflect"

	"github.com/issue9/localeutil"
	"github.com/issue9/source"
	"golang.org/x/text/language"
)

// GetSystemLanguageTag 返回当前系统的本地化信息
//
// *nix 系统会使用 LANG 环境变量中的值，windows 在 LANG
// 环境变量不存在的情况下，调用 GetUserDefaultLocaleName 函数获取。
func GetSystemLanguageTag() (language.Tag, error) {
	return localeutil.SystemLanguageTag()
}

// MD5 将一段字符串转换成 md5 编码
func MD5(str string) string {
	m := md5.New()
	m.Write([]byte(str))
	return hex.EncodeToString(m.Sum(nil))
}

// TraceStack 返回调用者的堆栈信息
func TraceStack(level int, msg ...interface{}) (string, error) {
	return source.TraceStack(level, msg...)
}

// SplitPath 将路径按分隔符分隔成字符串数组。比如：
//  /a/b/c  ==>  []string{"a", "b", "c"}
func SplitPath(path string) []string {
	vol := filepath.VolumeName(path)
	ret := make([]string, 0, 10)

	index := 0
	if len(vol) > 0 {
		ret = append(ret, vol)
		path = path[len(vol)+1:]
	}
	for i := 0; i < len(path); i++ {
		if os.IsPathSeparator(path[i]) {
			if i > index {
				ret = append(ret, path[index:i])
			}
			index = i + 1 // 过滤掉此符号
		}
	}

	if len(path) > index {
		ret = append(ret, path[index:])
	}

	return ret
}

// HasDuplication 检测数组中是否包含重复的值
//
// slice 需要检测的数组或是切片，其它类型会 panic；
// eq 对比数组中两个值是否相等，相等需要返回 true；
// 返回值表示存在相等值时，第二个值在数组中的下标值；
func HasDuplication(slice interface{}, eq func(i, j int) bool) int {
	v := reflect.ValueOf(slice)
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprint("参数 slice 只能是 slice 或是 array"))
	}

	for i := 0; i < v.Len(); i++ {
		for j := i + 1; j < v.Len(); j++ {
			if eq(i, j) {
				return j
			}
		}
	}

	return -1
}
<commit_msg>fix: TraceStack 现在引入其它，调用层级多了一次，需要在原有的level 上加 1<commit_after>// SPDX-License-Identifier: MIT

// Package utils 一些常用的函数集合。
package utils

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"os"
	"path/filepath"
	"reflect"

	"github.com/issue9/localeutil"
	"github.com/issue9/source"
	"golang.org/x/text/language"
)

// GetSystemLanguageTag 返回当前系统的本地化信息
//
// *nix 系统会使用 LANG 环境变量中的值，windows 在 LANG
// 环境变量不存在的情况下，调用 GetUserDefaultLocaleName 函数获取。
func GetSystemLanguageTag() (language.Tag, error) {
	return localeutil.SystemLanguageTag()
}

// MD5 将一段字符串转换成 md5 编码
func MD5(str string) string {
	m := md5.New()
	m.Write([]byte(str))
	return hex.EncodeToString(m.Sum(nil))
}

// TraceStack 返回调用者的堆栈信息
func TraceStack(level int, msg ...interface{}) (string, error) {
	return source.TraceStack(level+1, msg...)
}

// SplitPath 将路径按分隔符分隔成字符串数组。比如：
//  /a/b/c  ==>  []string{"a", "b", "c"}
func SplitPath(path string) []string {
	vol := filepath.VolumeName(path)
	ret := make([]string, 0, 10)

	index := 0
	if len(vol) > 0 {
		ret = append(ret, vol)
		path = path[len(vol)+1:]
	}
	for i := 0; i < len(path); i++ {
		if os.IsPathSeparator(path[i]) {
			if i > index {
				ret = append(ret, path[index:i])
			}
			index = i + 1 // 过滤掉此符号
		}
	}

	if len(path) > index {
		ret = append(ret, path[index:])
	}

	return ret
}

// HasDuplication 检测数组中是否包含重复的值
//
// slice 需要检测的数组或是切片，其它类型会 panic；
// eq 对比数组中两个值是否相等，相等需要返回 true；
// 返回值表示存在相等值时，第二个值在数组中的下标值；
func HasDuplication(slice interface{}, eq func(i, j int) bool) int {
	v := reflect.ValueOf(slice)
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprint("参数 slice 只能是 slice 或是 array"))
	}

	for i := 0; i < v.Len(); i++ {
		for j := i + 1; j < v.Len(); j++ {
			if eq(i, j) {
				return j
			}
		}
	}

	return -1
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/gcfg" //Ref: http://code.google.com/p/gcfg/
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
)

type Config struct {
	Auth struct {
		AccessKey string
		SecretKey string
	}

	Setup struct {
		Log       string
		Overwrite bool
	}

	Locations struct {
		Source      []string
		Destination []string
	}
}

//Get the configurations from ~/.s3upload.conf
func getConfigs() Config {
	usr, err := user.Current()
	if err != nil {
		logger.Fatalln(err.Error())
	}

	file := usr.HomeDir + string(os.PathSeparator) + ".s3upload.conf"
	var cfg Config
	cfg_err := gcfg.ReadFileInto(&cfg, file)
	if cfg_err != nil {
		err := ioutil.WriteFile(file, []byte(newConfig), 644)
		if err != nil {
			logger.Println("Could not create .s3upload.conf in the current user's home folder.")
		} else {
			fmt.Println(".s3upload.conf was successfully created in the current user's home folder.",
				" Please edit it and enter correct settings.")
		}
		os.Exit(1)
	}
	return cfg
}

var newConfig = `[Auth]
AccessKey="your_amazon_accesskey"  # Get from Amazon
SecretKey="your_amazon_secretkey"  # Get from Amazon

[Setup]
#Setting this option to true may incur additional cost
overwrite=false

[Locations]
source=""       # local directory to upload
destination=""  # S3 full path

# You can make s3upload upload multiple location by uncommenting the following
# lines and repeating them as much as you need.
# source=""
# destination=""
`
<commit_msg>Check if config file exists before checking for other errors.<commit_after>package main

import (
	"code.google.com/p/gcfg" //Ref: http://code.google.com/p/gcfg/
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
)

type Config struct {
	Auth struct {
		AccessKey string
		SecretKey string
	}

	Setup struct {
		Log       string
		Overwrite bool
	}

	Locations struct {
		Source      []string
		Destination []string
	}
}

//Get the configurations from ~/.s3upload.conf
func getConfigs() Config {
	usr, err := user.Current()
	if err != nil {
		logger.Fatalln(err.Error())
	}

	file := usr.HomeDir + string(os.PathSeparator) + ".s3upload.conf"

	//if the file does not exist create it
	if _, err := os.Stat(file); os.IsNotExist(err) {
		err := ioutil.WriteFile(file, []byte(newConfig), 644)
		if err != nil {
			//logger is not initialized yet so use Go's default log
			log.Fatalln("Could not create .s3upload.conf in the current user's home folder.")
		} else {
			log.Fatalln(".s3upload.conf was successfully created in the current user's home folder.",
				" Please edit it and enter correct settings.")
		}
	}

	var cfg Config
	cfg_err := gcfg.ReadFileInto(&cfg, file)
	if cfg_err != nil {
		fmt.Println(cfg_err.Error())
		os.Exit(1)
	}
	return cfg
}

var newConfig = `[Auth]
AccessKey="your_amazon_accesskey"  # Get from Amazon
SecretKey="your_amazon_secretkey"  # Get from Amazon

[Setup]
#Setting this option to true may incur additional cost
overwrite=false

[Locations]
source=""       # local directory to upload
destination=""  # S3 full path

# You can make s3upload upload multiple location by uncommenting the following
# lines and repeating them as much as you need.
# source=""
# destination=""
`
<|endoftext|>


<commit_before>package restpc

import (
	"fmt"
	"reflect"
	"runtime"
)

func getFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func mapFromKeyValuePairs(kvPairs ...interface{}) map[string]interface{} {
	if len(kvPairs)%2 != 0 {
		panic(fmt.Sprintf(
			"mapFromKeyValuePairs: must give even number of args, give %v",
			len(kvPairs),
		))
	}
	m := map[string]interface{}{}
	for i := 0; i < len(kvPairs)/2; i++ {
		m[fmt.Sprintf("%v", kvPairs[i])] = kvPairs[i+1]
	}
	return m
}
<commit_msg>remove unused func mapFromKeyValuePairs<commit_after>package restpc

import (
	"reflect"
	"runtime"
)

func getFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
<|endoftext|>