<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 inode

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

type DirInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	implicitDirs bool

	// The the GCS object backing the inode. The object's name is used as a
	// prefix when listing. Special case: the empty string means this is the root
	// inode.
	//
	// INVARIANT: src != nil
	// INVARIANT: src.Name == "" || src.Name[len(name)-1] == '/'
	src gcs.Object

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

	lc lookupCount
}

var _ Inode = &DirInode{}

// Set notes on NewRootInode.
const RootGen int64 = 0

// Create a directory inode for the root of the file system. For this inode,
// the result of SourceGeneration() is guaranteed to be RootGen.
//
// The initial lookup count is one.
func NewRootInode(
	bucket gcs.Bucket,
	implicitDirs bool) (d *DirInode) {
	dummy := &gcs.Object{
		Name:       "",
		Generation: RootGen,
	}

	d = NewDirInode(bucket, fuseops.RootInodeID, dummy, implicitDirs)
	return
}

// Create a directory inode for the supplied source object. The object's name
// must end with a slash unless this is the root directory, in which case it
// must be empty.
//
// If implicitDirs is set, LookUpChild will use ListObjects to find child
// directories that are "implicitly" defined by the existence of their own
// descendents. For example, if there is an object named "foo/bar/baz" and this
// is the directory "foo", a child directory named "bar" will be implied.
//
// The initial lookup count is one.
//
// REQUIRES: o != nil
// REQUIRES: o.Name == "" || o.Name[len(o.Name)-1] == '/'
func NewDirInode(
	bucket gcs.Bucket,
	id fuseops.InodeID,
	o *gcs.Object,
	implicitDirs bool) (d *DirInode) {
	if o.Name != "" && o.Name[len(o.Name)-1] != '/' {
		panic(fmt.Sprintf("Unexpected name: %s", o.Name))
	}

	// Set up the struct.
	d = &DirInode{
		bucket:       bucket,
		id:           id,
		implicitDirs: implicitDirs,
		src:          *o,
	}

	d.lc = lookupCount{
		count:   1,
		destroy: func() error { return nil },
	}

	return
}

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

func (d *DirInode) checkInvariants() {
	// INVARIANT: src.Name == "" || src.Name[len(name)-1] == '/'
	if !(d.src.Name == "" || d.src.Name[len(d.src.Name)-1] == '/') {
		panic(fmt.Sprintf("Unexpected name: %s", d.src.Name))
	}
}

func (d *DirInode) clobbered(ctx context.Context) (clobbered bool, err error) {
	// Special case: the root is never clobbered.
	if d.ID() == fuseops.RootInodeID {
		return
	}

	// Stat the backing object.
	req := &gcs.StatObjectRequest{
		Name: d.Name(),
	}

	o, err := d.bucket.StatObject(ctx, req)

	// "Not found" means clobbered.
	if _, ok := err.(*gcs.NotFoundError); ok {
		clobbered = true
		err = nil
		return
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// We are clobbered if the generation number has changed.
	clobbered = o.Generation != d.SourceGeneration()

	return
}

func (d *DirInode) lookUpChildFile(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	o, err = statObjectMayNotExist(ctx, d.bucket, d.Name()+name)
	if err != nil {
		err = fmt.Errorf("statObjectMayNotExist: %v", err)
		return
	}

	return
}

func (d *DirInode) lookUpChildDir(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	b := syncutil.NewBundle(ctx)

	// Stat the placeholder object.
	b.Add(func(ctx context.Context) (err error) {
		o, err = statObjectMayNotExist(ctx, d.bucket, d.Name()+name+"/")
		if err != nil {
			err = fmt.Errorf("statObjectMayNotExist: %v", err)
			return
		}

		return
	})

	// If implicit directories are enabled, find out whether the child name is
	// implicitly defined.
	var implicitlyDefined bool
	if d.implicitDirs {
		b.Add(func(ctx context.Context) (err error) {
			implicitlyDefined, err = objectNamePrefixNonEmpty(
				ctx,
				d.bucket,
				d.Name()+name+"/")

			if err != nil {
				err = fmt.Errorf("objectNamePrefixNonEmpty: %v", err)
				return
			}

			return
		})
	}

	// Wait for both.
	err = b.Join()
	if err != nil {
		return
	}

	// If statting failed by the directory is implicitly defined, fake a source
	// object.
	if o == nil && implicitlyDefined {
		o = &gcs.Object{
			Name:       d.Name() + name + "/",
			Generation: ImplicitDirGen,
		}
	}

	return
}

// Look up the file for a (file, dir) pair with conflicting names, overriding
// the default behavior. If the file doesn't exist, return a nil record with a
// nil error. If the directory doesn't exist, pretend the file doesn't exist.
//
// REQUIRES: strings.HasSuffix(name, ConflictingFileNameSuffix)
func (d *DirInode) lookUpConflicting(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	strippedName := strings.TrimSuffix(name, ConflictingFileNameSuffix)

	// In order to a marked name to be accepted, we require the conflicting
	// directory to exist.
	var dir *gcs.Object
	dir, err = d.lookUpChildDir(ctx, strippedName)
	if err != nil {
		err = fmt.Errorf("lookUpChildDir for stripped name: %v", err)
		return
	}

	if dir == nil {
		return
	}

	// The directory name exists. Find the conflicting file.
	o, err = d.lookUpChildFile(ctx, strippedName)
	if err != nil {
		err = fmt.Errorf("lookUpChildFile for stripped name: %v", err)
		return
	}

	return
}

// List the supplied object name prefix to find out whether it is non-empty.
func objectNamePrefixNonEmpty(
	ctx context.Context,
	bucket gcs.Bucket,
	prefix string) (nonEmpty bool, err error) {
	req := &gcs.ListObjectsRequest{
		Prefix:     prefix,
		MaxResults: 1,
	}

	listing, err := bucket.ListObjects(ctx, req)
	if err != nil {
		err = fmt.Errorf("ListObjects: %v", err)
		return
	}

	nonEmpty = len(listing.Objects) != 0
	return
}

// Stat the object with the given name, returning (nil, nil) if the object
// doesn't exist rather than failing.
func statObjectMayNotExist(
	ctx context.Context,
	bucket gcs.Bucket,
	name string) (o *gcs.Object, err error) {
	// Call the bucket.
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	o, err = bucket.StatObject(ctx, req)

	// Suppress "not found" errors.
	if _, ok := err.(*gcs.NotFoundError); ok {
		err = nil
	}

	// Annotate others.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (d *DirInode) Lock() {
	// We don't require locks.
}

func (d *DirInode) Unlock() {
	// We don't require locks.
}

func (d *DirInode) ID() fuseops.InodeID {
	return d.id
}

// Return the full name of the directory object in GCS, including the trailing
// slash (e.g. "foo/bar/").
func (d *DirInode) Name() string {
	return d.src.Name
}

func (d *DirInode) SourceGeneration() int64 {
	return d.src.Generation
}

// LOCKS_REQUIRED(d.mu)
func (d *DirInode) IncrementLookupCount() {
	d.lc.Inc()
}

// LOCKS_REQUIRED(d.mu)
func (d *DirInode) DecrementLookupCount(n uint64) (destroyed bool) {
	destroyed = d.lc.Dec(n)
	return
}

func (d *DirInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	// Find out whether the backing object has been clobbered in GCS.
	clobbered, err := d.clobbered(ctx)
	if err != nil {
		err = fmt.Errorf("clobbered: %v", err)
		return
	}

	// Set up basic attributes.
	attrs = fuseops.InodeAttributes{
		Mode: 0700 | os.ModeDir,
	}

	// Modify Nlink as appropriate.
	if !clobbered {
		attrs.Nlink = 1
	}

	return
}

// See notes on DirInode.LookUpChild.
const ImplicitDirGen int64 = -1

// A suffix that can be used to unambiguously tag a file system name.
// (Unambiguous because U+000A is not allowed in GCS object names.) This is
// used to refer to the file in a (file, directory) pair with conflicting
// object names.
//
// See also the notes on DirInode.LookUpChild.
const ConflictingFileNameSuffix = "\n"

// Look up the direct child with the given relative name, returning a record
// for the current object of that name in the GCS bucket. If both a file and a
// directory with the given name exist, the directory is preferred. Return a
// nil record with a nil error if neither is found.
//
// Special case: if the name ends in ConflictingFileNameSuffix, we strip the
// suffix, confirm that a conflicting directory exists, then return a record
// for the file.
//
// If this inode was created with implicitDirs is set, this method will use
// ListObjects to find child directories that are "implicitly" defined by the
// existence of their own descendents. For example, if there is an object named
// "foo/bar/baz" and this is the directory "foo", a child directory named "bar"
// will be implied. In this case, the child's generation will be the sentinel
// value ImplicitDirGen.
func (d *DirInode) LookUpChild(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	b := syncutil.NewBundle(ctx)

	// Is this a conflict marker name?
	if strings.HasSuffix(name, ConflictingFileNameSuffix) {
		o, err = d.lookUpConflicting(ctx, name)
		return
	}

	// Stat the child as a file.
	var fileRecord *gcs.Object
	b.Add(func(ctx context.Context) (err error) {
		fileRecord, err = d.lookUpChildFile(ctx, name)
		return
	})

	// Stat the child as a directory.
	var dirRecord *gcs.Object
	b.Add(func(ctx context.Context) (err error) {
		dirRecord, err = d.lookUpChildDir(ctx, name)
		return
	})

	// Wait for both.
	err = b.Join()
	if err != nil {
		return
	}

	// Prefer directories over files.
	switch {
	case dirRecord != nil:
		o = dirRecord
	case fileRecord != nil:
		o = fileRecord
	}

	return
}

// Read some number of entries from the directory, returning a continuation
// token that can be used to pick up the read operation where it left off.
// Supply the empty token on the first call.
//
// At the end of the directory, the returned continuation token will be empty.
// Otherwise it will be non-empty. There is no guarantee about the number of
// entries returned; it may be zero even with a non-empty continuation token.
//
// The contents of the Offset and Inode fields for returned entries is
// undefined.
//
// Warning: This method always behaves as if implicit directories are enabled,
// regardless of how the inode was configured. If you want to ensure that
// directories actually exist it non-implicit mode, you must call LookUpChild
// to do so.
func (d *DirInode) ReadEntries(
	ctx context.Context,
	tok string) (entries []fuseutil.Dirent, newTok string, err error) {
	// Ask the bucket to list some objects.
	req := &gcs.ListObjectsRequest{
		Delimiter:         "/",
		Prefix:            d.Name(),
		ContinuationToken: tok,
	}

	listing, err := d.bucket.ListObjects(ctx, req)
	if err != nil {
		err = fmt.Errorf("ListObjects: %v", err)
		return
	}

	// Convert objects to entries for files.
	for _, o := range listing.Objects {
		// Skip the entry for the backing object itself, which of course has its
		// own name as a prefix but which we don't wan tto appear to contain
		// itself.
		if o.Name == d.Name() {
			continue
		}

		e := fuseutil.Dirent{
			Name: path.Base(o.Name),
			Type: fuseutil.DT_File,
		}

		entries = append(entries, e)
	}

	// Convert runs to entries for directories.
	for _, p := range listing.CollapsedRuns {
		e := fuseutil.Dirent{
			Name: path.Base(p),
			Type: fuseutil.DT_Directory,
		}

		entries = append(entries, e)
	}

	// Return an appropriate continuation token, if any.
	newTok = listing.ContinuationToken

	return
}
<commit_msg>Fixed locking on directory inodes.<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 inode

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

type DirInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	implicitDirs bool

	// The the GCS object backing the inode. The object's name is used as a
	// prefix when listing. Special case: the empty string means this is the root
	// inode.
	//
	// INVARIANT: src.Name == "" || src.Name[len(name)-1] == '/'
	// INVARIANT: src.Generation == RootGen implies id == fuseops.RootInodeID
	src gcs.Object

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

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount
}

var _ Inode = &DirInode{}

// Set notes on NewRootInode.
const RootGen int64 = 0

// Create a directory inode for the root of the file system. For this inode,
// the result of SourceGeneration() is guaranteed to be RootGen.
//
// The initial lookup count is one.
func NewRootInode(
	bucket gcs.Bucket,
	implicitDirs bool) (d *DirInode) {
	dummy := &gcs.Object{
		Name:       "",
		Generation: RootGen,
	}

	d = NewDirInode(bucket, fuseops.RootInodeID, dummy, implicitDirs)
	return
}

// Create a directory inode for the supplied source object. The object's name
// must end with a slash unless this is the root directory, in which case it
// must be empty.
//
// If implicitDirs is set, LookUpChild will use ListObjects to find child
// directories that are "implicitly" defined by the existence of their own
// descendents. For example, if there is an object named "foo/bar/baz" and this
// is the directory "foo", a child directory named "bar" will be implied.
//
// The initial lookup count is one.
//
// REQUIRES: o != nil
// REQUIRES: o.Name == "" || o.Name[len(o.Name)-1] == '/'
func NewDirInode(
	bucket gcs.Bucket,
	id fuseops.InodeID,
	o *gcs.Object,
	implicitDirs bool) (d *DirInode) {
	if o.Name != "" && o.Name[len(o.Name)-1] != '/' {
		panic(fmt.Sprintf("Unexpected name: %s", o.Name))
	}

	// Set up the struct.
	d = &DirInode{
		bucket:       bucket,
		id:           id,
		implicitDirs: implicitDirs,
		src:          *o,
	}

	d.lc = lookupCount{
		count:   1,
		destroy: func() error { return nil },
	}

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

	return
}

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

func (d *DirInode) checkInvariants() {
	// INVARIANT: src.Name == "" || src.Name[len(name)-1] == '/'
	if !(d.src.Name == "" || d.src.Name[len(d.src.Name)-1] == '/') {
		panic(fmt.Sprintf("Unexpected name: %s", d.src.Name))
	}

	// INVARIANT: src.Generation == RootGen implies id == fuseops.RootInodeID
	if src.Generation == RootGen && id != fuseops.RootInodeID {
		panic("Unexpected root generation number")
	}
}

func (d *DirInode) clobbered(ctx context.Context) (clobbered bool, err error) {
	// Special case: the root is never clobbered.
	if d.ID() == fuseops.RootInodeID {
		return
	}

	// Stat the backing object.
	req := &gcs.StatObjectRequest{
		Name: d.Name(),
	}

	o, err := d.bucket.StatObject(ctx, req)

	// "Not found" means clobbered.
	if _, ok := err.(*gcs.NotFoundError); ok {
		clobbered = true
		err = nil
		return
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// We are clobbered if the generation number has changed.
	clobbered = o.Generation != d.SourceGeneration()

	return
}

func (d *DirInode) lookUpChildFile(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	o, err = statObjectMayNotExist(ctx, d.bucket, d.Name()+name)
	if err != nil {
		err = fmt.Errorf("statObjectMayNotExist: %v", err)
		return
	}

	return
}

func (d *DirInode) lookUpChildDir(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	b := syncutil.NewBundle(ctx)

	// Stat the placeholder object.
	b.Add(func(ctx context.Context) (err error) {
		o, err = statObjectMayNotExist(ctx, d.bucket, d.Name()+name+"/")
		if err != nil {
			err = fmt.Errorf("statObjectMayNotExist: %v", err)
			return
		}

		return
	})

	// If implicit directories are enabled, find out whether the child name is
	// implicitly defined.
	var implicitlyDefined bool
	if d.implicitDirs {
		b.Add(func(ctx context.Context) (err error) {
			implicitlyDefined, err = objectNamePrefixNonEmpty(
				ctx,
				d.bucket,
				d.Name()+name+"/")

			if err != nil {
				err = fmt.Errorf("objectNamePrefixNonEmpty: %v", err)
				return
			}

			return
		})
	}

	// Wait for both.
	err = b.Join()
	if err != nil {
		return
	}

	// If statting failed by the directory is implicitly defined, fake a source
	// object.
	if o == nil && implicitlyDefined {
		o = &gcs.Object{
			Name:       d.Name() + name + "/",
			Generation: ImplicitDirGen,
		}
	}

	return
}

// Look up the file for a (file, dir) pair with conflicting names, overriding
// the default behavior. If the file doesn't exist, return a nil record with a
// nil error. If the directory doesn't exist, pretend the file doesn't exist.
//
// REQUIRES: strings.HasSuffix(name, ConflictingFileNameSuffix)
func (d *DirInode) lookUpConflicting(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	strippedName := strings.TrimSuffix(name, ConflictingFileNameSuffix)

	// In order to a marked name to be accepted, we require the conflicting
	// directory to exist.
	var dir *gcs.Object
	dir, err = d.lookUpChildDir(ctx, strippedName)
	if err != nil {
		err = fmt.Errorf("lookUpChildDir for stripped name: %v", err)
		return
	}

	if dir == nil {
		return
	}

	// The directory name exists. Find the conflicting file.
	o, err = d.lookUpChildFile(ctx, strippedName)
	if err != nil {
		err = fmt.Errorf("lookUpChildFile for stripped name: %v", err)
		return
	}

	return
}

// List the supplied object name prefix to find out whether it is non-empty.
func objectNamePrefixNonEmpty(
	ctx context.Context,
	bucket gcs.Bucket,
	prefix string) (nonEmpty bool, err error) {
	req := &gcs.ListObjectsRequest{
		Prefix:     prefix,
		MaxResults: 1,
	}

	listing, err := bucket.ListObjects(ctx, req)
	if err != nil {
		err = fmt.Errorf("ListObjects: %v", err)
		return
	}

	nonEmpty = len(listing.Objects) != 0
	return
}

// Stat the object with the given name, returning (nil, nil) if the object
// doesn't exist rather than failing.
func statObjectMayNotExist(
	ctx context.Context,
	bucket gcs.Bucket,
	name string) (o *gcs.Object, err error) {
	// Call the bucket.
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	o, err = bucket.StatObject(ctx, req)

	// Suppress "not found" errors.
	if _, ok := err.(*gcs.NotFoundError); ok {
		err = nil
	}

	// Annotate others.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (d *DirInode) Lock() {
	d.mu.Lock()
}

func (d *DirInode) Unlock() {
	d.mu.Unlock()
}

func (d *DirInode) ID() fuseops.InodeID {
	return d.id
}

// Return the full name of the directory object in GCS, including the trailing
// slash (e.g. "foo/bar/").
func (d *DirInode) Name() string {
	return d.src.Name
}

func (d *DirInode) SourceGeneration() int64 {
	return d.src.Generation
}

// LOCKS_REQUIRED(d.mu)
func (d *DirInode) IncrementLookupCount() {
	d.lc.Inc()
}

// LOCKS_REQUIRED(d.mu)
func (d *DirInode) DecrementLookupCount(n uint64) (destroyed bool) {
	destroyed = d.lc.Dec(n)
	return
}

func (d *DirInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	// Find out whether the backing object has been clobbered in GCS.
	clobbered, err := d.clobbered(ctx)
	if err != nil {
		err = fmt.Errorf("clobbered: %v", err)
		return
	}

	// Set up basic attributes.
	attrs = fuseops.InodeAttributes{
		Mode: 0700 | os.ModeDir,
	}

	// Modify Nlink as appropriate.
	if !clobbered {
		attrs.Nlink = 1
	}

	return
}

// See notes on DirInode.LookUpChild.
const ImplicitDirGen int64 = -1

// A suffix that can be used to unambiguously tag a file system name.
// (Unambiguous because U+000A is not allowed in GCS object names.) This is
// used to refer to the file in a (file, directory) pair with conflicting
// object names.
//
// See also the notes on DirInode.LookUpChild.
const ConflictingFileNameSuffix = "\n"

// Look up the direct child with the given relative name, returning a record
// for the current object of that name in the GCS bucket. If both a file and a
// directory with the given name exist, the directory is preferred. Return a
// nil record with a nil error if neither is found.
//
// Special case: if the name ends in ConflictingFileNameSuffix, we strip the
// suffix, confirm that a conflicting directory exists, then return a record
// for the file.
//
// If this inode was created with implicitDirs is set, this method will use
// ListObjects to find child directories that are "implicitly" defined by the
// existence of their own descendents. For example, if there is an object named
// "foo/bar/baz" and this is the directory "foo", a child directory named "bar"
// will be implied. In this case, the child's generation will be the sentinel
// value ImplicitDirGen.
func (d *DirInode) LookUpChild(
	ctx context.Context,
	name string) (o *gcs.Object, err error) {
	b := syncutil.NewBundle(ctx)

	// Is this a conflict marker name?
	if strings.HasSuffix(name, ConflictingFileNameSuffix) {
		o, err = d.lookUpConflicting(ctx, name)
		return
	}

	// Stat the child as a file.
	var fileRecord *gcs.Object
	b.Add(func(ctx context.Context) (err error) {
		fileRecord, err = d.lookUpChildFile(ctx, name)
		return
	})

	// Stat the child as a directory.
	var dirRecord *gcs.Object
	b.Add(func(ctx context.Context) (err error) {
		dirRecord, err = d.lookUpChildDir(ctx, name)
		return
	})

	// Wait for both.
	err = b.Join()
	if err != nil {
		return
	}

	// Prefer directories over files.
	switch {
	case dirRecord != nil:
		o = dirRecord
	case fileRecord != nil:
		o = fileRecord
	}

	return
}

// Read some number of entries from the directory, returning a continuation
// token that can be used to pick up the read operation where it left off.
// Supply the empty token on the first call.
//
// At the end of the directory, the returned continuation token will be empty.
// Otherwise it will be non-empty. There is no guarantee about the number of
// entries returned; it may be zero even with a non-empty continuation token.
//
// The contents of the Offset and Inode fields for returned entries is
// undefined.
//
// Warning: This method always behaves as if implicit directories are enabled,
// regardless of how the inode was configured. If you want to ensure that
// directories actually exist it non-implicit mode, you must call LookUpChild
// to do so.
func (d *DirInode) ReadEntries(
	ctx context.Context,
	tok string) (entries []fuseutil.Dirent, newTok string, err error) {
	// Ask the bucket to list some objects.
	req := &gcs.ListObjectsRequest{
		Delimiter:         "/",
		Prefix:            d.Name(),
		ContinuationToken: tok,
	}

	listing, err := d.bucket.ListObjects(ctx, req)
	if err != nil {
		err = fmt.Errorf("ListObjects: %v", err)
		return
	}

	// Convert objects to entries for files.
	for _, o := range listing.Objects {
		// Skip the entry for the backing object itself, which of course has its
		// own name as a prefix but which we don't wan tto appear to contain
		// itself.
		if o.Name == d.Name() {
			continue
		}

		e := fuseutil.Dirent{
			Name: path.Base(o.Name),
			Type: fuseutil.DT_File,
		}

		entries = append(entries, e)
	}

	// Convert runs to entries for directories.
	for _, p := range listing.CollapsedRuns {
		e := fuseutil.Dirent{
			Name: path.Base(p),
			Type: fuseutil.DT_Directory,
		}

		entries = append(entries, e)
	}

	// Return an appropriate continuation token, if any.
	newTok = listing.ContinuationToken

	return
}
<|endoftext|>


<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 inode

import (
	"fmt"
	"os"
	"path"

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

type DirInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id fuse.InodeID

	// The name of the GCS object backing the inode, used as a prefix when
	// listing. Special case: the empty string means this is the root inode.
	//
	// INVARIANT: name == "" || name[len(name)-1] == '/'
	name string

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

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	Mu syncutil.InvariantMutex
}

var _ Inode = &DirInode{}

// Create a directory inode for the directory with the given name. The name
// must end with a slash unless this is the root directory, in which case it
// must be empty.
//
// REQUIRES: name == "" || name[len(name)-1] == '/'
func NewDirInode(
	bucket gcs.Bucket,
	id fuse.InodeID,
	name string) (d *DirInode) {
	// Set up the basic struct.
	d = &DirInode{
		bucket: bucket,
		id:     id,
		name:   name,
	}

	// Set up invariant checking.
	d.Mu = syncutil.NewInvariantMutex(d.checkInvariants)

	return
}

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

func (d *DirInode) checkInvariants() {
	// Check the name.
	if !(d.name == "" || d.name[len(d.name)-1] == '/') {
		panic(fmt.Sprintf("Unexpected name: %s", d.name))
	}
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Return the ID of this inode.
func (d *DirInode) ID() fuse.InodeID {
	return d.id
}

// Return the full name of the directory object in GCS, including the trailing
// slash (e.g. "foo/bar/").
func (d *DirInode) Name() string {
	return d.name
}

func (d *DirInode) Attributes(
	ctx context.Context) (attrs fuse.InodeAttributes, err error) {
	attrs = fuse.InodeAttributes{
		Mode: 0700 | os.ModeDir,
		// TODO(jacobsa): Track mtime and maybe atime.
	}

	return
}

// Look up the direct child with the given relative name, returning a record
// for the current object of that name in the GCS bucket. If both a file and a
// directory with the given name exist, be consistent from call to call about
// which is preferred. Return fuse.ENOENT if neither is found.
func (d *DirInode) LookUpChild(
	ctx context.Context,
	name string) (o *storage.Object, err error) {
	// See if the child is a directory first.
	statReq := &gcs.StatObjectRequest{
		Name: d.name + name + "/",
	}

	o, err = d.bucket.StatObject(ctx, statReq)
	if err != nil && err != gcs.ErrNotFound {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// Try again as a file.
	statReq.Name = d.name + name
	o, err = d.bucket.StatObject(ctx, statReq)

	if err == gcs.ErrNotFound {
		err = fuse.ENOENT
		return
	}

	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	return
}

// Read some number of entries from the directory, returning a continuation
// token that can be used to pick up the read operation where it left off.
// Supply the empty token on the first call.
//
// At the end of the directory, the returned continuation token will be empty.
// Otherwise it will be non-empty. There is no guarantee about the number of
// entries returned; it may be zero even with a non-empty continuation token.
//
// The contents of the Offset and Inode fields for returned entries is
// undefined.
func (d *DirInode) ReadEntries(
	ctx context.Context,
	tok string) (entries []fuseutil.Dirent, newTok string, err error) {
	// Ask the bucket to list some objects.
	query := &storage.Query{
		Delimiter: "/",
		Prefix:    d.name,
		Cursor:    tok,
	}

	listing, err := d.bucket.ListObjects(ctx, query)
	if err != nil {
		err = fmt.Errorf("ListObjects: %v", err)
		return
	}

	// Convert objects to entries for files.
	for _, o := range listing.Results {
		e := fuseutil.Dirent{
			Name: path.Base(o.Name),
			Type: fuseutil.DT_File,
		}

		entries = append(entries, e)
	}

	// Convert prefixes to entries for directories.
	for _, p := range listing.Prefixes {
		e := fuseutil.Dirent{
			Name: path.Base(p),
			Type: fuseutil.DT_Directory,
		}

		entries = append(entries, e)
	}

	// Return an appropriate continuation token, if any.
	if listing.Next != nil {
		newTok = listing.Next.Cursor
	}

	return
}
<commit_msg>Fixed the logic in DirInode.LookUpChild.<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 inode

import (
	"fmt"
	"os"
	"path"

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

type DirInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id fuse.InodeID

	// The name of the GCS object backing the inode, used as a prefix when
	// listing. Special case: the empty string means this is the root inode.
	//
	// INVARIANT: name == "" || name[len(name)-1] == '/'
	name string

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

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	Mu syncutil.InvariantMutex
}

var _ Inode = &DirInode{}

// Create a directory inode for the directory with the given name. The name
// must end with a slash unless this is the root directory, in which case it
// must be empty.
//
// REQUIRES: name == "" || name[len(name)-1] == '/'
func NewDirInode(
	bucket gcs.Bucket,
	id fuse.InodeID,
	name string) (d *DirInode) {
	// Set up the basic struct.
	d = &DirInode{
		bucket: bucket,
		id:     id,
		name:   name,
	}

	// Set up invariant checking.
	d.Mu = syncutil.NewInvariantMutex(d.checkInvariants)

	return
}

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

func (d *DirInode) checkInvariants() {
	// Check the name.
	if !(d.name == "" || d.name[len(d.name)-1] == '/') {
		panic(fmt.Sprintf("Unexpected name: %s", d.name))
	}
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Return the ID of this inode.
func (d *DirInode) ID() fuse.InodeID {
	return d.id
}

// Return the full name of the directory object in GCS, including the trailing
// slash (e.g. "foo/bar/").
func (d *DirInode) Name() string {
	return d.name
}

func (d *DirInode) Attributes(
	ctx context.Context) (attrs fuse.InodeAttributes, err error) {
	attrs = fuse.InodeAttributes{
		Mode: 0700 | os.ModeDir,
		// TODO(jacobsa): Track mtime and maybe atime.
	}

	return
}

// Look up the direct child with the given relative name, returning a record
// for the current object of that name in the GCS bucket. If both a file and a
// directory with the given name exist, be consistent from call to call about
// which is preferred. Return fuse.ENOENT if neither is found.
func (d *DirInode) LookUpChild(
	ctx context.Context,
	name string) (o *storage.Object, err error) {
	// Stat the child as a directory first.
	statReq := &gcs.StatObjectRequest{
		Name: d.name + name + "/",
	}

	o, err = d.bucket.StatObject(ctx, statReq)

	// Did we find it successfully?
	if err == nil {
		return
	}

	// Propagate all errors except "not found".
	if err != nil && err != gcs.ErrNotFound {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// Try again as a file.
	statReq.Name = d.name + name
	o, err = d.bucket.StatObject(ctx, statReq)

	if err == gcs.ErrNotFound {
		err = fuse.ENOENT
		return
	}

	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	return
}

// Read some number of entries from the directory, returning a continuation
// token that can be used to pick up the read operation where it left off.
// Supply the empty token on the first call.
//
// At the end of the directory, the returned continuation token will be empty.
// Otherwise it will be non-empty. There is no guarantee about the number of
// entries returned; it may be zero even with a non-empty continuation token.
//
// The contents of the Offset and Inode fields for returned entries is
// undefined.
func (d *DirInode) ReadEntries(
	ctx context.Context,
	tok string) (entries []fuseutil.Dirent, newTok string, err error) {
	// Ask the bucket to list some objects.
	query := &storage.Query{
		Delimiter: "/",
		Prefix:    d.name,
		Cursor:    tok,
	}

	listing, err := d.bucket.ListObjects(ctx, query)
	if err != nil {
		err = fmt.Errorf("ListObjects: %v", err)
		return
	}

	// Convert objects to entries for files.
	for _, o := range listing.Results {
		e := fuseutil.Dirent{
			Name: path.Base(o.Name),
			Type: fuseutil.DT_File,
		}

		entries = append(entries, e)
	}

	// Convert prefixes to entries for directories.
	for _, p := range listing.Prefixes {
		e := fuseutil.Dirent{
			Name: path.Base(p),
			Type: fuseutil.DT_Directory,
		}

		entries = append(entries, e)
	}

	// Return an appropriate continuation token, if any.
	if listing.Next != nil {
		newTok = listing.Next.Cursor
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Lukas Weber. All rights reserved.
// Use of this source code is governed by the MIT-styled
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"strings"

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

// A Buffer is a screen of the ui. Buffers are opened on a stack.
type Buffer interface {
	// Draw content on the screen
	Draw()
	// Title displayed in the bottom bar. It has to identify buffer uniquely.
	Title() string
	// Name of the buffer
	Name() string
	// Close the Buffer
	Close()

	// HandleCommand handles string commands. If it returns bool, the
	// command was accepted. If not, it was invalid.
	HandleCommand(cmd string, args []string, stack *BufferStack) bool
}

// BufferStack is the Stack structure managing drawing of the screen and
// buffers.
type BufferStack struct {
	buffers []Buffer

	prompt Prompt
}

func invalidCommand(cmd string) {
	StatusLine = "invalid command: " + cmd
}

// Init initializes the BufferStack and executes the initial command set in Config.
func (b *BufferStack) Init() {
	fields := strings.Fields(config.General.Initial_Command)
	if len(fields) > 0 {
		accept := b.handleCommand(fields[0], fields[1:])
		if !accept {
			invalidCommand(fields[0])
		}
		if len(b.buffers) == 0 {
			b.Push(NewSearchBuffer("", STMessages))
		}
	}
}

// Push pushes a new buffer on the stack. Focus is changed to that buffer.
func (b *BufferStack) Push(n Buffer) {
	for i, buf := range b.buffers {
		// If the buffer already exists, change to it instead.
		if buf.Name() == n.Name() && buf.Title() == n.Title() {
			n.Close()
			copy(b.buffers[i:], b.buffers[i+1:])
			b.buffers[len(b.buffers)-1] = buf
			b.refresh()
			return
		}
	}
	b.buffers = append(b.buffers, n)
	b.refresh()
}

// StatusLine is displayed at the bottom of the screen. useful for error messages.
var StatusLine string

// Pop pops the last buffer from the stack.
func (b *BufferStack) Pop() {
	if len(b.buffers) == 0 {
		return
	}
	b.buffers[len(b.buffers)-1].Close()
	b.buffers = b.buffers[:len(b.buffers)-1]
	b.refresh()
}

// refresh clears the terminal and redraws everything.
func (b *BufferStack) refresh() {
	termbox.Clear(0, 0)
	if len(b.buffers) == 0 {
		return
	}
	b.buffers[len(b.buffers)-1].Draw()
	w, h := termbox.Size()
	cbuf := termbox.CellBuffer()
	for i := 0; i < w; i++ {
		cbuf[(h-2)*w+i].Bg = termbox.Attribute(config.Theme.BottomBar)
		cbuf[(h-2)*w+i].Fg = termbox.AttrBold
	}
	title := b.buffers[len(b.buffers)-1].Title()
	name := b.buffers[len(b.buffers)-1].Name()
	printLine(0, h-2, fmt.Sprintf("[%d: %s] %s", len(b.buffers)-1, name, title), -1, -1)
	if b.prompt.Active() {
		b.prompt.Draw()
	} else if StatusLine != "" {
		_, h := termbox.Size()
		printLine(0, h-1, StatusLine, -1, -1)
	}
	termbox.Flush()
}

// handleCommand executes global commands.
func (b *BufferStack) handleCommand(cmd string, args []string) bool {
	switch cmd {
	case "close":
		b.Pop()
	case "quit":
		for len(b.buffers) > 0 {
			b.Pop()
		}
	case "search":
		b.Push(NewSearchBuffer(strings.Join(args, " "), STThreads))
	case "msearch":
		b.Push(NewSearchBuffer(strings.Join(args, " "), STMessages))
	case "compose":
		b.Push(NewComposeBuffer(composeMail()))
	case "help":
		b.Push(&HelpBuffer{b.buffers[len(b.buffers)-1].Name()})
	case "prompt":
		StatusLine = ""
		b.prompt.Activate(strings.Join(args, " "))
	case "refresh":
		for _, buf := range b.buffers {
			buf.HandleCommand("refresh", nil, b)
		}
		b.refresh()
	default:
		return false
	}
	return true
}

// HandleEvent handles termbox events and invokes commands if their keybinding was pressed.
func (b *BufferStack) HandleEvent(event *termbox.Event) {
	if len(b.buffers) == 0 {
		return
	}
	if event.Type == termbox.EventResize {
		termbox.Flush()
		for _, buf := range b.buffers {
			buf.HandleCommand("resize", nil, b)
		}
		b.refresh()
		return
	}

	if event.Type == termbox.EventKey {
		if b.prompt.Active() {
			cmd, args := b.prompt.HandleEvent(event)
			if len(cmd) != 0 {
				accept := b.buffers[len(b.buffers)-1].HandleCommand(cmd, args, b)
				if !accept {
					accept = b.handleCommand(cmd, args)
				}
				if !accept {
					invalidCommand(cmd)
				}
				_, h := termbox.Size()
				printLine(0, h-1, StatusLine, -1, -1)
			}
			return
		}
		cmd := getBinding("", event.Ch, event.Key)
		accept := false
		if cmd == nil {
			cmd := getBinding(b.buffers[len(b.buffers)-1].Name(), event.Ch, event.Key)
			if cmd == nil {
				return
			}
			accept = b.buffers[len(b.buffers)-1].HandleCommand(cmd.Command, cmd.Args, b)
		} else {
			accept = b.handleCommand(cmd.Command, cmd.Args)
		}
		if !accept {
			invalidCommand(cmd.Command)
		}

		if StatusLine != "" {
			_, h := termbox.Size()
			printLine(0, h-1, StatusLine, -1, -1)
		}
	}

}
<commit_msg>fix crash on invalid commands<commit_after>// Copyright 2015 Lukas Weber. All rights reserved.
// Use of this source code is governed by the MIT-styled
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"strings"

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

// A Buffer is a screen of the ui. Buffers are opened on a stack.
type Buffer interface {
	// Draw content on the screen
	Draw()
	// Title displayed in the bottom bar. It has to identify buffer uniquely.
	Title() string
	// Name of the buffer
	Name() string
	// Close the Buffer
	Close()

	// HandleCommand handles string commands. If it returns bool, the
	// command was accepted. If not, it was invalid.
	HandleCommand(cmd string, args []string, stack *BufferStack) bool
}

// BufferStack is the Stack structure managing drawing of the screen and
// buffers.
type BufferStack struct {
	buffers []Buffer

	prompt Prompt
}

func invalidCommand(cmd string) {
	StatusLine = "invalid command: " + cmd
}

// Init initializes the BufferStack and executes the initial command set in Config.
func (b *BufferStack) Init() {
	fields := strings.Fields(config.General.Initial_Command)
	if len(fields) > 0 {
		accept := b.handleCommand(fields[0], fields[1:])
		if !accept {
			invalidCommand(fields[0])
		}
		if len(b.buffers) == 0 {
			b.Push(NewSearchBuffer("", STMessages))
		}
	}
}

// Push pushes a new buffer on the stack. Focus is changed to that buffer.
func (b *BufferStack) Push(n Buffer) {
	for i, buf := range b.buffers {
		// If the buffer already exists, change to it instead.
		if buf.Name() == n.Name() && buf.Title() == n.Title() {
			n.Close()
			copy(b.buffers[i:], b.buffers[i+1:])
			b.buffers[len(b.buffers)-1] = buf
			b.refresh()
			return
		}
	}
	b.buffers = append(b.buffers, n)
	b.refresh()
}

// StatusLine is displayed at the bottom of the screen. useful for error messages.
var StatusLine string

// Pop pops the last buffer from the stack.
func (b *BufferStack) Pop() {
	if len(b.buffers) == 0 {
		return
	}
	b.buffers[len(b.buffers)-1].Close()
	b.buffers = b.buffers[:len(b.buffers)-1]
	b.refresh()
}

// refresh clears the terminal and redraws everything.
func (b *BufferStack) refresh() {
	termbox.Clear(0, 0)
	if len(b.buffers) == 0 {
		return
	}
	b.buffers[len(b.buffers)-1].Draw()
	w, h := termbox.Size()
	cbuf := termbox.CellBuffer()
	for i := 0; i < w; i++ {
		cbuf[(h-2)*w+i].Bg = termbox.Attribute(config.Theme.BottomBar)
		cbuf[(h-2)*w+i].Fg = termbox.AttrBold
	}
	title := b.buffers[len(b.buffers)-1].Title()
	name := b.buffers[len(b.buffers)-1].Name()
	printLine(0, h-2, fmt.Sprintf("[%d: %s] %s", len(b.buffers)-1, name, title), -1, -1)
	if b.prompt.Active() {
		b.prompt.Draw()
	} else if StatusLine != "" {
		_, h := termbox.Size()
		printLine(0, h-1, StatusLine, -1, -1)
	}
	termbox.Flush()
}

// handleCommand executes global commands.
func (b *BufferStack) handleCommand(cmd string, args []string) bool {
	switch cmd {
	case "close":
		b.Pop()
	case "quit":
		for len(b.buffers) > 0 {
			b.Pop()
		}
	case "search":
		b.Push(NewSearchBuffer(strings.Join(args, " "), STThreads))
	case "msearch":
		b.Push(NewSearchBuffer(strings.Join(args, " "), STMessages))
	case "compose":
		b.Push(NewComposeBuffer(composeMail()))
	case "help":
		b.Push(&HelpBuffer{b.buffers[len(b.buffers)-1].Name()})
	case "prompt":
		StatusLine = ""
		b.prompt.Activate(strings.Join(args, " "))
	case "refresh":
		for _, buf := range b.buffers {
			buf.HandleCommand("refresh", nil, b)
		}
		b.refresh()
	default:
		return false
	}
	return true
}

// HandleEvent handles termbox events and invokes commands if their keybinding was pressed.
func (b *BufferStack) HandleEvent(event *termbox.Event) {
	if len(b.buffers) == 0 {
		return
	}
	if event.Type == termbox.EventResize {
		termbox.Flush()
		for _, buf := range b.buffers {
			buf.HandleCommand("resize", nil, b)
		}
		b.refresh()
		return
	}

	if event.Type == termbox.EventKey {
		if b.prompt.Active() {
			cmd, args := b.prompt.HandleEvent(event)
			if len(cmd) != 0 {
				accept := b.buffers[len(b.buffers)-1].HandleCommand(cmd, args, b)
				if !accept {
					accept = b.handleCommand(cmd, args)
				}
				if !accept {
					invalidCommand(cmd)
				}
				_, h := termbox.Size()
				printLine(0, h-1, StatusLine, -1, -1)
			}
			return
		}
		cmd := getBinding("", event.Ch, event.Key)
		accept := false
		if cmd == nil {
			cmd = getBinding(b.buffers[len(b.buffers)-1].Name(), event.Ch, event.Key)
			if cmd == nil {
				return
			}
		}
		accept = b.buffers[len(b.buffers)-1].HandleCommand(cmd.Command, cmd.Args, b)
		if !accept {
			accept = b.handleCommand(cmd.Command, cmd.Args)
		}
		if !accept {
			invalidCommand(cmd.Command)
		}

		if StatusLine != "" {
			_, h := termbox.Size()
			printLine(0, h-1, StatusLine, -1, -1)
		}
	}

}
<|endoftext|>


<commit_before>// Package hashchain implements the hash chain for the key server in Mute.
package hashchain

import (
	"github.com/mutecomm/mute/encode/base64"
	"github.com/mutecomm/mute/log"
)

// Type denotes the current hash chain type.
var Type = []byte{0x01}

// SplitEntry splits a base64 encoded key hashchain entry. Specification:
// https://github.com/mutecomm/mute/blob/master/doc/keyserver.md#key-hashchain-operation
func SplitEntry(entry string) (hash, typ, nonce, hashID, crUID, uidIndex []byte, err error) {
	e, err := base64.Decode(entry)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, err
	}
	if len(e) != 153 {
		return nil, nil, nil, nil, nil, nil, log.Errorf("entry '%s' does not have byte length 153 (but %d)", entry, len(e))
	}
	return e[:32], e[32:33], e[33:41], e[41:73], e[73:121], e[121:], nil
}
<commit_msg>hashchain: check type<commit_after>// Package hashchain implements the hash chain for the key server in Mute.
package hashchain

import (
	"bytes"

	"github.com/mutecomm/mute/encode/base64"
	"github.com/mutecomm/mute/log"
)

// Type denotes the current hash chain type.
var Type = []byte{0x01}

// SplitEntry splits a base64 encoded key hashchain entry. Specification:
// https://github.com/mutecomm/mute/blob/master/doc/keyserver.md#key-hashchain-operation
func SplitEntry(entry string) (hash, typ, nonce, hashID, crUID, uidIndex []byte, err error) {
	e, err := base64.Decode(entry)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, err
	}
	if len(e) != 153 {
		return nil, nil, nil, nil, nil, nil,
			log.Errorf("hashchain: entry '%s' does not have byte length 153 (but %d)", entry, len(e))
	}
	// HASH(entry[n]) | TYPE | NONCE | HashID | CrUID | UIDIndex
	hash = e[:32]
	typ = e[32:33]
	nonce = e[33:41]
	hashID = e[41:73]
	crUID = e[73:121]
	uidIndex = e[121:]
	// check type
	if !bytes.Equal(typ, Type) {
		return nil, nil, nil, nil, nil, nil,
			log.Errorf("hashchain: wrong type 0x%x (should be 0x%x)", typ, Type)
	}
	return
}
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2019] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package v1

import (
	"context"
	"fmt"

	"github.com/lastbackend/lastbackend/pkg/api/client/types"
	rv1 "github.com/lastbackend/lastbackend/pkg/api/types/v1/request"
	vv1 "github.com/lastbackend/lastbackend/pkg/api/types/v1/views"
	"github.com/lastbackend/lastbackend/pkg/distribution/errors"
	t "github.com/lastbackend/lastbackend/pkg/distribution/types"
	"github.com/lastbackend/lastbackend/pkg/util/http/request"
)

type DeploymentClient struct {
	client *request.RESTClient

	namespace t.NamespaceSelfLink
	service   t.ServiceSelfLink
	selflink  t.DeploymentSelfLink
}

func (dc *DeploymentClient) Pod(args ...string) types.PodClientV1 {
	name := ""
	// Get any parameters passed to us out of the args variable into "real"
	// variables we created for them.
	for i := range args {
		switch i {
		case 0: // hostname
			name = args[0]
		default:
			panic("Wrong parameter count: (is allowed from 0 to 1)")
		}
	}
	return newPodClient(dc.client, dc.namespace.String(), t.KindDeployment, dc.selflink.String(), name)
}

func (dc *DeploymentClient) List(ctx context.Context) (*vv1.DeploymentList, error) {

	var s *vv1.DeploymentList
	var e *errors.Http

	err := dc.client.Get(fmt.Sprintf("/namespace/%s/service/%s/deploymet", dc.namespace.String(), dc.service.String())).
		AddHeader("Content-Type", "application/json").
		JSON(&s, &e)

	if err != nil {
		return nil, err
	}
	if e != nil {
		return nil, errors.New(e.Message)
	}

	if s == nil {
		list := make(vv1.DeploymentList, 0)
		s = &list
	}

	return s, nil
}

func (dc *DeploymentClient) Get(ctx context.Context) (*vv1.Deployment, error) {

	var s *vv1.Deployment
	var e *errors.Http

	err := dc.client.Get(fmt.Sprintf("/namespace/%s/service/%s/deploymet/%s", dc.namespace.String(), dc.service.String(), dc.selflink.Name())).
		AddHeader("Content-Type", "application/json").
		JSON(&s, &e)

	if err != nil {
		return nil, err
	}
	if e != nil {
		return nil, errors.New(e.Message)
	}

	return s, nil
}

func (dc *DeploymentClient) Update(ctx context.Context, opts *rv1.DeploymentUpdateOptions) (*vv1.Deployment, error) {

	body, err := opts.ToJson()
	if err != nil {
		return nil, err
	}

	var s *vv1.Deployment
	var e *errors.Http

	err = dc.client.Put(fmt.Sprintf("/namespace/%s/service/%s/deployment/%s", dc.namespace.String(), dc.service.String(), dc.selflink.Name())).
		AddHeader("Content-Type", "application/json").
		Body(body).
		JSON(&s, &e)

	if err != nil {
		return nil, err
	}
	if e != nil {
		return nil, errors.New(e.Message)
	}

	return s, nil
}

func newDeploymentClient(client *request.RESTClient, namespace, service, name string) *DeploymentClient {
	return &DeploymentClient{
		client:    client,
		namespace: *t.NewNamespaceSelfLink(namespace),
		service:   *t.NewServiceSelfLink(namespace, service),
		selflink:  *t.NewDeploymentSelfLink(namespace, service, name),
	}
}
<commit_msg>update deployment client path<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2019] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package v1

import (
	"context"
	"fmt"

	"github.com/lastbackend/lastbackend/pkg/api/client/types"
	rv1 "github.com/lastbackend/lastbackend/pkg/api/types/v1/request"
	vv1 "github.com/lastbackend/lastbackend/pkg/api/types/v1/views"
	"github.com/lastbackend/lastbackend/pkg/distribution/errors"
	t "github.com/lastbackend/lastbackend/pkg/distribution/types"
	"github.com/lastbackend/lastbackend/pkg/util/http/request"
)

type DeploymentClient struct {
	client *request.RESTClient

	namespace t.NamespaceSelfLink
	service   t.ServiceSelfLink
	selflink  t.DeploymentSelfLink
}

func (dc *DeploymentClient) Pod(args ...string) types.PodClientV1 {
	name := ""
	// Get any parameters passed to us out of the args variable into "real"
	// variables we created for them.
	for i := range args {
		switch i {
		case 0: // hostname
			name = args[0]
		default:
			panic("Wrong parameter count: (is allowed from 0 to 1)")
		}
	}
	return newPodClient(dc.client, dc.namespace.String(), t.KindDeployment, dc.selflink.String(), name)
}

func (dc *DeploymentClient) List(ctx context.Context) (*vv1.DeploymentList, error) {

	var s *vv1.DeploymentList
	var e *errors.Http

	err := dc.client.Get(fmt.Sprintf("/namespace/%s/service/%s/deployment", dc.namespace.String(), dc.service.String())).
		AddHeader("Content-Type", "application/json").
		JSON(&s, &e)

	if err != nil {
		return nil, err
	}
	if e != nil {
		return nil, errors.New(e.Message)
	}

	if s == nil {
		list := make(vv1.DeploymentList, 0)
		s = &list
	}

	return s, nil
}

func (dc *DeploymentClient) Get(ctx context.Context) (*vv1.Deployment, error) {

	var s *vv1.Deployment
	var e *errors.Http

	err := dc.client.Get(fmt.Sprintf("/namespace/%s/service/%s/deployment/%s", dc.namespace.String(), dc.service.String(), dc.selflink.Name())).
		AddHeader("Content-Type", "application/json").
		JSON(&s, &e)

	if err != nil {
		return nil, err
	}
	if e != nil {
		return nil, errors.New(e.Message)
	}

	return s, nil
}

func (dc *DeploymentClient) Update(ctx context.Context, opts *rv1.DeploymentUpdateOptions) (*vv1.Deployment, error) {

	body, err := opts.ToJson()
	if err != nil {
		return nil, err
	}

	var s *vv1.Deployment
	var e *errors.Http

	err = dc.client.Put(fmt.Sprintf("/namespace/%s/service/%s/deployment/%s", dc.namespace.String(), dc.service.String(), dc.selflink.Name())).
		AddHeader("Content-Type", "application/json").
		Body(body).
		JSON(&s, &e)

	if err != nil {
		return nil, err
	}
	if e != nil {
		return nil, errors.New(e.Message)
	}

	return s, nil
}

func newDeploymentClient(client *request.RESTClient, namespace, service, name string) *DeploymentClient {
	return &DeploymentClient{
		client:    client,
		namespace: *t.NewNamespaceSelfLink(namespace),
		service:   *t.NewServiceSelfLink(namespace, service),
		selflink:  *t.NewDeploymentSelfLink(namespace, service, name),
	}
}
<|endoftext|>


<commit_before>package ccd

import (
  "fmt"
  "github.com/jteeuwen/go-pkg-xmlx"
  "io"
  "strings"
  "time"
)

type Problem struct {
}

type CCD struct {
  Patient  Patient
  Meds     []Med
  Problems []Problem
}

// Node get.
// helper function to continually transverse down the
// xml nodes in args, and return the last one.
func nget(node *xmlx.Node, args ...string) *xmlx.Node {
  for _, a := range args {
    if node == nil {
      return nil
    }

    node = node.SelectNode("*", a)
  }

  return node
}

// Node Safe get.
// just like nget, but returns a node no matter what.
func nsget(node *xmlx.Node, args ...string) *xmlx.Node {
  n := nget(node, args...)
  if n == nil {
    return xmlx.NewNode(0)
  }
  return n
}

func UnmarshalFile(filename string) (*CCD, error) {
  doc := xmlx.New()
  err := doc.LoadFile(filename, nil)
  if err != nil {
    return nil, err
  }

  return UnmarshalDoc(doc)
}

func UnmarshalStream(r io.Reader) (*CCD, error) {
  doc := xmlx.New()
  err := doc.LoadStream(r, nil)
  if err != nil {
    return nil, err
  }

  return UnmarshalDoc(doc)
}

func Unmarshal(data []byte) (*CCD, error) {
  doc := xmlx.New()
  err := doc.LoadBytes(data, nil)
  if err != nil {
    return nil, err
  }

  return UnmarshalDoc(doc)
}

// Unmarshals a CCD into a CCD struct.
func UnmarshalDoc(doc *xmlx.Document) (*CCD, error) {
  var errs_ []error
  // var errs []error

  ccd := &CCD{}
  ccd.Patient, errs_ = parsePatient(doc.Root)
  //errs = append(errs, errs_...)

  componentNode := nget(doc.Root, "component", "structuredBody")
  if componentNode != nil {
    componentNodes := componentNode.SelectNodes("*", "component")
    for _, componentNode := range componentNodes {
      sectionNode := componentNode.SelectNode("*", "section")
      switch templateId(sectionNode) {
      case "2.16.840.1.113883.10.20.1.8":
        ccd.Meds, errs_ = parseMeds(sectionNode)
      case "2.16.840.1.113883.10.20.1.11":
        ccd.Problems, errs_ = parseProblems(sectionNode)
      }
      //errs = append(errs, errs_...)
    }
  }

  // disabling errors for now. may return "warnings" or something
  _ = errs_

  return ccd, nil
}

type Name struct {
  Last     string
  First    string
  Middle   string
  Suffix   string
  Prefix   string // title
  Type     string // L = legal name, PN = patient name (not sure)
  NickName string
}

func (n Name) IsZero() bool {
  return n == (Name{})
}

type Address struct {
  Line1   string
  Line2   string
  City    string
  County  string
  State   string
  Zip     string
  Country string
  Type    string // H or HP = home, TMP = temporary, WP = work/office
}

func (a Address) IsZero() bool {
  return a == (Address{})
}

type Patient struct {
  Name          Name
  Dob           time.Time
  Address       Address
  Gender        string
  MaritalStatus string
  Race          string
  Ethnicity     string
}

func (p Patient) IsZero() bool {
  return p == (Patient{})
}

// parses patient information from the CCD and returns
// a Patient struct
func parsePatient(root *xmlx.Node) (Patient, []error) {
  patient := Patient{}

  anode := nget(root, "ClinicalDocument", "recordTarget", "patientRole", "addr")
  // address isn't always present
  if anode != nil {
    patient.Address.Type = anode.As("*", "use")
    lines := anode.SelectNodes("*", "streetAddressLine")
    if len(lines) > 0 {
      patient.Address.Line1 = lines[0].Value
    }
    if len(lines) > 1 {
      patient.Address.Line2 = lines[1].Value
    }
    patient.Address.City = anode.S("*", "city")
    patient.Address.County = anode.S("*", "county")
    patient.Address.State = anode.S("*", "state")
    patient.Address.Zip = anode.S("*", "postalCode")
    patient.Address.Country = anode.S("*", "country")
  }

  pnode := nget(root, "ClinicalDocument", "recordTarget", "patientRole", "patient")
  if pnode == nil {
    return patient, []error{
      fmt.Errorf("Could not find the node in CCD: ClinicalDocument/recordTarget/patientRole/patient"),
    }
  }

  for n, nameNode := range pnode.SelectNodes("*", "name") {
    given := nameNode.SelectNodes("*", "given")
    // This is a NickName if it's the second <name><given> tag block or the
    // given tag has the qualifier CM.
    if n == 1 || (len(given) > 0 && given[0].As("*", "qualifier") == "CM") {
      patient.Name.NickName = given[0].Value
      continue
    }

    patient.Name.Type = nameNode.As("*", "use")
    if len(given) > 0 {
      patient.Name.First = given[0].Value
    }
    if len(given) > 1 {
      patient.Name.Middle = given[1].Value
    }
    patient.Name.Last = nameNode.S("*", "family")
    patient.Name.Prefix = nameNode.S("*", "prefix")
    suffixes := nameNode.SelectNodes("*", "suffix")
    for n, suffix := range suffixes {
      // if it's the second suffix, or it has the qualifier TITLE
      if n == 1 || (len(patient.Name.Prefix) == 0 && suffix.As("*", "qualifier") == "TITLE") {
        patient.Name.Prefix = suffix.Value
      } else {
        patient.Name.Suffix = suffix.Value
      }
    }
  }

  birthNode := pnode.SelectNode("*", "birthTime")
  if birthNode != nil {
    patient.Dob, _ = time.Parse("20060102", birthNode.As("*", "value"))
  }

  genderNode := pnode.SelectNode("*", "administrativeGenderCode")
  if genderNode != nil && genderNode.As("*", "codeSystem") == "2.16.840.1.113883.5.1" {
    switch genderNode.As("*", "code") {
    case "M":
      patient.Gender = "Male"
    case "F":
      patient.Gender = "Female"
    case "UN":
      patient.Gender = "Undifferentiated"
    default:
      patient.Gender = "Unknown"
    }
  }

  maritalNode := pnode.SelectNode("*", "maritalStatusCode")
  if maritalNode != nil && maritalNode.As("*", "codeSystem") == "2.16.840.1.113883.5.2" {
    patient.MaritalStatus = maritalNode.As("*", "code")
  }

  raceNode := pnode.SelectNode("*", "raceCode")
  if raceNode != nil && raceNode.As("*", "codeSystem") == "2.16.840.1.113883.6.238" {
    patient.Race = raceNode.As("*", "code")
  }

  ethnicNode := pnode.SelectNode("*", "ethnicGroupCode")
  if ethnicNode != nil && ethnicNode.As("*", "codeSystem") == "2.16.840.1.113883.6.238" {
    patient.Ethnicity = ethnicNode.As("*", "code")
  }

  return patient, nil
}

type Med struct {
  Name      string
  Status    string
  StartDate time.Time
  StopDate  time.Time

  Id struct {
    Type  string
    Value string
  }
}

func parseMeds(node *xmlx.Node) ([]Med, []error) {
  var meds []Med
  var errs []error

  entryNodes := node.SelectNodes("*", "entry")
  for _, entryNode := range entryNodes {
    if templateId(entryNode) != "2.16.840.1.113883.10.20.1.24" {
      continue
    }

    mpNode := nget(entryNode, "substanceAdministration", "consumable", "manufacturedProduct")
    if mpNode == nil {
      continue
    }

    med := Med{}
    med.Status = nsget(entryNode, "substanceAdministration", "statusCode").As("*", "code")

    etimeNodes := nget(entryNode, "substanceAdministration").SelectNodes("*", "effectiveTime")
    for _, etimeNode := range etimeNodes {
      if strings.ToLower(etimeNode.As("*", "type")) == "ivl_ts" {
        lowNode := etimeNode.SelectNode("*", "low")
        if lowNode != nil {
          med.StartDate, _ = time.Parse("20060102", lowNode.As("*", "value"))
        }

        highNode := etimeNode.SelectNode("*", "high")
        if highNode != nil {
          med.StopDate, _ = time.Parse("20060102", highNode.As("*", "value"))
        }
      }
    }

    manNode := nget(mpNode, "manufacturedMaterial")

    codeNode := nget(manNode, "code")
    if codeNode != nil {
      codeSystem := codeNode.As("*", "codeSystem")
      var err error
      med.Id.Type, err = codeSystemToMedType(codeSystem)
      if err != nil {
        // Sometimes the attributes for "code" are completely missing.
        // try to see if there is a translation node and get it from there
        transNode := codeNode.SelectNode("*", "translation")
        if transNode != nil {
          codeSystem = transNode.As("*", "codeSystem")
          var err2 error
          med.Id.Type, err2 = codeSystemToMedType(codeSystem)
          if err2 != nil {
            errs = append(errs, err)
          }
        } else {
          errs = append(errs, err)
        }
      }
    }
    med.Id.Value = codeNode.As("*", "code")

    if displayName := codeNode.As("*", "displayName"); displayName != "" {
      med.Name = displayName
    } else if nameNode := manNode.SelectNode("*", "name"); nameNode != nil {
      med.Name = nameNode.Value
    } else if originalNode := codeNode.SelectNode("*", "originalText"); originalNode != nil {
      med.Name = originalNode.Value
    }

    meds = append(meds, med)
  }

  return meds, errs
}

func parseProblems(node *xmlx.Node) ([]Problem, []error) {
  return nil, nil
}

func templateId(node *xmlx.Node) string {
  idNodes := node.SelectNodes("*", "templateId")
  id := ""
  for _, idNode := range idNodes {
    id = idNode.As("*", "root")
    if strings.HasPrefix(id, "2.16.840.1.113883.10.20.1.") {
      return id
    }
  }

  return id
}

func codeSystemToMedType(codeSystem string) (string, error) {
  switch codeSystem {
  case "2.16.840.1.113883.6.69": // NDC
    return "NDC", nil
  case "2.16.840.1.113883.6.88": // RxNorm
    return "RxNorm", nil
  }
  return "", fmt.Errorf(`Unknown med codeSystem value of "%s"`, codeSystem)
}
<commit_msg>Added comment to Meds specification.<commit_after>package ccd

import (
  "fmt"
  "github.com/jteeuwen/go-pkg-xmlx"
  "io"
  "strings"
  "time"
)

type Problem struct {
}

type CCD struct {
  Patient  Patient
  Meds     []Med
  Problems []Problem
}

// Node get.
// helper function to continually transverse down the
// xml nodes in args, and return the last one.
func nget(node *xmlx.Node, args ...string) *xmlx.Node {
  for _, a := range args {
    if node == nil {
      return nil
    }

    node = node.SelectNode("*", a)
  }

  return node
}

// Node Safe get.
// just like nget, but returns a node no matter what.
func nsget(node *xmlx.Node, args ...string) *xmlx.Node {
  n := nget(node, args...)
  if n == nil {
    return xmlx.NewNode(0)
  }
  return n
}

func UnmarshalFile(filename string) (*CCD, error) {
  doc := xmlx.New()
  err := doc.LoadFile(filename, nil)
  if err != nil {
    return nil, err
  }

  return UnmarshalDoc(doc)
}

func UnmarshalStream(r io.Reader) (*CCD, error) {
  doc := xmlx.New()
  err := doc.LoadStream(r, nil)
  if err != nil {
    return nil, err
  }

  return UnmarshalDoc(doc)
}

func Unmarshal(data []byte) (*CCD, error) {
  doc := xmlx.New()
  err := doc.LoadBytes(data, nil)
  if err != nil {
    return nil, err
  }

  return UnmarshalDoc(doc)
}

// Unmarshals a CCD into a CCD struct.
func UnmarshalDoc(doc *xmlx.Document) (*CCD, error) {
  var errs_ []error
  // var errs []error

  ccd := &CCD{}
  ccd.Patient, errs_ = parsePatient(doc.Root)
  //errs = append(errs, errs_...)

  componentNode := nget(doc.Root, "component", "structuredBody")
  if componentNode != nil {
    componentNodes := componentNode.SelectNodes("*", "component")
    for _, componentNode := range componentNodes {
      sectionNode := componentNode.SelectNode("*", "section")
      switch templateId(sectionNode) {
      case "2.16.840.1.113883.10.20.1.8":
        ccd.Meds, errs_ = parseMeds(sectionNode)
      case "2.16.840.1.113883.10.20.1.11":
        ccd.Problems, errs_ = parseProblems(sectionNode)
      }
      //errs = append(errs, errs_...)
    }
  }

  // disabling errors for now. may return "warnings" or something
  _ = errs_

  return ccd, nil
}

type Name struct {
  Last     string
  First    string
  Middle   string
  Suffix   string
  Prefix   string // title
  Type     string // L = legal name, PN = patient name (not sure)
  NickName string
}

func (n Name) IsZero() bool {
  return n == (Name{})
}

type Address struct {
  Line1   string
  Line2   string
  City    string
  County  string
  State   string
  Zip     string
  Country string
  Type    string // H or HP = home, TMP = temporary, WP = work/office
}

func (a Address) IsZero() bool {
  return a == (Address{})
}

type Patient struct {
  Name          Name
  Dob           time.Time
  Address       Address
  Gender        string
  MaritalStatus string
  Race          string
  Ethnicity     string
}

func (p Patient) IsZero() bool {
  return p == (Patient{})
}

// parses patient information from the CCD and returns
// a Patient struct
func parsePatient(root *xmlx.Node) (Patient, []error) {
  patient := Patient{}

  anode := nget(root, "ClinicalDocument", "recordTarget", "patientRole", "addr")
  // address isn't always present
  if anode != nil {
    patient.Address.Type = anode.As("*", "use")
    lines := anode.SelectNodes("*", "streetAddressLine")
    if len(lines) > 0 {
      patient.Address.Line1 = lines[0].Value
    }
    if len(lines) > 1 {
      patient.Address.Line2 = lines[1].Value
    }
    patient.Address.City = anode.S("*", "city")
    patient.Address.County = anode.S("*", "county")
    patient.Address.State = anode.S("*", "state")
    patient.Address.Zip = anode.S("*", "postalCode")
    patient.Address.Country = anode.S("*", "country")
  }

  pnode := nget(root, "ClinicalDocument", "recordTarget", "patientRole", "patient")
  if pnode == nil {
    return patient, []error{
      fmt.Errorf("Could not find the node in CCD: ClinicalDocument/recordTarget/patientRole/patient"),
    }
  }

  for n, nameNode := range pnode.SelectNodes("*", "name") {
    given := nameNode.SelectNodes("*", "given")
    // This is a NickName if it's the second <name><given> tag block or the
    // given tag has the qualifier CM.
    if n == 1 || (len(given) > 0 && given[0].As("*", "qualifier") == "CM") {
      patient.Name.NickName = given[0].Value
      continue
    }

    patient.Name.Type = nameNode.As("*", "use")
    if len(given) > 0 {
      patient.Name.First = given[0].Value
    }
    if len(given) > 1 {
      patient.Name.Middle = given[1].Value
    }
    patient.Name.Last = nameNode.S("*", "family")
    patient.Name.Prefix = nameNode.S("*", "prefix")
    suffixes := nameNode.SelectNodes("*", "suffix")
    for n, suffix := range suffixes {
      // if it's the second suffix, or it has the qualifier TITLE
      if n == 1 || (len(patient.Name.Prefix) == 0 && suffix.As("*", "qualifier") == "TITLE") {
        patient.Name.Prefix = suffix.Value
      } else {
        patient.Name.Suffix = suffix.Value
      }
    }
  }

  birthNode := pnode.SelectNode("*", "birthTime")
  if birthNode != nil {
    patient.Dob, _ = time.Parse("20060102", birthNode.As("*", "value"))
  }

  genderNode := pnode.SelectNode("*", "administrativeGenderCode")
  if genderNode != nil && genderNode.As("*", "codeSystem") == "2.16.840.1.113883.5.1" {
    switch genderNode.As("*", "code") {
    case "M":
      patient.Gender = "Male"
    case "F":
      patient.Gender = "Female"
    case "UN":
      patient.Gender = "Undifferentiated"
    default:
      patient.Gender = "Unknown"
    }
  }

  maritalNode := pnode.SelectNode("*", "maritalStatusCode")
  if maritalNode != nil && maritalNode.As("*", "codeSystem") == "2.16.840.1.113883.5.2" {
    patient.MaritalStatus = maritalNode.As("*", "code")
  }

  raceNode := pnode.SelectNode("*", "raceCode")
  if raceNode != nil && raceNode.As("*", "codeSystem") == "2.16.840.1.113883.6.238" {
    patient.Race = raceNode.As("*", "code")
  }

  ethnicNode := pnode.SelectNode("*", "ethnicGroupCode")
  if ethnicNode != nil && ethnicNode.As("*", "codeSystem") == "2.16.840.1.113883.6.238" {
    patient.Ethnicity = ethnicNode.As("*", "code")
  }

  return patient, nil
}

type Med struct {
  Name      string
  Status    string
  StartDate time.Time
  StopDate  time.Time

  Id struct {
    Type  string
    Value string
  }
}

// http://wiki.ihe.net/index.php?title=1.3.6.1.4.1.19376.1.5.3.1.4.7
func parseMeds(node *xmlx.Node) ([]Med, []error) {
  var meds []Med
  var errs []error

  entryNodes := node.SelectNodes("*", "entry")
  for _, entryNode := range entryNodes {
    if templateId(entryNode) != "2.16.840.1.113883.10.20.1.24" {
      continue
    }

    mpNode := nget(entryNode, "substanceAdministration", "consumable", "manufacturedProduct")
    if mpNode == nil {
      continue
    }

    med := Med{}
    med.Status = nsget(entryNode, "substanceAdministration", "statusCode").As("*", "code")

    etimeNodes := nget(entryNode, "substanceAdministration").SelectNodes("*", "effectiveTime")
    for _, etimeNode := range etimeNodes {
      if strings.ToLower(etimeNode.As("*", "type")) == "ivl_ts" {
        lowNode := etimeNode.SelectNode("*", "low")
        if lowNode != nil {
          med.StartDate, _ = time.Parse("20060102", lowNode.As("*", "value"))
        }

        highNode := etimeNode.SelectNode("*", "high")
        if highNode != nil {
          med.StopDate, _ = time.Parse("20060102", highNode.As("*", "value"))
        }
      }
    }

    manNode := nget(mpNode, "manufacturedMaterial")

    codeNode := nget(manNode, "code")
    if codeNode != nil {
      codeSystem := codeNode.As("*", "codeSystem")
      var err error
      med.Id.Type, err = codeSystemToMedType(codeSystem)
      if err != nil {
        // Sometimes the attributes for "code" are completely missing.
        // try to see if there is a translation node and get it from there
        transNode := codeNode.SelectNode("*", "translation")
        if transNode != nil {
          codeSystem = transNode.As("*", "codeSystem")
          var err2 error
          med.Id.Type, err2 = codeSystemToMedType(codeSystem)
          if err2 != nil {
            errs = append(errs, err)
          }
        } else {
          errs = append(errs, err)
        }
      }
    }
    med.Id.Value = codeNode.As("*", "code")

    if displayName := codeNode.As("*", "displayName"); displayName != "" {
      med.Name = displayName
    } else if nameNode := manNode.SelectNode("*", "name"); nameNode != nil {
      med.Name = nameNode.Value
    } else if originalNode := codeNode.SelectNode("*", "originalText"); originalNode != nil {
      med.Name = originalNode.Value
    }

    meds = append(meds, med)
  }

  return meds, errs
}

func parseProblems(node *xmlx.Node) ([]Problem, []error) {
  return nil, nil
}

func templateId(node *xmlx.Node) string {
  idNodes := node.SelectNodes("*", "templateId")
  id := ""
  for _, idNode := range idNodes {
    id = idNode.As("*", "root")
    if strings.HasPrefix(id, "2.16.840.1.113883.10.20.1.") {
      return id
    }
  }

  return id
}

func codeSystemToMedType(codeSystem string) (string, error) {
  switch codeSystem {
  case "2.16.840.1.113883.6.69": // NDC
    return "NDC", nil
  case "2.16.840.1.113883.6.88": // RxNorm
    return "RxNorm", nil
  }
  return "", fmt.Errorf(`Unknown med codeSystem value of "%s"`, codeSystem)
}
<|endoftext|>


<commit_before>package session

import (
	"net/http"
	"time"

	"github.com/skygeario/skygear-server/pkg/core/auth"
	"github.com/skygeario/skygear-server/pkg/core/config"
	coreHttp "github.com/skygeario/skygear-server/pkg/core/http"
	"github.com/skygeario/skygear-server/pkg/core/model"
)

type writerImpl struct {
	authContext       auth.ContextGetter
	clientConfigs     []config.APIClientConfiguration
	mfaConfiguration  *config.MFAConfiguration
	useInsecureCookie bool
}

func NewWriter(
	authContext auth.ContextGetter,
	clientConfigs []config.APIClientConfiguration,
	mfaConfiguration *config.MFAConfiguration,
	useInsecureCookie bool,
) Writer {
	return &writerImpl{
		authContext:       authContext,
		clientConfigs:     clientConfigs,
		mfaConfiguration:  mfaConfiguration,
		useInsecureCookie: useInsecureCookie,
	}
}

func (w *writerImpl) WriteSession(rw http.ResponseWriter, accessToken *string, mfaBearerToken *string) {
	clientConfig, _ := model.GetClientConfig(w.clientConfigs, w.authContext.AccessKey().ClientID)
	useCookie := clientConfig.SessionTransport == config.SessionTransportTypeCookie

	cookieSession := &http.Cookie{
		Name:     coreHttp.CookieNameSession,
		Path:     "/",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
	}
	cookieMFABearerToken := &http.Cookie{
		Name:     coreHttp.CookieNameMFABearerToken,
		Path:     "/_auth/mfa/bearer_token/authenticate",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
	}
	w.configureCookieSameSite(cookieSession, clientConfig.SameSite)
	w.configureCookieSameSite(cookieMFABearerToken, clientConfig.SameSite)

	if useCookie {
		cookieSession.Value = *accessToken
		*accessToken = ""
		cookieSession.MaxAge = int(time.Duration(clientConfig.AccessTokenLifetime).Seconds())

		if mfaBearerToken != nil {
			cookieMFABearerToken.Value = *mfaBearerToken
			*mfaBearerToken = ""
			cookieMFABearerToken.MaxAge = int(time.Duration(w.mfaConfiguration.BearerToken.ExpireInDays).Seconds())
		}
	} else {
		cookieSession.Expires = time.Unix(0, 0)
		cookieMFABearerToken.Expires = time.Unix(0, 0)
	}

	updateCookie(rw, cookieSession)
	if mfaBearerToken != nil {
		updateCookie(rw, cookieMFABearerToken)
	}
}

func (w *writerImpl) configureCookieSameSite(cookie *http.Cookie, sameSite config.SessionCookieSameSite) {
	switch sameSite {
	case config.SessionCookieSameSiteNone:
		cookie.SameSite = http.SameSiteDefaultMode
	case config.SessionCookieSameSiteLax:
		cookie.SameSite = http.SameSiteLaxMode
	case config.SessionCookieSameSiteStrict:
		cookie.SameSite = http.SameSiteStrictMode
	}
}

func (w *writerImpl) ClearSession(rw http.ResponseWriter) {
	updateCookie(rw, &http.Cookie{
		Name:     coreHttp.CookieNameSession,
		Path:     "/",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
		Expires:  time.Unix(0, 0),
	})
}

func (w *writerImpl) ClearMFABearerToken(rw http.ResponseWriter) {
	updateCookie(rw, &http.Cookie{
		Name:     coreHttp.CookieNameMFABearerToken,
		Path:     "/_auth/mfa/bearer_token/authenticate",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
		Expires:  time.Unix(0, 0),
	})
}

func updateCookie(rw http.ResponseWriter, cookie *http.Cookie) {
	header := rw.Header()
	resp := http.Response{Header: header}

	cookies := resp.Cookies()
	updated := false
	for i, c := range cookies {
		if c.Name == cookie.Name && c.Domain == cookie.Domain && c.Path == cookie.Path {
			cookies[i] = cookie
			updated = true
		}
	}
	if !updated {
		cookies = append(cookies, cookie)
	}

	setCookies := make([]string, len(cookies))
	for i, c := range cookies {
		setCookies[i] = c.String()
	}
	header["Set-Cookie"] = setCookies
}
<commit_msg>Fix max-age calculation<commit_after>package session

import (
	"net/http"
	"time"

	"github.com/skygeario/skygear-server/pkg/core/auth"
	"github.com/skygeario/skygear-server/pkg/core/config"
	coreHttp "github.com/skygeario/skygear-server/pkg/core/http"
	"github.com/skygeario/skygear-server/pkg/core/model"
)

type writerImpl struct {
	authContext       auth.ContextGetter
	clientConfigs     []config.APIClientConfiguration
	mfaConfiguration  *config.MFAConfiguration
	useInsecureCookie bool
}

func NewWriter(
	authContext auth.ContextGetter,
	clientConfigs []config.APIClientConfiguration,
	mfaConfiguration *config.MFAConfiguration,
	useInsecureCookie bool,
) Writer {
	return &writerImpl{
		authContext:       authContext,
		clientConfigs:     clientConfigs,
		mfaConfiguration:  mfaConfiguration,
		useInsecureCookie: useInsecureCookie,
	}
}

func (w *writerImpl) WriteSession(rw http.ResponseWriter, accessToken *string, mfaBearerToken *string) {
	clientConfig, _ := model.GetClientConfig(w.clientConfigs, w.authContext.AccessKey().ClientID)
	useCookie := clientConfig.SessionTransport == config.SessionTransportTypeCookie

	cookieSession := &http.Cookie{
		Name:     coreHttp.CookieNameSession,
		Path:     "/",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
	}
	cookieMFABearerToken := &http.Cookie{
		Name:     coreHttp.CookieNameMFABearerToken,
		Path:     "/_auth/mfa/bearer_token/authenticate",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
	}
	w.configureCookieSameSite(cookieSession, clientConfig.SameSite)
	w.configureCookieSameSite(cookieMFABearerToken, clientConfig.SameSite)

	if useCookie {
		cookieSession.Value = *accessToken
		*accessToken = ""
		cookieSession.MaxAge = clientConfig.AccessTokenLifetime

		if mfaBearerToken != nil {
			cookieMFABearerToken.Value = *mfaBearerToken
			*mfaBearerToken = ""
			cookieMFABearerToken.MaxAge = 86400 * w.mfaConfiguration.BearerToken.ExpireInDays
		}
	} else {
		cookieSession.Expires = time.Unix(0, 0)
		cookieMFABearerToken.Expires = time.Unix(0, 0)
	}

	updateCookie(rw, cookieSession)
	if mfaBearerToken != nil {
		updateCookie(rw, cookieMFABearerToken)
	}
}

func (w *writerImpl) configureCookieSameSite(cookie *http.Cookie, sameSite config.SessionCookieSameSite) {
	switch sameSite {
	case config.SessionCookieSameSiteNone:
		cookie.SameSite = http.SameSiteDefaultMode
	case config.SessionCookieSameSiteLax:
		cookie.SameSite = http.SameSiteLaxMode
	case config.SessionCookieSameSiteStrict:
		cookie.SameSite = http.SameSiteStrictMode
	}
}

func (w *writerImpl) ClearSession(rw http.ResponseWriter) {
	updateCookie(rw, &http.Cookie{
		Name:     coreHttp.CookieNameSession,
		Path:     "/",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
		Expires:  time.Unix(0, 0),
	})
}

func (w *writerImpl) ClearMFABearerToken(rw http.ResponseWriter) {
	updateCookie(rw, &http.Cookie{
		Name:     coreHttp.CookieNameMFABearerToken,
		Path:     "/_auth/mfa/bearer_token/authenticate",
		HttpOnly: true,
		Secure:   !w.useInsecureCookie,
		Expires:  time.Unix(0, 0),
	})
}

func updateCookie(rw http.ResponseWriter, cookie *http.Cookie) {
	header := rw.Header()
	resp := http.Response{Header: header}

	cookies := resp.Cookies()
	updated := false
	for i, c := range cookies {
		if c.Name == cookie.Name && c.Domain == cookie.Domain && c.Path == cookie.Path {
			cookies[i] = cookie
			updated = true
		}
	}
	if !updated {
		cookies = append(cookies, cookie)
	}

	setCookies := make([]string, len(cookies))
	for i, c := range cookies {
		setCookies[i] = c.String()
	}
	header["Set-Cookie"] = setCookies
}
<|endoftext|>


<commit_before>// +build linux

/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kubelet

import (
	"fmt"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/klog"
	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
)

func (kl *Kubelet) initNetworkUtil() {
	kl.syncNetworkUtil()
	go kl.iptClient.Monitor(utiliptables.Chain("KUBE-KUBELET-CANARY"),
		[]utiliptables.Table{utiliptables.TableMangle, utiliptables.TableNAT, utiliptables.TableFilter},
		kl.syncNetworkUtil, 1*time.Minute, wait.NeverStop)
}

// syncNetworkUtil ensures the network utility are present on host.
// Network util includes:
// 1. 	In nat table, KUBE-MARK-DROP rule to mark connections for dropping
// 	Marked connection will be drop on INPUT/OUTPUT Chain in filter table
// 2. 	In nat table, KUBE-MARK-MASQ rule to mark connections for SNAT
// 	Marked connection will get SNAT on POSTROUTING Chain in nat table
func (kl *Kubelet) syncNetworkUtil() {
	if kl.iptablesMasqueradeBit < 0 || kl.iptablesMasqueradeBit > 31 {
		klog.Errorf("invalid iptables-masquerade-bit %v not in [0, 31]", kl.iptablesMasqueradeBit)
		return
	}

	if kl.iptablesDropBit < 0 || kl.iptablesDropBit > 31 {
		klog.Errorf("invalid iptables-drop-bit %v not in [0, 31]", kl.iptablesDropBit)
		return
	}

	if kl.iptablesDropBit == kl.iptablesMasqueradeBit {
		klog.Errorf("iptables-masquerade-bit %v and iptables-drop-bit %v must be different", kl.iptablesMasqueradeBit, kl.iptablesDropBit)
		return
	}

	// Setup KUBE-MARK-DROP rules
	dropMark := getIPTablesMark(kl.iptablesDropBit)
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableNAT, KubeMarkDropChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, KubeMarkDropChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableNAT, KubeMarkDropChain, "-j", "MARK", "--set-xmark", dropMark); err != nil {
		klog.Errorf("Failed to ensure marking rule for %v: %v", KubeMarkDropChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableFilter, KubeFirewallChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableFilter, KubeFirewallChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableFilter, KubeFirewallChain,
		"-m", "comment", "--comment", "kubernetes firewall for dropping marked packets",
		"-m", "mark", "--mark", dropMark,
		"-j", "DROP"); err != nil {
		klog.Errorf("Failed to ensure rule to drop packet marked by %v in %v chain %v: %v", KubeMarkDropChain, utiliptables.TableFilter, KubeFirewallChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Prepend, utiliptables.TableFilter, utiliptables.ChainOutput, "-j", string(KubeFirewallChain)); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", utiliptables.TableFilter, utiliptables.ChainOutput, KubeFirewallChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Prepend, utiliptables.TableFilter, utiliptables.ChainInput, "-j", string(KubeFirewallChain)); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", utiliptables.TableFilter, utiliptables.ChainInput, KubeFirewallChain, err)
		return
	}

	// Setup KUBE-MARK-MASQ rules
	masqueradeMark := getIPTablesMark(kl.iptablesMasqueradeBit)
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableNAT, KubeMarkMasqChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, KubeMarkMasqChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableNAT, KubePostroutingChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, KubePostroutingChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableNAT, KubeMarkMasqChain, "-j", "MARK", "--set-xmark", masqueradeMark); err != nil {
		klog.Errorf("Failed to ensure marking rule for %v: %v", KubeMarkMasqChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Prepend, utiliptables.TableNAT, utiliptables.ChainPostrouting,
		"-m", "comment", "--comment", "kubernetes postrouting rules", "-j", string(KubePostroutingChain)); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", utiliptables.TableNAT, utiliptables.ChainPostrouting, KubePostroutingChain, err)
		return
	}
	// Establish the masquerading rule.
	// NB: THIS MUST MATCH the corresponding code in the iptables and ipvs
	// modes of kube-proxy
	masqRule := []string{
		"-m", "comment", "--comment", "kubernetes service traffic requiring SNAT",
		"-m", "mark", "--mark", masqueradeMark,
		"-j", "MASQUERADE",
	}
	if kl.iptClient.HasRandomFully() {
		masqRule = append(masqRule, "--random-fully")
		klog.V(3).Info("Using `--random-fully` in the MASQUERADE rule for iptables")
	} else {
		klog.V(2).Info("Not using `--random-fully` in the MASQUERADE rule for iptables because the local version of iptables does not support it")
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableNAT, KubePostroutingChain, masqRule...); err != nil {
		klog.Errorf("Failed to ensure SNAT rule for packets marked by %v in %v chain %v: %v", KubeMarkMasqChain, utiliptables.TableNAT, KubePostroutingChain, err)
		return
	}
}

// getIPTablesMark returns the fwmark given the bit
func getIPTablesMark(bit int) string {
	value := 1 << uint(bit)
	return fmt.Sprintf("%#08x/%#08x", value, value)
}
<commit_msg>UPSTREAM: 91569: kubelet: block non-forwarded packets from crossing the localhost boundary<commit_after>// +build linux

/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kubelet

import (
	"fmt"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/klog"
	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
)

func (kl *Kubelet) initNetworkUtil() {
	kl.syncNetworkUtil()
	go kl.iptClient.Monitor(utiliptables.Chain("KUBE-KUBELET-CANARY"),
		[]utiliptables.Table{utiliptables.TableMangle, utiliptables.TableNAT, utiliptables.TableFilter},
		kl.syncNetworkUtil, 1*time.Minute, wait.NeverStop)
}

// syncNetworkUtil ensures the network utility are present on host.
// Network util includes:
// 1. 	In nat table, KUBE-MARK-DROP rule to mark connections for dropping
// 	Marked connection will be drop on INPUT/OUTPUT Chain in filter table
// 2. 	In nat table, KUBE-MARK-MASQ rule to mark connections for SNAT
// 	Marked connection will get SNAT on POSTROUTING Chain in nat table
func (kl *Kubelet) syncNetworkUtil() {
	if kl.iptablesMasqueradeBit < 0 || kl.iptablesMasqueradeBit > 31 {
		klog.Errorf("invalid iptables-masquerade-bit %v not in [0, 31]", kl.iptablesMasqueradeBit)
		return
	}

	if kl.iptablesDropBit < 0 || kl.iptablesDropBit > 31 {
		klog.Errorf("invalid iptables-drop-bit %v not in [0, 31]", kl.iptablesDropBit)
		return
	}

	if kl.iptablesDropBit == kl.iptablesMasqueradeBit {
		klog.Errorf("iptables-masquerade-bit %v and iptables-drop-bit %v must be different", kl.iptablesMasqueradeBit, kl.iptablesDropBit)
		return
	}

	// Setup KUBE-MARK-DROP rules
	dropMark := getIPTablesMark(kl.iptablesDropBit)
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableNAT, KubeMarkDropChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, KubeMarkDropChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableNAT, KubeMarkDropChain, "-j", "MARK", "--set-xmark", dropMark); err != nil {
		klog.Errorf("Failed to ensure marking rule for %v: %v", KubeMarkDropChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableFilter, KubeFirewallChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableFilter, KubeFirewallChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableFilter, KubeFirewallChain,
		"-m", "comment", "--comment", "kubernetes firewall for dropping marked packets",
		"-m", "mark", "--mark", dropMark,
		"-j", "DROP"); err != nil {
		klog.Errorf("Failed to ensure rule to drop packet marked by %v in %v chain %v: %v", KubeMarkDropChain, utiliptables.TableFilter, KubeFirewallChain, err)
		return
	}

	// drop all non-local packets to localhost if they're not part of an existing
	// forwarded connection. See #90259
	if !kl.iptClient.IsIpv6() { // ipv6 doesn't have this issue
		if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableFilter, KubeFirewallChain,
			"-m", "comment", "--comment", "block incoming localnet connections",
			"--dst", "127.0.0.0/8",
			"!", "--src", "127.0.0.0/8",
			"-m", "conntrack",
			"!", "--ctstate", "RELATED,ESTABLISHED,DNAT",
			"-j", "DROP"); err != nil {
			klog.Errorf("Failed to ensure rule to drop invalid localhost packets in %v chain %v: %v", utiliptables.TableFilter, KubeFirewallChain, err)
			return
		}
	}

	if _, err := kl.iptClient.EnsureRule(utiliptables.Prepend, utiliptables.TableFilter, utiliptables.ChainOutput, "-j", string(KubeFirewallChain)); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", utiliptables.TableFilter, utiliptables.ChainOutput, KubeFirewallChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Prepend, utiliptables.TableFilter, utiliptables.ChainInput, "-j", string(KubeFirewallChain)); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", utiliptables.TableFilter, utiliptables.ChainInput, KubeFirewallChain, err)
		return
	}

	// Setup KUBE-MARK-MASQ rules
	masqueradeMark := getIPTablesMark(kl.iptablesMasqueradeBit)
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableNAT, KubeMarkMasqChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, KubeMarkMasqChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureChain(utiliptables.TableNAT, KubePostroutingChain); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, KubePostroutingChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableNAT, KubeMarkMasqChain, "-j", "MARK", "--set-xmark", masqueradeMark); err != nil {
		klog.Errorf("Failed to ensure marking rule for %v: %v", KubeMarkMasqChain, err)
		return
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Prepend, utiliptables.TableNAT, utiliptables.ChainPostrouting,
		"-m", "comment", "--comment", "kubernetes postrouting rules", "-j", string(KubePostroutingChain)); err != nil {
		klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", utiliptables.TableNAT, utiliptables.ChainPostrouting, KubePostroutingChain, err)
		return
	}
	// Establish the masquerading rule.
	// NB: THIS MUST MATCH the corresponding code in the iptables and ipvs
	// modes of kube-proxy
	masqRule := []string{
		"-m", "comment", "--comment", "kubernetes service traffic requiring SNAT",
		"-m", "mark", "--mark", masqueradeMark,
		"-j", "MASQUERADE",
	}
	if kl.iptClient.HasRandomFully() {
		masqRule = append(masqRule, "--random-fully")
		klog.V(3).Info("Using `--random-fully` in the MASQUERADE rule for iptables")
	} else {
		klog.V(2).Info("Not using `--random-fully` in the MASQUERADE rule for iptables because the local version of iptables does not support it")
	}
	if _, err := kl.iptClient.EnsureRule(utiliptables.Append, utiliptables.TableNAT, KubePostroutingChain, masqRule...); err != nil {
		klog.Errorf("Failed to ensure SNAT rule for packets marked by %v in %v chain %v: %v", KubeMarkMasqChain, utiliptables.TableNAT, KubePostroutingChain, err)
		return
	}
}

// getIPTablesMark returns the fwmark given the bit
func getIPTablesMark(bit int) string {
	value := 1 << uint(bit)
	return fmt.Sprintf("%#08x/%#08x", value, value)
}
<|endoftext|>


<commit_before>package cleanup

import (
	"testing"
	"time"

	"github.com/grafana/grafana/pkg/setting"
	. "github.com/smartystreets/goconvey/convey"
)

func TestCleanUpTmpFiles(t *testing.T) {
	Convey("Cleanup service tests", t, func() {
		cfg := setting.Cfg{}
		cfg.TempDataLifetime, _ = time.ParseDuration("24h")
		service := CleanUpService{
			Cfg: &cfg,
		}
		now := time.Now()
		secondAgo := now.Add(-time.Second)
		twoDaysAgo := now.Add(-time.Second * 3600 * 24 * 2)
		weekAgo := now.Add(-time.Second * 3600 * 24 * 7)

		Convey("Should not cleanup recent files", func() {
			So(service.shouldCleanupTempFile(secondAgo, now), ShouldBeFalse)
		})

		Convey("Should cleanup older files", func() {
			So(service.shouldCleanupTempFile(twoDaysAgo, now), ShouldBeTrue)
		})

		Convey("After increasing temporary files lifetime, older files should be kept", func() {
			cfg.TempDataLifetime, _ = time.ParseDuration("1000h")
			So(service.shouldCleanupTempFile(weekAgo, now), ShouldBeFalse)
		})

		Convey("If lifetime is 0, files should never be cleaned up", func() {
			cfg.TempDataLifetime = 0
			So(service.shouldCleanupTempFile(weekAgo, now), ShouldBeFalse)
		})
	})
}
<commit_msg>Chore: Goconvey migration for cleanup package (#40547)<commit_after>package cleanup

import (
	"testing"
	"time"

	"github.com/grafana/grafana/pkg/setting"
	"github.com/stretchr/testify/require"
)

func TestCleanUpTmpFiles(t *testing.T) {
	cfg := setting.Cfg{}
	cfg.TempDataLifetime, _ = time.ParseDuration("24h")
	service := CleanUpService{
		Cfg: &cfg,
	}
	now := time.Now()
	secondAgo := now.Add(-time.Second)
	twoDaysAgo := now.Add(-time.Second * 3600 * 24 * 2)
	weekAgo := now.Add(-time.Second * 3600 * 24 * 7)
	t.Run("Should not cleanup recent files", func(t *testing.T) {
		require.False(t, service.shouldCleanupTempFile(secondAgo, now))
	})
	t.Run("Should cleanup older files", func(t *testing.T) {
		require.True(t, service.shouldCleanupTempFile(twoDaysAgo, now))
	})

	t.Run("After increasing temporary files lifetime, older files should be kept", func(t *testing.T) {
		cfg.TempDataLifetime, _ = time.ParseDuration("1000h")
		require.False(t, service.shouldCleanupTempFile(weekAgo, now))
	})

	t.Run("If lifetime is 0, files should never be cleaned up", func(t *testing.T) {
		cfg.TempDataLifetime = 0
		require.False(t, service.shouldCleanupTempFile(weekAgo, now))
	})
}
<|endoftext|>


<commit_before>package critter

type Slimupation uint64

const (
	SlimupationMage Slimupation = iota
	SlimupationBrute
	SlimupationSticky

	slimupationCount
)

var slimupationInfo = [slimupationCount]struct {
	title  string
	flavor string
	color  string
	radius int
}{
	SlimupationMage: {
		title:  "mage",
		flavor: "with magical properties.",
	},
	SlimupationBrute: {
		title:  "brute",
		flavor: "that can bench press a whole hero.",
	},
	SlimupationSticky: {
		title:  "citizen",
		flavor: "that's a little stickier than most.",
	},
		color:  "#00d",
		radius: 7},

	SlimupationBrute: {
		title:  "brute",
		flavor: "that can bench press a whole hero.",
		color:  "#d00",
		radius: 7},
	SlimupationSticky: {
		title:  "citizen",
		flavor: "that's a little stickier than most.",
		color:  "#0d0",
		radius: 14},
}

func (s Slimupation) Name() string   { return slimupationInfo[s].title }
func (s Slimupation) Flavor() string { return slimupationInfo[s].flavor }
<commit_msg>okay pleased be good<commit_after>package critter

type Slimupation uint64

const (
	SlimupationMage Slimupation = iota
	SlimupationBrute
	SlimupationSticky

	slimupationCount
)

var slimupationInfo = [slimupationCount]struct {
	title  string
	flavor string
	color  string
	radius int
}{
	SlimupationMage: {
		title:  "mage",
		flavor: "with some magical abilities.",
		color:  "#00d",
		radius: 7,},

	SlimupationBrute: {
		title:  "brute",
		flavor: "that can bench press a whole hero.",
		color:  "#d00",
		radius: 7,},
	SlimupationSticky: {
		title:  "citizen",
		flavor: "that's a little stickier than most.",
		color:  "#0d0",
		radius: 14,},
}

func (s Slimupation) Name() string   { return slimupationInfo[s].title }
func (s Slimupation) Flavor() string { return slimupationInfo[s].flavor }
<|endoftext|>