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

import (
	"flag"
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/sys/unix"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/reqtrace"
)

var fTraceByPID = flag.Bool(
	"fuse.trace_by_pid",
	false,
	"Enable a hacky mode that uses reqtrace to group all ops from each "+
		"individual PID. Not a good idea to use in production; races, bugs, and "+
		"resource leaks likely lurk.")

// A connection to the fuse kernel process.
type Connection struct {
	debugLogger *log.Logger
	errorLogger *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from bazilfuse request ID (*not* the op ID for logging used above)
	// to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[bazilfuse.RequestID]func()

	// A map from PID to a traced context for that PID.
	//
	// GUARDED_BY(mu)
	pidMap map[int]context.Context
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	debugLogger *log.Logger,
	errorLogger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		debugLogger: debugLogger,
		errorLogger: errorLogger,
		wrapped:     wrapped,
		parentCtx:   parentCtx,
		cancelFuncs: make(map[bazilfuse.RequestID]func()),
		pidMap:      make(map[int]context.Context),
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) debugLog(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.debugLogger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	reqID bazilfuse.RequestID,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[reqID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", reqID))
	}

	c.cancelFuncs[reqID] = f
}

// Wait until the process completes, then close off the trace and remove the
// context from the map.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) reportWhenPIDGone(
	pid int,
	ctx context.Context,
	report reqtrace.ReportFunc) {
	// HACK(jacobsa): Poll until the process no longer exists.
	const pollPeriod = 50 * time.Millisecond
	for {
		// The man page for kill(2) says that if the signal is zero, then "no
		// signal is sent, but error checking is still performed; this can be used
		// to check for the existence of a process ID".
		err := unix.Kill(pid, 0)

		// ESRCH means the process is gone.
		if err == unix.ESRCH {
			break
		}

		// If we receive EPERM, we're not going to be able to do what we want. We
		// don't really have any choice but to print info and leak.
		if err == unix.EPERM {
			log.Printf("Failed to kill(2) PID %v; no permissions. Leaking trace.", pid)
			return
		}

		// Otherwise, panic.
		if err != nil {
			panic(fmt.Errorf("Kill(%v): %v", pid, err))
		}

		time.Sleep(pollPeriod)
	}

	// Finish up.
	report(nil)

	c.mu.Lock()
	delete(c.pidMap, pid)
	c.mu.Unlock()
}

// Set up a hacky per-PID trace context, if enabled. Either way, return a
// context from which an operation should inherit.
//
// See notes on fTraceByPID.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) maybeTraceByPID(
	pid int) (ctx context.Context) {
	ctx = c.parentCtx

	// Is there anything to do?
	if !reqtrace.Enabled() || !*fTraceByPID {
		return
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	// Do we already have a traced context for this PID?
	if existing, ok := c.pidMap[pid]; ok {
		ctx = existing
		return
	}

	// Set up a new one and stick it in the map.
	var report reqtrace.ReportFunc
	ctx, report = reqtrace.Trace(ctx, fmt.Sprintf("Requests from PID %v", pid))
	c.pidMap[pid] = ctx

	// Ensure we close the trace and remove it from the map eventually.
	go c.reportWhenPIDGone(pid, ctx, report)

	return
}

// Set up state for an op that is about to be returned to the user, given its
// underlying bazilfuse request.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(
	bfReq bazilfuse.Request) (ctx context.Context) {
	reqID := bfReq.Hdr().ID

	// Note that the op is in flight.
	c.opsInFlight.Add(1)

	// Choose a parent context.
	ctx = c.maybeTraceByPID(int(bfReq.Hdr().Pid))

	// Set up a cancellation function.
	//
	// Special case: On Darwin, osxfuse appears to aggressively reuse "unique"
	// request IDs. This matters for Forget requests, which have no reply
	// associated and therefore appear to have IDs that are immediately eligible
	// for reuse. For these, we should not record any state keyed on their ID.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	if _, ok := bfReq.(*bazilfuse.ForgetRequest); !ok {
		var cancel func()
		ctx, cancel = context.WithCancel(ctx)
		c.recordCancelFunc(reqID, cancel)
	}

	return
}

// Clean up all state associated with an op to which the user has responded,
// given its underlying bazilfuse request. This must be called before a
// response is sent to the kernel, to avoid a race where the request's ID might
// be reused by osxfuse.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(bfReq bazilfuse.Request) {
	c.mu.Lock()
	defer c.mu.Unlock()

	reqID := bfReq.Hdr().ID

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	//
	// Special case: we don't do this for Forget requests. See the note in
	// beginOp above.
	if _, ok := bfReq.(*bazilfuse.ForgetRequest); !ok {
		cancel, ok := c.cancelFuncs[reqID]
		if !ok {
			panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
		}

		cancel()
		delete(c.cancelFuncs, reqID)
	}

	// Decrement the in-flight counter.
	c.opsInFlight.Done()
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) handleInterrupt(req *bazilfuse.InterruptRequest) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// NOTE(jacobsa): fuse.txt in the Linux kernel documentation
	// (https://goo.gl/H55Dnr) defines the kernel <-> userspace protocol for
	// interrupts.
	//
	// In particular, my reading of it is that an interrupt request cannot be
	// delivered to userspace before the original request. The part about the
	// race and EAGAIN appears to be aimed at userspace programs that
	// concurrently process requests.
	//
	// So in this method we assume that if we can't find the ID to be
	// interrupted, it means that the request has already been replied to.
	cancel, ok := c.cancelFuncs[req.IntrID]
	if !ok {
		return
	}

	cancel()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		var bfReq bazilfuse.Request
		bfReq, err = c.wrapped.ReadRequest()

		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.debugLog(opID, 1, "<- %v", bfReq)

		// Special case: responding to statfs is required to make mounting work on
		// OS X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.debugLog(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Special case: handle interrupt requests.
		if interruptReq, ok := bfReq.(*bazilfuse.InterruptRequest); ok {
			c.handleInterrupt(interruptReq)
			continue
		}

		// Set up op dependencies.
		opCtx := c.beginOp(bfReq)

		debugLogForOp := func(calldepth int, format string, v ...interface{}) {
			c.debugLog(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp(bfReq) }

		op = fuseops.Convert(
			opCtx,
			bfReq,
			debugLogForOp,
			c.errorLogger,
			finished)

		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<commit_msg>Made more firm some wording about interrupt semantics.<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 fuse

import (
	"flag"
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/sys/unix"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/reqtrace"
)

var fTraceByPID = flag.Bool(
	"fuse.trace_by_pid",
	false,
	"Enable a hacky mode that uses reqtrace to group all ops from each "+
		"individual PID. Not a good idea to use in production; races, bugs, and "+
		"resource leaks likely lurk.")

// A connection to the fuse kernel process.
type Connection struct {
	debugLogger *log.Logger
	errorLogger *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from bazilfuse request ID (*not* the op ID for logging used above)
	// to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[bazilfuse.RequestID]func()

	// A map from PID to a traced context for that PID.
	//
	// GUARDED_BY(mu)
	pidMap map[int]context.Context
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	debugLogger *log.Logger,
	errorLogger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		debugLogger: debugLogger,
		errorLogger: errorLogger,
		wrapped:     wrapped,
		parentCtx:   parentCtx,
		cancelFuncs: make(map[bazilfuse.RequestID]func()),
		pidMap:      make(map[int]context.Context),
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) debugLog(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.debugLogger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	reqID bazilfuse.RequestID,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[reqID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", reqID))
	}

	c.cancelFuncs[reqID] = f
}

// Wait until the process completes, then close off the trace and remove the
// context from the map.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) reportWhenPIDGone(
	pid int,
	ctx context.Context,
	report reqtrace.ReportFunc) {
	// HACK(jacobsa): Poll until the process no longer exists.
	const pollPeriod = 50 * time.Millisecond
	for {
		// The man page for kill(2) says that if the signal is zero, then "no
		// signal is sent, but error checking is still performed; this can be used
		// to check for the existence of a process ID".
		err := unix.Kill(pid, 0)

		// ESRCH means the process is gone.
		if err == unix.ESRCH {
			break
		}

		// If we receive EPERM, we're not going to be able to do what we want. We
		// don't really have any choice but to print info and leak.
		if err == unix.EPERM {
			log.Printf("Failed to kill(2) PID %v; no permissions. Leaking trace.", pid)
			return
		}

		// Otherwise, panic.
		if err != nil {
			panic(fmt.Errorf("Kill(%v): %v", pid, err))
		}

		time.Sleep(pollPeriod)
	}

	// Finish up.
	report(nil)

	c.mu.Lock()
	delete(c.pidMap, pid)
	c.mu.Unlock()
}

// Set up a hacky per-PID trace context, if enabled. Either way, return a
// context from which an operation should inherit.
//
// See notes on fTraceByPID.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) maybeTraceByPID(
	pid int) (ctx context.Context) {
	ctx = c.parentCtx

	// Is there anything to do?
	if !reqtrace.Enabled() || !*fTraceByPID {
		return
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	// Do we already have a traced context for this PID?
	if existing, ok := c.pidMap[pid]; ok {
		ctx = existing
		return
	}

	// Set up a new one and stick it in the map.
	var report reqtrace.ReportFunc
	ctx, report = reqtrace.Trace(ctx, fmt.Sprintf("Requests from PID %v", pid))
	c.pidMap[pid] = ctx

	// Ensure we close the trace and remove it from the map eventually.
	go c.reportWhenPIDGone(pid, ctx, report)

	return
}

// Set up state for an op that is about to be returned to the user, given its
// underlying bazilfuse request.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(
	bfReq bazilfuse.Request) (ctx context.Context) {
	reqID := bfReq.Hdr().ID

	// Note that the op is in flight.
	c.opsInFlight.Add(1)

	// Choose a parent context.
	ctx = c.maybeTraceByPID(int(bfReq.Hdr().Pid))

	// Set up a cancellation function.
	//
	// Special case: On Darwin, osxfuse aggressively reuses "unique" request IDs.
	// This matters for Forget requests, which have no reply associated and
	// therefore have IDs that are immediately eligible for reuse. For these, we
	// should not record any state keyed on their ID.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	if _, ok := bfReq.(*bazilfuse.ForgetRequest); !ok {
		var cancel func()
		ctx, cancel = context.WithCancel(ctx)
		c.recordCancelFunc(reqID, cancel)
	}

	return
}

// Clean up all state associated with an op to which the user has responded,
// given its underlying bazilfuse request. This must be called before a
// response is sent to the kernel, to avoid a race where the request's ID might
// be reused by osxfuse.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(bfReq bazilfuse.Request) {
	c.mu.Lock()
	defer c.mu.Unlock()

	reqID := bfReq.Hdr().ID

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	//
	// Special case: we don't do this for Forget requests. See the note in
	// beginOp above.
	if _, ok := bfReq.(*bazilfuse.ForgetRequest); !ok {
		cancel, ok := c.cancelFuncs[reqID]
		if !ok {
			panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
		}

		cancel()
		delete(c.cancelFuncs, reqID)
	}

	// Decrement the in-flight counter.
	c.opsInFlight.Done()
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) handleInterrupt(req *bazilfuse.InterruptRequest) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// NOTE(jacobsa): fuse.txt in the Linux kernel documentation
	// (https://goo.gl/H55Dnr) defines the kernel <-> userspace protocol for
	// interrupts.
	//
	// In particular, my reading of it is that an interrupt request cannot be
	// delivered to userspace before the original request. The part about the
	// race and EAGAIN appears to be aimed at userspace programs that
	// concurrently process requests (cf. http://goo.gl/BES2rs).
	//
	// So in this method if we can't find the ID to be interrupted, it means that
	// the request has already been replied to.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	// Cf. http://comments.gmane.org/gmane.comp.file-systems.fuse.devel/14675
	cancel, ok := c.cancelFuncs[req.IntrID]
	if !ok {
		return
	}

	cancel()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		var bfReq bazilfuse.Request
		bfReq, err = c.wrapped.ReadRequest()

		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.debugLog(opID, 1, "<- %v", bfReq)

		// Special case: responding to statfs is required to make mounting work on
		// OS X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.debugLog(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Special case: handle interrupt requests.
		if interruptReq, ok := bfReq.(*bazilfuse.InterruptRequest); ok {
			c.handleInterrupt(interruptReq)
			continue
		}

		// Set up op dependencies.
		opCtx := c.beginOp(bfReq)

		debugLogForOp := func(calldepth int, format string, v ...interface{}) {
			c.debugLog(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp(bfReq) }

		op = fuseops.Convert(
			opCtx,
			bfReq,
			debugLogForOp,
			c.errorLogger,
			finished)

		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew R. Wilson <mwilson@mattwilson.org>. All
// rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package omnilink

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"time"
)

type Omni struct {
	c          *net.TCPConn // Connection to Omni controller
	address    *net.TCPAddr // Address of Omni controller
	key        []byte       // AES key
	sessionKey []byte       // AES session key
	seqNum     uint16       // packet sequence number
	seqMutex   sync.Mutex   // protect sequence number
	buf        []byte       // incoming data buffer
}

func Connect(host, port string, key []byte) (*Omni, error) {
	var omni Omni
	omni.key = key
	omni.buf = make([]byte, 512)

	address, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port))
	if err != nil {
		return nil, err
	}
	omni.address = address

	// Connect to the Omni controller
	c, err := net.DialTCP("tcp", nil, omni.address)
	if err != nil {
		if c != nil {
			c.Close()
		}
		return nil, err
	}

	omni.c = c
	omni.c.SetKeepAlivePeriod(time.Duration(30) * time.Second)
	omni.c.SetKeepAlive(true)

	return &omni, nil
}

func (omni *Omni) getSeqNum() uint16 {
	omni.seqMutex.Lock()
	omni.seqNum++
	if omni.seqNum > 65535 || omni.seqNum < 1 {
		omni.seqNum = 1 // wrap around to 1, not 0
	}
	n := omni.seqNum
	omni.seqMutex.Unlock()
	return n
}

func (omni *Omni) negotiateNewSession() error {
	// Send the "Request New Session" message
	pkt := &packet{seqnum: omni.getSeqNum(), msgtype: pktReqNewSession}
	msg, _ := pkt.preparePacket(nil)
	_, err := omni.c.Write(msg)

	if err != nil {
		return err
	}

	nn := 0
	// read at least 4 bytes:
	for nn < 4 {
		n, err := omni.c.Read(omni.buf[nn:])
		//TODO: should we have a timeout here?
		if err != nil {
			return err
		}
		nn += n
	}

	if omni.buf[0] != msg[0] || omni.buf[1] != msg[1] {
		return errors.New("New Session response seq num doesn't match request.")
	}

	if pktMsgType(omni.buf[2]) != pktAckNewSession {
		return fmt.Errorf("New Session response was wrong type: %x.", omni.buf[2])
	}

	// Should have a good response, let's read the next 7 bytes for the data
	for nn < 11 {
		n, err := omni.c.Read(omni.buf[nn:])
		if err != nil {
			return err
		}
		nn += n
	}

	//sessionID := omni.buf[6:11]

	return nil
}
<commit_msg>Work in progress<commit_after>// Copyright 2015 Matthew R. Wilson <mwilson@mattwilson.org>. All
// rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package omnilink

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"time"
)

// The Omni structure represents an instance of a connection to an Omni
// controller.
type Omni struct {
	c          *net.TCPConn // Connection to Omni controller
	address    *net.TCPAddr // Address of Omni controller
	key        []byte       // AES key
	sessionID  []byte       // Session ID
	sessionKey []byte       // AES session key
	seqNum     uint16       // packet sequence number
	seqMutex   sync.Mutex   // protect sequence number
	buf        []byte       // incoming data buffer
}

// Connect this Omni instance to the controller specified in the parameters.
func Connect(host, port string, key []byte) (*Omni, error) {
	var omni Omni
	omni.key = key
	omni.buf = make([]byte, 512)

	address, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port))
	if err != nil {
		return nil, err
	}
	omni.address = address

	// Connect to the Omni controller
	c, err := net.DialTCP("tcp", nil, omni.address)
	if err != nil {
		if c != nil {
			c.Close()
		}
		return nil, err
	}

	omni.c = c
	omni.c.SetKeepAlivePeriod(time.Duration(30) * time.Second)
	omni.c.SetKeepAlive(true)

	err = omni.negotiateNewSession()
	if err != nil {
		omni.c.Close()
		return nil, err
	}

	return &omni, nil
}

func (omni *Omni) getSeqNum() uint16 {
	omni.seqMutex.Lock()
	omni.seqNum++
	if omni.seqNum > 65535 || omni.seqNum < 1 {
		omni.seqNum = 1 // wrap around to 1, not 0
	}
	n := omni.seqNum
	omni.seqMutex.Unlock()
	return n
}

func (omni *Omni) negotiateNewSession() error {
	// Send the "Request New Session" message
	pkt := &packet{seqnum: omni.getSeqNum(), msgtype: pktReqNewSession}
	msg, _ := pkt.preparePacket(nil)
	_, err := omni.c.Write(msg)

	if err != nil {
		omni.c.Close()
		return err
	}

	nn := 0
	// read at least 4 bytes:
	for nn < 4 {
		n, err := omni.c.Read(omni.buf[nn:])
		//TODO: should we have a timeout here?
		if err != nil {
			omni.c.Close()
			return err
		}
		nn += n
	}

	if omni.buf[0] != msg[0] || omni.buf[1] != msg[1] {
		omni.c.Close()
		return errors.New("New Session response seq num doesn't match request.")
	}

	if pktMsgType(omni.buf[2]) != pktAckNewSession {
		omni.c.Close()
		return fmt.Errorf("New Session response was wrong type: %x.", omni.buf[2])
	}

	// Should have a good response, let's read the next 7 bytes for the data
	for nn < 11 {
		n, err := omni.c.Read(omni.buf[nn:])
		if err != nil {
			omni.c.Close()
			return err
		}
		nn += n
	}

	var keyArray [16]byte
	var sessionArray [5]byte

	copy(keyArray[:], omni.key)
	copy(sessionArray[:], omni.buf[6:11])

	sessionKey := deriveSessionKey(keyArray, sessionArray)
	if len(sessionKey) != 16 {
		omni.c.Close()
		return errors.New("Bad session key length.")
	}
	omni.sessionKey = sessionKey[:]

	pkt = &packet{seqnum: omni.getSeqNum(), msgtype: pktReqSecure,
		data: omni.sessionID}
	msg, _ = pkt.preparePacket(omni.sessionKey)
	_, err = omni.c.Write(msg)

	if err != nil {
		omni.c.Close()
		return err
	}

	nn = 0
	for nn < 4 {
		n, err := omni.c.Read(omni.buf[nn:])
		//TODO: should we have a timeout here?
		if err != nil {
			omni.c.Close()
			return err
		}
		nn += n
	}

	if omni.buf[0] != msg[0] || omni.buf[1] != msg[1] {
		omni.c.Close()
		return errors.New("New Session response seq num doesn't match request.")
	}

	if pktMsgType(omni.buf[2]) != pktAckSecure {
		omni.c.Close()
		return fmt.Errorf("Secure Session response was wrong type: %x.", omni.buf[2])
	}

	return nil
}

func (omni *Omni) readEncryptedPacket() *packet {

	return nil
}
<|endoftext|>


<commit_before>package cons

import (
	"fmt"
	"regexp"
	"strings"
)

const (
	Start = "\033["
	Reset = "\033[0m"
)

var (
	plain  = false
	colors = map[string][2]string{
		"k": {"30", "40"}, // black
		"r": {"31", "41"}, // red
		"g": {"32", "42"}, // green
		"y": {"33", "43"}, // yellow
		"b": {"34", "44"}, // blue
		"m": {"35", "45"}, // magenta
		"c": {"36", "46"}, // cyan
		"w": {"37", "47"}, // white
	}
	styles = map[string]string{
		"b": "1",  // bold
		"k": "5",  // blink
		"u": "4",  // underline
		"i": "3",  // italic
		"v": "7",  // inverted
		"f": "51", // framed
	}
	// Three diagnostics set after each call to Color()
	// Tracers[0] is three indexes used in parsing the arg to Color():
	//     the first +, the :, and the second +
	// Tracers[1] gives the parsed colors and styles as foreground color, style, background color, style
	// Tracers[2] gives the final ansi control string
	Tracers [3]string
)

func append_code(base string, code string) string {
	if len(code) == 0 {
		return base
	}
	appended := base
	if len(appended) > 0 {
		appended += ";"
	}
	appended += code
	return appended
}

type style_spec struct {
	fg_color string
	fg_style string
	bg_color string
	bg_style string
}

func (spec style_spec) String() string {
	var result string
	result = append_code(result, spec.fg_color)
	result = append_code(result, spec.fg_style)
	result = append_code(result, spec.bg_color)
	result = append_code(result, spec.bg_style)
	return result
}

func IsValid(s string) bool {
	rx := []string{"(?:^[krgbymcw]$)", "(?:^[bkuivf]$)"}
	halves := strings.Split(s, ":")
	for _, h := range halves {
		if len(h) == 0 {
			continue
		}
		quarters := strings.Split(h, "+")
		rxIx := 0
		for _, q := range quarters {
			if len(q) == 0 {
				rxIx++
				continue
			}
			//fmt.Printf("pattern = %s\n", q)
			match, err := regexp.MatchString(rx[rxIx], q)
			if err != nil || !match {
				return false
			}
			rxIx++
		}
	}
	return true
}

// ColorCode returns the ansi control chars for coloring and styling console text
// code should be in UTF-8
func ColorCode(code string) string {
	code = strings.ToLower(code)
	if plain || code == "" {
		return ""
	}
	if code == "reset" {
		return Reset
	}

	var spec style_spec
	var ptrs = []*string{&spec.fg_color, &spec.fg_style, &spec.bg_color, &spec.bg_style}
	ptrIx := 0
	for _, roon := range code {
		switch string(roon) {
		case ":":
			ptrIx = 2
		case "+":
			ptrIx++
		default:
			*ptrs[ptrIx] = string(roon)
		}
	}
	Tracers[1] = fmt.Sprintf("%s %s %s %s",
		spec.fg_color, spec.fg_style, spec.bg_color, spec.bg_style)
	// now map to the real styles
	spec.fg_color = colors[spec.fg_color][0]
	spec.fg_style = styles[spec.fg_style]
	spec.bg_color = colors[spec.bg_color][1]
	spec.bg_style = styles[spec.bg_style]
	// get the ansi codes without the start and end escapes
	// (useful for printing the codes without sending escapes)
	Tracers[2] = fmt.Sprintf("%s", spec)

	if len(spec.String()) == 0 {
		return ""
	}
	return fmt.Sprintf("%s%sm", Start, spec)

}

func resetIfNeeded(code string) string {
	if len(code) > 0 {
		return Reset
	}
	return ""
}

// Color(s, style) Surrounds `s` with ANSI color and reset code.
func Color(s, style string) string {
	code := ColorCode(style)
	return code + s + resetIfNeeded(code)
}

// ColorFunc Creates a fast closure.
//
// Prefer ColorFunc over Color as it does not recompute ANSI codes.
func ColorFunc(style string) func(string) string {
	if style == "" {
		return func(s string) string {
			return s
		}
	} else {
		code := ColorCode(style)
		return func(s string) string {
			return code + s + resetIfNeeded(code)
		}
	}
}

var ShowRed = ColorFunc("r")
var ShowMagenta = ColorFunc("m")
var ShowGreen = ColorFunc("g")

// DisableColors disables ANSI color codes. On by default.
func DisableColors(disable bool) {
	plain = disable
}
<commit_msg>clean up<commit_after>package cons

import (
	"fmt"
	"regexp"
	"strings"
)

const (
	Start = "\033["
	Reset = "\033[0m"
)

var (
	plain  = false
	colors = map[string][2]string{
		"k": {"30", "40"}, // black
		"r": {"31", "41"}, // red
		"g": {"32", "42"}, // green
		"y": {"33", "43"}, // yellow
		"b": {"34", "44"}, // blue
		"m": {"35", "45"}, // magenta
		"c": {"36", "46"}, // cyan
		"w": {"37", "47"}, // white
	}
	styles = map[string]string{
		"b": "1",  // bold
		"k": "5",  // blink
		"u": "4",  // underline
		"i": "3",  // italic
		"v": "7",  // inverted
		"f": "51", // framed
	}
	// Three diagnostics set after each call to Color()
	// Tracers[0] is three indexes used in parsing the arg to Color():
	//     the first +, the :, and the second +
	// Tracers[1] gives the parsed colors and styles as foreground color, style, background color, style
	// Tracers[2] gives the final ansi control string
	Tracers [3]string
)

func append_code(base string, code string) string {
	if len(code) == 0 {
		return base
	}
	appended := base
	if len(appended) > 0 {
		appended += ";"
	}
	appended += code
	return appended
}

type style_spec struct {
	fg_color string
	fg_style string
	bg_color string
	bg_style string
}

func (spec style_spec) String() string {
	var result string
	result = append_code(result, spec.fg_color)
	result = append_code(result, spec.fg_style)
	result = append_code(result, spec.bg_color)
	result = append_code(result, spec.bg_style)
	return result
}

func IsValid(s string) bool {
	rx := []string{"(?:^[krgbymcw]$)", "(?:^[bkuivf]$)"}
	halves := strings.Split(s, ":")
	for _, h := range halves {
		if len(h) == 0 {
			continue
		}
		quarters := strings.Split(h, "+")
		rxIx := 0
		for _, q := range quarters {
			if len(q) == 0 {
				rxIx++
				continue
			}
			//fmt.Printf("pattern = %s\n", q)
			match, err := regexp.MatchString(rx[rxIx], q)
			if err != nil || !match {
				return false
			}
			rxIx++
		}
	}
	return true
}

// ColorCode returns the ansi control chars for coloring and styling console text
// code should be in UTF-8
func ColorCode(code string) string {
	code = strings.ToLower(code)
	if plain || code == "" || code == "reset" {
		return Reset
	}

	var spec style_spec
	var ptrs = []*string{&spec.fg_color, &spec.fg_style, &spec.bg_color, &spec.bg_style}
	ptrIx := 0
	for _, roon := range code {
		switch string(roon) {
		case ":":
			ptrIx = 2
		case "+":
			ptrIx++
		default:
			*ptrs[ptrIx] = string(roon)
		}
	}
	Tracers[1] = fmt.Sprintf("%s %s %s %s",
		spec.fg_color, spec.fg_style, spec.bg_color, spec.bg_style)
	// now map to the real styles
	spec.fg_color = colors[spec.fg_color][0]
	spec.fg_style = styles[spec.fg_style]
	spec.bg_color = colors[spec.bg_color][1]
	spec.bg_style = styles[spec.bg_style]
	// get the ansi codes without the start and end escapes
	// (useful for printing the codes without sending escapes)
	Tracers[2] = fmt.Sprintf("%s", spec)

	if len(spec.String()) == 0 {
		return ""
	}
	return fmt.Sprintf("%s%sm", Start, spec)

}

func resetIfNeeded(code string) string {
	if len(code) > 0 {
		return Reset
	}
	return ""
}

// Color(s, style) Surrounds `s` with ANSI color and reset code.
func Color(s, style string) string {
	code := ColorCode(style)
	return code + s + resetIfNeeded(code)
}

// ColorFunc Creates a fast closure.
//
// Prefer ColorFunc over Color as it does not recompute ANSI codes.
func ColorFunc(style string) func(string) string {
	if style == "" {
		return func(s string) string {
			return s
		}
	} else {
		code := ColorCode(style)
		return func(s string) string {
			return code + s + resetIfNeeded(code)
		}
	}
}

var ShowRed = ColorFunc("r")
var ShowMagenta = ColorFunc("m")
var ShowGreen = ColorFunc("g")

// DisableColors disables ANSI color codes. On by default.
func DisableColors(disable bool) {
	plain = disable
}

func Printf(code, format string, args ...interface{}) {

}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gorilla/mux"
	"github.com/maraino/go-mock"
	"launchpad.net/gocheck"
	"net/http"
	"github.com/tsuru/config"
	"testing"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { gocheck.TestingT(t) }

type Suite struct {}

var _ = gocheck.Suite(&Suite{})

type LoggerTest struct {
    mock.Mock
}

func (l *LoggerTest) Panicf(format string, v ...interface{}) {
    l.Called(format, )
}

func routeExists(method string, url string) bool {
	var match *mux.RouteMatch = &mux.RouteMatch{}

	router := getRouter()

	request, _ := http.NewRequest(method, url, nil)

	return router.Match(request, match)
}

func (s *Suite) TestRouterHasHealthcheck(c *gocheck.C) {
	c.Assert(routeExists("GET", "/healthcheck"), gocheck.Equals, true)
}

func (s *Suite) TestLoadConfig(c *gocheck.C) {
	logger := &LoggerTest{}
	logger.When("Panicf").Times(0)

	loadConfigFile("../testdata/etc/featness-api1.conf", logger)

	value, errorGetBool := config.GetBool("my_data")
	ok, errorMock := logger.Verify()

	c.Assert(value, gocheck.Equals, true)
	c.Assert(errorGetBool, gocheck.IsNil)
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(errorMock, gocheck.IsNil)
}

func (s *Suite) TestLoadConfigWhenWrongPath(c *gocheck.C) {
	logger := &LoggerTest{}
	logger.When("Panicf", `Could not find featness-api config file. Searched on %s.
	For an example conf check featness-api/etc/featness-api.conf file.\n %s`).Times(1)

	loadConfigFile("wrong-path.conf", logger)

	ok, errorMock := logger.Verify()
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(errorMock, gocheck.IsNil)
}<commit_msg>Improved test to loadConfig<commit_after>package main

import (
	"github.com/gorilla/mux"
	"github.com/maraino/go-mock"
	"launchpad.net/gocheck"
	"net/http"
	"github.com/tsuru/config"
	"testing"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { gocheck.TestingT(t) }

type Suite struct {}

var _ = gocheck.Suite(&Suite{})

type LoggerTest struct {
    mock.Mock
}

func (l *LoggerTest) Panicf(format string, v ...interface{}) {
    l.Called(format, )
}

func routeExists(method string, url string) bool {
	var match *mux.RouteMatch = &mux.RouteMatch{}

	router := getRouter()

	request, _ := http.NewRequest(method, url, nil)

	return router.Match(request, match)
}

func (s *Suite) TestRouterHasHealthcheck(c *gocheck.C) {
	c.Assert(routeExists("GET", "/healthcheck"), gocheck.Equals, true)
}

func (s *Suite) TestLoadConfig(c *gocheck.C) {
	logger := &LoggerTest{}
	logger.When("Panicf").Times(0)

	loadConfigFile("../testdata/etc/featness-api1.conf", logger)

	value, errorGetBool := config.GetBool("my_data")
	ok, errorMock := logger.Verify()

	c.Assert(value, gocheck.Equals, true)
	c.Assert(errorGetBool, gocheck.IsNil)
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(errorMock, gocheck.IsNil)
}

func (s *Suite) TestLoadConfigWhenWrongPath(c *gocheck.C) {
	logger := &LoggerTest{}
	logger.When("Panicf", mock.Any).Times(1)

	loadConfigFile("wrong-path.conf", logger)

	ok, errorMock := logger.Verify()
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(errorMock, gocheck.IsNil)
}<|endoftext|>


<commit_before>// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package fnmatch

import (
	"path/filepath"
	"runtime"
	"testing"
)

type testcase struct {
	pat   string
	name  string
	flags int
	match bool
}

var testcases = []testcase{
	{"", "", 0, true},
	{"*", "", 0, true},
	{"*", "foo", 0, true},
	{"*", "bar", 0, true},
	{"*", "*", 0, true},
	{"**", "f", 0, true},
	{"**", "foo.txt", 0, true},
	{"*.*", "foo.txt", 0, true},
	{"foo*.txt", "foobar.txt", 0, true},
	{"foo.txt", "foo.txt", 0, true},

	{"foo.txt", "bar/foo.txt", 0, false},
	{"*/foo.txt", "bar/foo.txt", 0, true},
	{"f?o.txt", "foo.txt", 0, true},
	{"f?o.txt", "fooo.txt", 0, false},
	{"f[ab]o.txt", "foo.txt", 0, false},
	{"f[ab]o.txt", "fao.txt", 0, true},
	{"f[ab]o.txt", "fbo.txt", 0, true},
	{"f[ab]o.txt", "fco.txt", 0, false},
	{"f[ab]o.txt", "fabo.txt", 0, false},
	{"f[ab]o.txt", "f[ab]o.txt", 0, false},
	{"f\\[ab\\]o.txt", "f[ab]o.txt", NoEscape, false},

	{"*foo.txt", "bar/foo.txt", 0, true},
	{"*foo.txt", "bar/foo.txt", PathName, false},
	{"*/foo.txt", "bar/foo.txt", 0, true},
	{"*/foo.txt", "bar/foo.txt", PathName, true},
	{"*/foo.txt", "bar/baz/foo.txt", 0, true},
	{"*/foo.txt", "bar/baz/foo.txt", PathName, false},
	{"**/foo.txt", "bar/baz/foo.txt", 0, true},
	{"**/foo.txt", "bar/baz/foo.txt", PathName, true},

	{"foo.txt", "foo.TXT", CaseFold, true},
	{"foo.txt", "foo.TXT", 0, false},
	{"(?i)foo.txt", "foo.TXT", 0, true},

	// These characters are literals in glob, but not in regexp.
	{"hey$hello", "hey$hello", 0, true},
	{"hey^hello", "hey^hello", 0, true},
	{"hey{hello", "hey{hello", 0, true},
	{"hey}hello", "hey}hello", 0, true},
	{"hey(hello", "hey(hello", 0, true},
	{"hey)hello", "hey)hello", 0, true},
	{"hey|hello", "hey|hello", 0, true},
	{"hey|hello", "hey|other", 0, false},
}

func TestMatch(t *testing.T) {
	switch runtime.GOOS {
	case "windows":
		testcases = append(testcases, testcase{"foo.txt", "foo.TXT", 0, true})
	case "darwin":
		testcases = append(testcases, testcase{"foo.txt", "foo.TXT", 0, true})
		fallthrough
	default:
		testcases = append(testcases, testcase{"f\\[ab\\]o.txt", "f[ab]o.txt", 0, true})
		testcases = append(testcases, testcase{"foo\\.txt", "foo.txt", 0, true})
		testcases = append(testcases, testcase{"foo\\*.txt", "foo*.txt", 0, true})
		testcases = append(testcases, testcase{"foo\\.txt", "foo.txt", NoEscape, false})
		testcases = append(testcases, testcase{"f\\\\\\[ab\\\\\\]o.txt", "f\\[ab\\]o.txt", 0, true})
	}

	for _, tc := range testcases {
		if m, err := Match(tc.pat, filepath.FromSlash(tc.name), tc.flags); m != tc.match {
			if err != nil {
				t.Error(err)
			} else {
				t.Errorf("Match(%q, %q, %d) != %v", tc.pat, tc.name, tc.flags, tc.match)
			}
		}
	}
}

func TestInvalid(t *testing.T) {
	if _, err := Match("foo[bar", "...", 0); err == nil {
		t.Error("Unexpected nil error")
	}
}
<commit_msg>fnmatch: Test should pass on Mac/windows<commit_after>// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package fnmatch

import (
	"path/filepath"
	"runtime"
	"testing"
)

type testcase struct {
	pat   string
	name  string
	flags int
	match bool
}

var testcases = []testcase{
	{"", "", 0, true},
	{"*", "", 0, true},
	{"*", "foo", 0, true},
	{"*", "bar", 0, true},
	{"*", "*", 0, true},
	{"**", "f", 0, true},
	{"**", "foo.txt", 0, true},
	{"*.*", "foo.txt", 0, true},
	{"foo*.txt", "foobar.txt", 0, true},
	{"foo.txt", "foo.txt", 0, true},

	{"foo.txt", "bar/foo.txt", 0, false},
	{"*/foo.txt", "bar/foo.txt", 0, true},
	{"f?o.txt", "foo.txt", 0, true},
	{"f?o.txt", "fooo.txt", 0, false},
	{"f[ab]o.txt", "foo.txt", 0, false},
	{"f[ab]o.txt", "fao.txt", 0, true},
	{"f[ab]o.txt", "fbo.txt", 0, true},
	{"f[ab]o.txt", "fco.txt", 0, false},
	{"f[ab]o.txt", "fabo.txt", 0, false},
	{"f[ab]o.txt", "f[ab]o.txt", 0, false},
	{"f\\[ab\\]o.txt", "f[ab]o.txt", NoEscape, false},

	{"*foo.txt", "bar/foo.txt", 0, true},
	{"*foo.txt", "bar/foo.txt", PathName, false},
	{"*/foo.txt", "bar/foo.txt", 0, true},
	{"*/foo.txt", "bar/foo.txt", PathName, true},
	{"*/foo.txt", "bar/baz/foo.txt", 0, true},
	{"*/foo.txt", "bar/baz/foo.txt", PathName, false},
	{"**/foo.txt", "bar/baz/foo.txt", 0, true},
	{"**/foo.txt", "bar/baz/foo.txt", PathName, true},

	{"foo.txt", "foo.TXT", CaseFold, true},
	{"(?i)foo.txt", "foo.TXT", 0, true},

	// These characters are literals in glob, but not in regexp.
	{"hey$hello", "hey$hello", 0, true},
	{"hey^hello", "hey^hello", 0, true},
	{"hey{hello", "hey{hello", 0, true},
	{"hey}hello", "hey}hello", 0, true},
	{"hey(hello", "hey(hello", 0, true},
	{"hey)hello", "hey)hello", 0, true},
	{"hey|hello", "hey|hello", 0, true},
	{"hey|hello", "hey|other", 0, false},
}

func TestMatch(t *testing.T) {
	switch runtime.GOOS {
	case "windows", "darwin":
		testcases = append(testcases, testcase{"foo.txt", "foo.TXT", 0, true})
		fallthrough
	default:
		testcases = append(testcases, testcase{"f\\[ab\\]o.txt", "f[ab]o.txt", 0, true})
		testcases = append(testcases, testcase{"foo\\.txt", "foo.txt", 0, true})
		testcases = append(testcases, testcase{"foo\\*.txt", "foo*.txt", 0, true})
		testcases = append(testcases, testcase{"foo\\.txt", "foo.txt", NoEscape, false})
		testcases = append(testcases, testcase{"f\\\\\\[ab\\\\\\]o.txt", "f\\[ab\\]o.txt", 0, true})
	}

	for _, tc := range testcases {
		if m, err := Match(tc.pat, filepath.FromSlash(tc.name), tc.flags); m != tc.match {
			if err != nil {
				t.Error(err)
			} else {
				t.Errorf("Match(%q, %q, %d) != %v", tc.pat, tc.name, tc.flags, tc.match)
			}
		}
	}
}

func TestInvalid(t *testing.T) {
	if _, err := Match("foo[bar", "...", 0); err == nil {
		t.Error("Unexpected nil error")
	}
}
<|endoftext|>


<commit_before>package lfs_test // avoid import cycle

import (
	"fmt"
	"sort"
	"testing"

	. "github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/test"
	"github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/assert"
)

func TestAllCurrentObjectsNone(t *testing.T) {
	repo := test.NewRepo(t)
	repo.Pushd()
	defer func() {
		repo.Popd()
		repo.Cleanup()
	}()

	actual := AllLocalObjects()

	assert.Equal(t, []string{}, actual, "Should be no objects")
}

func TestAllCurrentObjectsSome(t *testing.T) {

	// FFFFFFF why is this creating the same data for every file????????

	repo := test.NewRepo(t)
	repo.Pushd()
	defer func() {
		repo.Popd()
		repo.Cleanup()
	}()

	// We're not testing commits here, just storage, so just create a single
	// commit input with lots of files to generate many oids
	numFiles := 20
	files := make([]*test.FileInput, 0, numFiles)
	for i := 0; i < numFiles; i++ {
		// Must be >=16 bytes for each file to be unique
		files = append(files, &test.FileInput{Filename: fmt.Sprintf("file%d.txt", i), Size: 30})
	}

	inputs := []*test.CommitInput{
		{Files: files},
	}

	outputs := repo.AddCommits(inputs)

	expected := make([]string, 0, numFiles)
	for _, f := range outputs[0].Files {
		expected = append(expected, f.Oid)
	}
	actual := AllLocalObjects()

	// sort to ensure comparison is equal
	sort.Strings(expected)
	sort.Strings(actual)
	assert.Equal(t, expected, actual, "Oids from disk should be the same as in commits")

}
<commit_msg>Test data is fixed now<commit_after>package lfs_test // avoid import cycle

import (
	"fmt"
	"sort"
	"testing"

	. "github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/test"
	"github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/assert"
)

func TestAllCurrentObjectsNone(t *testing.T) {
	repo := test.NewRepo(t)
	repo.Pushd()
	defer func() {
		repo.Popd()
		repo.Cleanup()
	}()

	actual := AllLocalObjects()

	assert.Equal(t, []string{}, actual, "Should be no objects")
}

func TestAllCurrentObjectsSome(t *testing.T) {

	repo := test.NewRepo(t)
	repo.Pushd()
	defer func() {
		repo.Popd()
		repo.Cleanup()
	}()

	// We're not testing commits here, just storage, so just create a single
	// commit input with lots of files to generate many oids
	numFiles := 20
	files := make([]*test.FileInput, 0, numFiles)
	for i := 0; i < numFiles; i++ {
		// Must be >=16 bytes for each file to be unique
		files = append(files, &test.FileInput{Filename: fmt.Sprintf("file%d.txt", i), Size: 30})
	}

	inputs := []*test.CommitInput{
		{Files: files},
	}

	outputs := repo.AddCommits(inputs)

	expected := make([]string, 0, numFiles)
	for _, f := range outputs[0].Files {
		expected = append(expected, f.Oid)
	}
	actual := AllLocalObjects()

	// sort to ensure comparison is equal
	sort.Strings(expected)
	sort.Strings(actual)
	assert.Equal(t, expected, actual, "Oids from disk should be the same as in commits")

}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 restore

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/dag"
	"github.com/jacobsa/comeback/internal/fs"
	. "github.com/jacobsa/ogletest"
	"github.com/jacobsa/timeutil"
)

func TestVisitor(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type VisitorTest struct {
	ctx       context.Context
	blobStore blob.Store

	// A directory that is deleted when the test completes.
	dir string

	// A visitor configured with the above directory.
	visitor dag.Visitor
}

var _ SetUpInterface = &VisitorTest{}
var _ TearDownInterface = &VisitorTest{}

func init() { RegisterTestSuite(&VisitorTest{}) }

func (t *VisitorTest) SetUp(ti *TestInfo) {
	var err error
	t.ctx = ti.Ctx

	// Create the blob store.
	t.blobStore, err = newFakeBlobStore(t.ctx)
	AssertEq(nil, err)

	// Set up the directory.
	t.dir, err = ioutil.TempDir("", "visitor_test")
	AssertEq(nil, err)

	// Create the visitor.
	t.visitor = newVisitor(t.dir, t.blobStore, log.New(ioutil.Discard, "", 0))
}

func (t *VisitorTest) TearDown() {
	var err error

	err = os.RemoveAll(t.dir)
	AssertEq(nil, err)
}

func (t *VisitorTest) call(n *node) (err error) {
	err = t.visitor.Visit(t.ctx, n)
	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *VisitorTest) File_MissingBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_CorruptBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_Empty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_NonEmpty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_PermsAndModTime() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) Directory() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeDirectory,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeDir, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))
}

func (t *VisitorTest) Symlink() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeSymlink,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
			Target:      "taco/burrito",
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeSymlink, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))

	// Readlink
	target, err := os.Readlink(p)

	AssertEq(nil, err)
	ExpectEq("taco/burrito", target)
}

func (t *VisitorTest) ParentDirsAlreadyExist() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeSymlink,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
			Target:      "taco/burrito",
		},
	}

	p := path.Join(t.dir, n.RelPath)

	// Create leading directories.
	err = os.MkdirAll(path.Dir(p), 0700)
	AssertEq(nil, err)

	// Call. Everything should work as usual.
	err = t.call(n)
	AssertEq(nil, err)

	target, err := os.Readlink(p)
	AssertEq(nil, err)
	ExpectEq("taco/burrito", target)
}
<commit_msg>VisitorTest.Directory_AlreadyExists<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 restore

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/dag"
	"github.com/jacobsa/comeback/internal/fs"
	. "github.com/jacobsa/ogletest"
	"github.com/jacobsa/timeutil"
)

func TestVisitor(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type VisitorTest struct {
	ctx       context.Context
	blobStore blob.Store

	// A directory that is deleted when the test completes.
	dir string

	// A visitor configured with the above directory.
	visitor dag.Visitor
}

var _ SetUpInterface = &VisitorTest{}
var _ TearDownInterface = &VisitorTest{}

func init() { RegisterTestSuite(&VisitorTest{}) }

func (t *VisitorTest) SetUp(ti *TestInfo) {
	var err error
	t.ctx = ti.Ctx

	// Create the blob store.
	t.blobStore, err = newFakeBlobStore(t.ctx)
	AssertEq(nil, err)

	// Set up the directory.
	t.dir, err = ioutil.TempDir("", "visitor_test")
	AssertEq(nil, err)

	// Create the visitor.
	t.visitor = newVisitor(t.dir, t.blobStore, log.New(ioutil.Discard, "", 0))
}

func (t *VisitorTest) TearDown() {
	var err error

	err = os.RemoveAll(t.dir)
	AssertEq(nil, err)
}

func (t *VisitorTest) call(n *node) (err error) {
	err = t.visitor.Visit(t.ctx, n)
	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *VisitorTest) File_MissingBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_CorruptBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_Empty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_NonEmpty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_PermsAndModTime() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) Directory() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeDirectory,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeDir, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))
}

func (t *VisitorTest) Directory_AlreadyExists() {
	var err error

	n := &node{
		RelPath: "foo",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeDirectory,
			Name:        "foo",
			Permissions: 0741,
		},
	}

	p := path.Join(t.dir, n.RelPath)

	// Create the directory.
	err = os.Mkdir(p, 0700)
	AssertEq(nil, err)

	// The call should succeed and the permissions should be updated as usual.
	err = t.call(n)
	AssertEq(nil, err)

	fi, err := os.Lstat(p)
	AssertEq(nil, err)
	ExpectEq(0741|os.ModeDir, fi.Mode())
}

func (t *VisitorTest) Symlink() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeSymlink,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
			Target:      "taco/burrito",
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeSymlink, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))

	// Readlink
	target, err := os.Readlink(p)

	AssertEq(nil, err)
	ExpectEq("taco/burrito", target)
}

func (t *VisitorTest) ParentDirsAlreadyExist() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeSymlink,
			Name:        "baz",
			Permissions: 0700,
			Target:      "taco/burrito",
		},
	}

	p := path.Join(t.dir, n.RelPath)

	// Create leading directories.
	err = os.MkdirAll(path.Dir(p), 0700)
	AssertEq(nil, err)

	// Call. Everything should work as usual.
	err = t.call(n)
	AssertEq(nil, err)

	target, err := os.Readlink(p)
	AssertEq(nil, err)
	ExpectEq("taco/burrito", target)
}
<|endoftext|>


<commit_before>// Copyright 2011 Gary Burd
// Copyright 2013 Unknown
//
// 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 doc

import (
	"bytes"
	"encoding/base32"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/doc"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"

	"github.com/Unknwon/gowalker/utils"
	"github.com/astaxie/beego"
	"github.com/russross/blackfriday"
)

type sliceWriter struct{ p *[]byte }

func (w sliceWriter) Write(p []byte) (int, error) {
	*w.p = append(*w.p, p...)
	return len(p), nil
}

func (w *walker) readDir(dir string) ([]os.FileInfo, error) {
	if dir != w.pdoc.ImportPath {
		panic("unexpected")
	}
	fis := make([]os.FileInfo, 0, len(w.srcs))
	for _, src := range w.srcs {
		fis = append(fis, src)
	}
	return fis, nil
}

func (w *walker) openFile(path string) (io.ReadCloser, error) {
	if strings.HasPrefix(path, w.pdoc.ImportPath+"/") {
		if src, ok := w.srcs[path[len(w.pdoc.ImportPath)+1:]]; ok {
			return ioutil.NopCloser(bytes.NewReader(src.data)), nil
		}
	}
	panic("unexpected")
}

func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
	pkg := imports[path]
	if pkg == nil {
		// Guess the package name without importing it. Start with the last
		// element of the path.
		name := path[strings.LastIndex(path, "/")+1:]

		// Trim commonly used prefixes and suffixes containing illegal name
		// runes.
		name = strings.TrimSuffix(name, ".go")
		name = strings.TrimSuffix(name, "-go")
		name = strings.TrimPrefix(name, "go.")
		name = strings.TrimPrefix(name, "go-")
		name = strings.TrimPrefix(name, "biogo.")

		// It's also common for the last element of the path to contain an
		// extra "go" prefix, but not always. TODO: examine unresolved ids to
		// detect when trimming the "go" prefix is appropriate.

		pkg = ast.NewObj(ast.Pkg, name)
		pkg.Data = ast.NewScope(nil)
		imports[path] = pkg
	}
	return pkg, nil
}

func (b *walker) printNode(node interface{}) string {
	b.buf = b.buf[:0]
	err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&b.buf}, b.fset, node)
	if err != nil {
		return err.Error()
	}
	return string(b.buf)
}

func (w *walker) printDecl(decl ast.Node) string {
	var d Code
	d, w.buf = printDecl(decl, w.fset, w.buf)
	return d.Text
}

func (w *walker) printPos(pos token.Pos) string {
	position := w.fset.Position(pos)
	src := w.srcs[position.Filename]
	if src == nil || src.browseURL == "" {
		// src can be nil when line comments are used (//line <file>:<line>).
		return ""
	}
	return src.browseURL + fmt.Sprintf(w.lineFmt, position.Line)
}

func (w *walker) printCode(decl ast.Node) string {
	pos := decl.Pos()
	posPos := w.fset.Position(pos)
	src := w.srcs[posPos.Filename]
	if src == nil || src.browseURL == "" {
		// src can be nil when line comments are used (//line <file>:<line>).
		return ""
	}

	var code []string
	code, ok := w.srcLines[posPos.Filename]
	// Check source file line arrays.
	if !ok {
		w.srcLines[posPos.Filename] = strings.Split(string(src.data), "\n")
		code = w.srcLines[posPos.Filename]
	}

	// Get code.
	var buf bytes.Buffer
	l := len(code)
CutCode:
	for i := posPos.Line; i < l; i++ {
		// Check end of code block.
		switch {
		case len(code[i]) > 0 && code[i][0] == '}': // Normal end.
			break CutCode
		case len(code[i-1]) > 4 && code[i-1][:4] == "func" &&
			code[i-1][len(code[i-1])-1] == '}': // One line functions.
			line := code[i-1]
			buf.WriteString("       ")
			buf.WriteString(line[strings.Index(line, "{")+1 : len(line)-1])
			buf.WriteByte('\n')
			break CutCode
		}

		buf.WriteString(code[i])
		buf.WriteByte('\n')
	}
	return buf.String()
}

func (w *walker) values(vdocs []*doc.Value) []*Value {
	var result []*Value
	for _, d := range vdocs {
		result = append(result, &Value{
			Decl: w.printDecl(d.Decl),
			URL:  w.printPos(d.Decl.Pos()),
			Doc:  d.Doc,
		})
	}
	return result
}

func (w *walker) funcs(fdocs []*doc.Func) []*Func {
	var result []*Func
	for _, d := range fdocs {
		/*	var exampleName string
			switch {
			case d.Recv == "":
				exampleName = d.Name
			case d.Recv[0] == '*':
				exampleName = d.Recv[1:] + "_" + d.Name
			default:
				exampleName = d.Recv + "_" + d.Name
			}*/
		result = append(result, &Func{
			Decl: w.printDecl(d.Decl),
			URL:  w.printPos(d.Decl.Pos()),
			Doc:  d.Doc,
			Name: d.Name,
			Code: w.printCode(d.Decl),
			//Recv: d.Recv,
			//Examples: w.getExamples(exampleName),
		})
	}
	return result
}

func (w *walker) types(tdocs []*doc.Type) []*Type {
	var result []*Type
	for _, d := range tdocs {
		result = append(result, &Type{
			Doc:  d.Doc,
			Name: d.Name,
			Decl: w.printDecl(d.Decl),
			URL:  w.printPos(d.Decl.Pos()),
			//Consts:  w.values(d.Consts),
			//Vars:    w.values(d.Vars),
			Funcs:   w.funcs(d.Funcs),
			Methods: w.funcs(d.Methods),
			//Examples: w.getExamples(d.Name),
		})
	}
	return result
}

/*func (w *walker) notes(gnotes map[string][]*doc.Note) map[string][]*Note {
	if len(gnotes) == 0 {
		return nil
	}
	notes := make(map[string][]*Note)
	for tag, gvalues := range gnotes {
		values := make([]*Note, len(gvalues))
		for i := range gvalues {
			values[i] = &Note{
				URL:  w.printPos(gvalues[i].Pos),
				UID:  gvalues[i].UID,
				Body: strings.TrimSpace(gvalues[i].Body),
			}
		}
		notes[tag] = values
	}
	return notes
}*/

var buildPicPattern = regexp.MustCompile(`\[+!+\[+([a-zA-Z ]*)+\]+\(+[a-zA-z]+://[^\s]*`)

// build generates data from source files.
func (w *walker) build(srcs []*source) (*Package, error) {
	// Set created time.
	w.pdoc.Created = time.Now().UTC()

	// Add source files to walker, I skipped references here.
	w.srcs = make(map[string]*source)
	for _, src := range srcs {
		if strings.HasSuffix(src.name, ".go") {
			w.srcs[src.name] = src
		} else if strings.HasPrefix(strings.ToLower(src.name), "readme") {
			// Readme.
			w.pdoc.Doc = strings.Replace(string(src.data), "=", "", -1)
			if w.pdoc.Doc[0] == '\n' {
				w.pdoc.Doc = w.pdoc.Doc[1:]
			}
			w.pdoc.Doc = w.pdoc.Doc[strings.Index(w.pdoc.Doc, "\n")+1:]
			// Find all picture path of build system.
			for _, m := range buildPicPattern.FindAllString(w.pdoc.Doc, -1) {
				start := strings.Index(m, "http")
				end := strings.Index(m, ")")
				if start > -1 && end > -1 {
					picPath := m[start:end]
					w.pdoc.Doc = strings.Replace(w.pdoc.Doc, m, "![]("+picPath+")", 1)
				}
			}
			w.pdoc.Doc = string(blackfriday.MarkdownCommon([]byte(w.pdoc.Doc)))
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h3>", "h5>", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h2>", "h4>", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h1>", "h3>", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "", "", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "", "", -1)
			w.pdoc.Doc = "
" + w.pdoc.Doc + "
"
		}
	}

	w.fset = token.NewFileSet()

	// Find the package and associated files.
	ctxt := build.Context{
		GOOS:          runtime.GOOS,
		GOARCH:        runtime.GOARCH,
		CgoEnabled:    true,
		JoinPath:      path.Join,
		IsAbsPath:     path.IsAbs,
		SplitPathList: func(list string) []string { return strings.Split(list, ":") },
		IsDir:         func(path string) bool { panic("unexpected") },
		HasSubdir:     func(root, dir string) (rel string, ok bool) { panic("unexpected") },
		ReadDir:       func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) },
		OpenFile:      func(path string) (r io.ReadCloser, err error) { return w.openFile(path) },
		Compiler:      "gc",
	}

	bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0)
	// Continue if there are no Go source files; we still want the directory info.
	_, nogo := err.(*build.NoGoError)
	if err != nil {
		if nogo {
			err = nil
			beego.Info("doc.walker.build(): No Go Source file.")
		} else {
			return w.pdoc, errors.New("doc.walker.build(): " + err.Error())
		}
	}

	// Parse the Go files

	files := make(map[string]*ast.File)
	for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) {
		file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
		if err != nil {
			beego.Error("doc.walker.build():", err)
			continue
		}
		w.pdoc.Files = append(w.pdoc.Files, name)
		//w.pdoc.SourceSize += len(w.srcs[name].data)
		files[name] = file
	}

	apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil)

	// Find examples in the test files.

	/*for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) {
		file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
		if err != nil {
			w.pdoc.Errors = append(w.pdoc.Errors, err.Error())
			continue
		}
		w.pdoc.TestFiles = append(w.pdoc.TestFiles, &File{Name: name, URL: w.srcs[name].browseURL})
		w.pdoc.TestSourceSize += len(w.srcs[name].data)
		w.examples = append(w.examples, doc.Examples(file)...)
	}*/

	//w.vetPackage(apkg)

	mode := doc.Mode(0)
	if w.pdoc.ImportPath == "builtin" {
		mode |= doc.AllDecls
	}

	pdoc := doc.New(apkg, w.pdoc.ImportPath, mode)

	w.pdoc.Synopsis = utils.Synopsis(pdoc.Doc)
	pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")
	var buf bytes.Buffer
	doc.ToHTML(&buf, pdoc.Doc, nil)
	w.pdoc.Doc = w.pdoc.Doc + "
" + buf.String()
	w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "
", "
", 1)
	w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "
", "
", 1)
	w.pdoc.Doc = base32.StdEncoding.EncodeToString([]byte(w.pdoc.Doc))

	//w.pdoc.Examples = w.getExamples("")
	//w.pdoc.IsCmd = bpkg.IsCommand()
	w.pdoc.GOOS = ctxt.GOOS
	w.pdoc.GOARCH = ctxt.GOARCH

	w.srcLines = make(map[string][]string)
	w.pdoc.Consts = w.values(pdoc.Consts)
	w.pdoc.Funcs = w.funcs(pdoc.Funcs)
	w.pdoc.Types = w.types(pdoc.Types)
	w.pdoc.Vars = w.values(pdoc.Vars)
	//w.pdoc.Notes = w.notes(pdoc.Notes)

	w.pdoc.Imports = bpkg.Imports
	w.pdoc.TestImports = bpkg.TestImports
	//w.pdoc.XTestImports = bpkg.XTestImports

	beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine())
	return w.pdoc, err
}
<commit_msg>bug fix<commit_after>// Copyright 2011 Gary Burd
// Copyright 2013 Unknown
//
// 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 doc

import (
	"bytes"
	"encoding/base32"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/doc"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"

	"github.com/Unknwon/gowalker/utils"
	"github.com/astaxie/beego"
	"github.com/russross/blackfriday"
)

type sliceWriter struct{ p *[]byte }

func (w sliceWriter) Write(p []byte) (int, error) {
	*w.p = append(*w.p, p...)
	return len(p), nil
}

func (w *walker) readDir(dir string) ([]os.FileInfo, error) {
	if dir != w.pdoc.ImportPath {
		panic("unexpected")
	}
	fis := make([]os.FileInfo, 0, len(w.srcs))
	for _, src := range w.srcs {
		fis = append(fis, src)
	}
	return fis, nil
}

func (w *walker) openFile(path string) (io.ReadCloser, error) {
	if strings.HasPrefix(path, w.pdoc.ImportPath+"/") {
		if src, ok := w.srcs[path[len(w.pdoc.ImportPath)+1:]]; ok {
			return ioutil.NopCloser(bytes.NewReader(src.data)), nil
		}
	}
	panic("unexpected")
}

func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
	pkg := imports[path]
	if pkg == nil {
		// Guess the package name without importing it. Start with the last
		// element of the path.
		name := path[strings.LastIndex(path, "/")+1:]

		// Trim commonly used prefixes and suffixes containing illegal name
		// runes.
		name = strings.TrimSuffix(name, ".go")
		name = strings.TrimSuffix(name, "-go")
		name = strings.TrimPrefix(name, "go.")
		name = strings.TrimPrefix(name, "go-")
		name = strings.TrimPrefix(name, "biogo.")

		// It's also common for the last element of the path to contain an
		// extra "go" prefix, but not always. TODO: examine unresolved ids to
		// detect when trimming the "go" prefix is appropriate.

		pkg = ast.NewObj(ast.Pkg, name)
		pkg.Data = ast.NewScope(nil)
		imports[path] = pkg
	}
	return pkg, nil
}

func (b *walker) printNode(node interface{}) string {
	b.buf = b.buf[:0]
	err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&b.buf}, b.fset, node)
	if err != nil {
		return err.Error()
	}
	return string(b.buf)
}

func (w *walker) printDecl(decl ast.Node) string {
	var d Code
	d, w.buf = printDecl(decl, w.fset, w.buf)
	return d.Text
}

func (w *walker) printPos(pos token.Pos) string {
	position := w.fset.Position(pos)
	src := w.srcs[position.Filename]
	if src == nil || src.browseURL == "" {
		// src can be nil when line comments are used (//line <file>:<line>).
		return ""
	}
	return src.browseURL + fmt.Sprintf(w.lineFmt, position.Line)
}

func (w *walker) printCode(decl ast.Node) string {
	pos := decl.Pos()
	posPos := w.fset.Position(pos)
	src := w.srcs[posPos.Filename]
	if src == nil || src.browseURL == "" {
		// src can be nil when line comments are used (//line <file>:<line>).
		return ""
	}

	var code []string
	code, ok := w.srcLines[posPos.Filename]
	// Check source file line arrays.
	if !ok {
		w.srcLines[posPos.Filename] = strings.Split(string(src.data), "\n")
		code = w.srcLines[posPos.Filename]
	}

	// Get code.
	var buf bytes.Buffer
	l := len(code)
CutCode:
	for i := posPos.Line; i < l; i++ {
		// Check end of code block.
		switch {
		case len(code[i]) > 0 && code[i][0] == '}': // Normal end.
			break CutCode
		case len(code[i-1]) > 4 && code[i-1][:4] == "func" &&
			code[i-1][len(code[i-1])-1] == '}': // One line functions.
			line := code[i-1]
			buf.WriteString("       ")
			buf.WriteString(line[strings.Index(line, "{")+1 : len(line)-1])
			buf.WriteByte('\n')
			break CutCode
		}

		buf.WriteString(code[i])
		buf.WriteByte('\n')
	}
	return buf.String()
}

func (w *walker) values(vdocs []*doc.Value) []*Value {
	var result []*Value
	for _, d := range vdocs {
		result = append(result, &Value{
			Decl: w.printDecl(d.Decl),
			URL:  w.printPos(d.Decl.Pos()),
			Doc:  d.Doc,
		})
	}
	return result
}

func (w *walker) funcs(fdocs []*doc.Func) []*Func {
	var result []*Func
	for _, d := range fdocs {
		/*	var exampleName string
			switch {
			case d.Recv == "":
				exampleName = d.Name
			case d.Recv[0] == '*':
				exampleName = d.Recv[1:] + "_" + d.Name
			default:
				exampleName = d.Recv + "_" + d.Name
			}*/
		result = append(result, &Func{
			Decl: w.printDecl(d.Decl),
			URL:  w.printPos(d.Decl.Pos()),
			Doc:  d.Doc,
			Name: d.Name,
			Code: w.printCode(d.Decl),
			//Recv: d.Recv,
			//Examples: w.getExamples(exampleName),
		})
	}
	return result
}

func (w *walker) types(tdocs []*doc.Type) []*Type {
	var result []*Type
	for _, d := range tdocs {
		result = append(result, &Type{
			Doc:  d.Doc,
			Name: d.Name,
			Decl: w.printDecl(d.Decl),
			URL:  w.printPos(d.Decl.Pos()),
			//Consts:  w.values(d.Consts),
			//Vars:    w.values(d.Vars),
			Funcs:   w.funcs(d.Funcs),
			Methods: w.funcs(d.Methods),
			//Examples: w.getExamples(d.Name),
		})
	}
	return result
}

var buildPicPattern = regexp.MustCompile(`\[+!+\[+([a-zA-Z ]*)+\]+\(+[a-zA-z]+://[^\s]*`)

// build generates data from source files.
func (w *walker) build(srcs []*source) (*Package, error) {
	// Set created time.
	w.pdoc.Created = time.Now().UTC()

	// Add source files to walker, I skipped references here.
	w.srcs = make(map[string]*source)
	for _, src := range srcs {
		if strings.HasSuffix(src.name, ".go") {
			w.srcs[src.name] = src
		} else if strings.HasPrefix(strings.ToLower(src.name), "readme") {
			// Readme.
			w.pdoc.Doc = strings.Replace(string(src.data), "=", "", -1)
			if w.pdoc.Doc[0] == '\n' {
				w.pdoc.Doc = w.pdoc.Doc[1:]
			}
			w.pdoc.Doc = w.pdoc.Doc[strings.Index(w.pdoc.Doc, "\n")+1:]
			// Find all picture path of build system.
			for _, m := range buildPicPattern.FindAllString(w.pdoc.Doc, -1) {
				start := strings.Index(m, "http")
				end := strings.Index(m, ")")
				if (start > -1) && (end > -1) && (start < end) {
					picPath := m[start:end]
					w.pdoc.Doc = strings.Replace(w.pdoc.Doc, m, "![]("+picPath+")", 1)
				}
			}
			w.pdoc.Doc = string(blackfriday.MarkdownCommon([]byte(w.pdoc.Doc)))
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h3>", "h5>", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h2>", "h4>", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h1>", "h3>", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "", "", -1)
			w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "", "", -1)
			w.pdoc.Doc = "
" + w.pdoc.Doc + "
"
		}
	}

	w.fset = token.NewFileSet()

	// Find the package and associated files.
	ctxt := build.Context{
		GOOS:          runtime.GOOS,
		GOARCH:        runtime.GOARCH,
		CgoEnabled:    true,
		JoinPath:      path.Join,
		IsAbsPath:     path.IsAbs,
		SplitPathList: func(list string) []string { return strings.Split(list, ":") },
		IsDir:         func(path string) bool { panic("unexpected") },
		HasSubdir:     func(root, dir string) (rel string, ok bool) { panic("unexpected") },
		ReadDir:       func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) },
		OpenFile:      func(path string) (r io.ReadCloser, err error) { return w.openFile(path) },
		Compiler:      "gc",
	}

	bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0)
	// Continue if there are no Go source files; we still want the directory info.
	_, nogo := err.(*build.NoGoError)
	if err != nil {
		if nogo {
			err = nil
			beego.Info("doc.walker.build(): No Go Source file.")
		} else {
			return w.pdoc, errors.New("doc.walker.build(): " + err.Error())
		}
	}

	// Parse the Go files

	files := make(map[string]*ast.File)
	for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) {
		file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
		if err != nil {
			beego.Error("doc.walker.build():", err)
			continue
		}
		w.pdoc.Files = append(w.pdoc.Files, name)
		//w.pdoc.SourceSize += len(w.srcs[name].data)
		files[name] = file
	}

	apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil)

	// Find examples in the test files.

	/*for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) {
		file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
		if err != nil {
			w.pdoc.Errors = append(w.pdoc.Errors, err.Error())
			continue
		}
		w.pdoc.TestFiles = append(w.pdoc.TestFiles, &File{Name: name, URL: w.srcs[name].browseURL})
		w.pdoc.TestSourceSize += len(w.srcs[name].data)
		w.examples = append(w.examples, doc.Examples(file)...)
	}*/

	//w.vetPackage(apkg)

	mode := doc.Mode(0)
	if w.pdoc.ImportPath == "builtin" {
		mode |= doc.AllDecls
	}

	pdoc := doc.New(apkg, w.pdoc.ImportPath, mode)

	w.pdoc.Synopsis = utils.Synopsis(pdoc.Doc)
	pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")
	var buf bytes.Buffer
	doc.ToHTML(&buf, pdoc.Doc, nil)
	w.pdoc.Doc = w.pdoc.Doc + "
" + buf.String()
	w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "
", "
", 1)
	w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "
", "
", 1)
	w.pdoc.Doc = base32.StdEncoding.EncodeToString([]byte(w.pdoc.Doc))

	//w.pdoc.Examples = w.getExamples("")
	//w.pdoc.IsCmd = bpkg.IsCommand()
	w.pdoc.GOOS = ctxt.GOOS
	w.pdoc.GOARCH = ctxt.GOARCH

	w.srcLines = make(map[string][]string)
	w.pdoc.Consts = w.values(pdoc.Consts)
	w.pdoc.Funcs = w.funcs(pdoc.Funcs)
	w.pdoc.Types = w.types(pdoc.Types)
	w.pdoc.Vars = w.values(pdoc.Vars)
	//w.pdoc.Notes = w.notes(pdoc.Notes)

	w.pdoc.Imports = bpkg.Imports
	w.pdoc.TestImports = bpkg.TestImports
	//w.pdoc.XTestImports = bpkg.XTestImports

	beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine())
	return w.pdoc, err
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package keystore

import (
	"errors"
	"fmt"
	"net/http"
	"sync"

	"github.com/gorilla/rpc/v2"

	"github.com/ava-labs/gecko/chains/atomic"
	"github.com/ava-labs/gecko/database"
	"github.com/ava-labs/gecko/database/encdb"
	"github.com/ava-labs/gecko/database/prefixdb"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/engine/common"
	"github.com/ava-labs/gecko/utils/formatting"
	"github.com/ava-labs/gecko/utils/logging"
	"github.com/ava-labs/gecko/vms/components/codec"

	jsoncodec "github.com/ava-labs/gecko/utils/json"
	zxcvbn "github.com/nbutton23/zxcvbn-go"
)

const (
	// maxUserPassLen is the maximum length of the username or password allowed
	maxUserPassLen = 1024

	// requiredPassScore defines the score a password must achieve to be accepted
	// as a password with strong characteristics by the zxcvbn package
	//
	// The scoring mechanism defined is as follows;
	//
	// 0 # too guessable: risky password. (guesses < 10^3)
	// 1 # very guessable: protection from throttled online attacks. (guesses < 10^6)
	// 2 # somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)
	// 3 # safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)
	// 4 # very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)
	requiredPassScore = 2
)

var (
	errEmptyUsername     = errors.New("username can't be the empty string")
	errUserPassMaxLength = fmt.Errorf("CreateUser call rejected due to username or password exceeding maximum length of %d chars", maxUserPassLen)
	errWeakPassword      = errors.New("Failed to create user as the given password is too weak. A stronger password is one of 8 or more characters containing attributes of upper and lowercase letters, numbers, and/or special characters")
)

// KeyValuePair ...
type KeyValuePair struct {
	Key   []byte `serialize:"true"`
	Value []byte `serialize:"true"`
}

// UserDB describes the full content of a user
type UserDB struct {
	User `serialize:"true"`
	Data []KeyValuePair `serialize:"true"`
}

// Keystore is the RPC interface for keystore management
type Keystore struct {
	lock sync.Mutex
	log  logging.Logger

	codec codec.Codec

	// Key: username
	// Value: The user with that name
	users map[string]*User

	// Used to persist users and their data
	userDB database.Database
	bcDB   database.Database
	//           BaseDB
	//          /      \
	//    UserDB        BlockchainDB
	//                 /      |     \
	//               Usr     Usr    Usr
	//            /   |   \
	//          BID  BID  BID
}

// Initialize the keystore
func (ks *Keystore) Initialize(log logging.Logger, db database.Database) {
	ks.log = log
	ks.codec = codec.NewDefault()
	ks.users = make(map[string]*User)
	ks.userDB = prefixdb.New([]byte("users"), db)
	ks.bcDB = prefixdb.New([]byte("bcs"), db)
}

// CreateHandler returns a new service object that can send requests to thisAPI.
func (ks *Keystore) CreateHandler() *common.HTTPHandler {
	newServer := rpc.NewServer()
	codec := jsoncodec.NewCodec()
	newServer.RegisterCodec(codec, "application/json")
	newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
	newServer.RegisterService(ks, "keystore")
	return &common.HTTPHandler{LockOptions: common.NoLock, Handler: newServer}
}

// Get the user whose name is [username]
func (ks *Keystore) getUser(username string) (*User, error) {
	// If the user is already in memory, return it
	usr, exists := ks.users[username]
	if exists {
		return usr, nil
	}
	// The user is not in memory; try the database
	usrBytes, err := ks.userDB.Get([]byte(username))
	if err != nil { // Most likely bc user doesn't exist in database
		return nil, err
	}

	usr = &User{}
	return usr, ks.codec.Unmarshal(usrBytes, usr)
}

// CreateUserArgs are arguments for passing into CreateUser requests
type CreateUserArgs struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// CreateUserReply is the response from calling CreateUser
type CreateUserReply struct {
	Success bool `json:"success"`
}

// CreateUser creates an empty user with the provided username and password
func (ks *Keystore) CreateUser(_ *http.Request, args *CreateUserArgs, reply *CreateUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("CreateUser called with %.*s", maxUserPassLen, args.Username)

	if len(args.Username) > maxUserPassLen || len(args.Password) > maxUserPassLen {
		return errUserPassMaxLength
	}

	if args.Username == "" {
		return errEmptyUsername
	}
	if usr, err := ks.getUser(args.Username); err == nil || usr != nil {
		return fmt.Errorf("user already exists: %s", args.Username)
	}

	if zxcvbn.PasswordStrength(args.Password, nil).Score < requiredPassScore {
		return errWeakPassword
	}

	usr := &User{}
	if err := usr.Initialize(args.Password); err != nil {
		return err
	}

	usrBytes, err := ks.codec.Marshal(usr)
	if err != nil {
		return err
	}

	if err := ks.userDB.Put([]byte(args.Username), usrBytes); err != nil {
		return err
	}
	ks.users[args.Username] = usr
	reply.Success = true
	return nil
}

// ListUsersArgs are the arguments to ListUsers
type ListUsersArgs struct{}

// ListUsersReply is the reply from ListUsers
type ListUsersReply struct {
	Users []string `json:"users"`
}

// ListUsers lists all the registered usernames
func (ks *Keystore) ListUsers(_ *http.Request, args *ListUsersArgs, reply *ListUsersReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("ListUsers called")

	reply.Users = []string{}

	it := ks.userDB.NewIterator()
	defer it.Release()
	for it.Next() {
		reply.Users = append(reply.Users, string(it.Key()))
	}
	return it.Error()
}

// ExportUserArgs are the arguments to ExportUser
type ExportUserArgs struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// ExportUserReply is the reply from ExportUser
type ExportUserReply struct {
	User formatting.CB58 `json:"user"`
}

// ExportUser exports a serialized encoding of a user's information complete with encrypted database values
func (ks *Keystore) ExportUser(_ *http.Request, args *ExportUserArgs, reply *ExportUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("ExportUser called for %s", args.Username)

	usr, err := ks.getUser(args.Username)
	if err != nil {
		return err
	}
	if !usr.CheckPassword(args.Password) {
		return fmt.Errorf("incorrect password for user %q", args.Username)
	}

	userDB := prefixdb.New([]byte(args.Username), ks.bcDB)

	userData := UserDB{
		User: *usr,
	}

	it := userDB.NewIterator()
	defer it.Release()
	for it.Next() {
		userData.Data = append(userData.Data, KeyValuePair{
			Key:   it.Key(),
			Value: it.Value(),
		})
	}
	if err := it.Error(); err != nil {
		return err
	}

	b, err := ks.codec.Marshal(&userData)
	if err != nil {
		return err
	}
	reply.User.Bytes = b
	return nil
}

// ImportUserArgs are arguments for ImportUser
type ImportUserArgs struct {
	Username string          `json:"username"`
	Password string          `json:"password"`
	User     formatting.CB58 `json:"user"`
}

// ImportUserReply is the response for ImportUser
type ImportUserReply struct {
	Success bool `json:"success"`
}

// ImportUser imports a serialized encoding of a user's information complete with encrypted database values, integrity checks the password, and adds it to the database
func (ks *Keystore) ImportUser(r *http.Request, args *ImportUserArgs, reply *ImportUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("ImportUser called for %s", args.Username)

	if usr, err := ks.getUser(args.Username); err == nil || usr != nil {
		return fmt.Errorf("user already exists: %s", args.Username)
	}

	userData := UserDB{}
	if err := ks.codec.Unmarshal(args.User.Bytes, &userData); err != nil {
		return err
	}
	if !userData.User.CheckPassword(args.Password) {
		return fmt.Errorf("incorrect password for user %q", args.Username)
	}

	usrBytes, err := ks.codec.Marshal(&userData.User)
	if err != nil {
		return err
	}

	userBatch := ks.userDB.NewBatch()
	if err := userBatch.Put([]byte(args.Username), usrBytes); err != nil {
		return err
	}

	userDataDB := prefixdb.New([]byte(args.Username), ks.bcDB)
	dataBatch := userDataDB.NewBatch()
	for _, kvp := range userData.Data {
		dataBatch.Put(kvp.Key, kvp.Value)
	}

	if err := atomic.WriteAll(dataBatch, userBatch); err != nil {
		return err
	}

	ks.users[args.Username] = &userData.User

	reply.Success = true
	return nil
}

// DeleteUserArgs are arguments for passing into DeleteUser requests
type DeleteUserArgs struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// DeleteUserReply is the response from calling DeleteUser
type DeleteUserReply struct {
	Success bool `json:"success"`
}

// DeleteUser deletes user with the provided username and password.
func (ks *Keystore) DeleteUser(_ *http.Request, args *DeleteUserArgs, reply *DeleteUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("DeleteUser called with %s", args.Username)

	if args.Username == "" {
		return errEmptyUsername
	}

	// check if user exists and valid user.
	usr, err := ks.getUser(args.Username)
	switch {
	case err != nil || usr == nil:
		return fmt.Errorf("user doesn't exist: %s", args.Username)
	case !usr.CheckPassword(args.Password):
		return fmt.Errorf("incorrect password for user %q", args.Username)
	}

	userNameBytes := []byte(args.Username)
	userBatch := ks.userDB.NewBatch()
	if err := userBatch.Delete(userNameBytes); err != nil {
		return err
	}

	userDataDB := prefixdb.New(userNameBytes, ks.bcDB)
	dataBatch := userDataDB.NewBatch()

	it := userDataDB.NewIterator()
	defer it.Release()

	for it.Next() {
		if err = dataBatch.Delete(it.Key()); err != nil {
			return err
		}
	}

	if err = it.Error(); err != nil {
		return err
	}

	if err := atomic.WriteAll(dataBatch, userBatch); err != nil {
		return err
	}

	// delete from users map.
	delete(ks.users, args.Username)

	reply.Success = true
	return nil
}

// NewBlockchainKeyStore ...
func (ks *Keystore) NewBlockchainKeyStore(blockchainID ids.ID) *BlockchainKeystore {
	return &BlockchainKeystore{
		blockchainID: blockchainID,
		ks:           ks,
	}
}

// GetDatabase ...
func (ks *Keystore) GetDatabase(bID ids.ID, username, password string) (database.Database, error) {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	usr, err := ks.getUser(username)
	if err != nil {
		return nil, err
	}
	if !usr.CheckPassword(password) {
		return nil, fmt.Errorf("incorrect password for user %q", username)
	}

	userDB := prefixdb.New([]byte(username), ks.bcDB)
	bcDB := prefixdb.NewNested(bID.Bytes(), userDB)
	encDB, err := encdb.New([]byte(password), bcDB)

	if err != nil {
		return nil, err
	}

	return encDB, nil
}
<commit_msg>fix: Add a check on username in importUser<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package keystore

import (
	"errors"
	"fmt"
	"net/http"
	"sync"

	"github.com/gorilla/rpc/v2"

	"github.com/ava-labs/gecko/chains/atomic"
	"github.com/ava-labs/gecko/database"
	"github.com/ava-labs/gecko/database/encdb"
	"github.com/ava-labs/gecko/database/prefixdb"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/engine/common"
	"github.com/ava-labs/gecko/utils/formatting"
	"github.com/ava-labs/gecko/utils/logging"
	"github.com/ava-labs/gecko/vms/components/codec"

	jsoncodec "github.com/ava-labs/gecko/utils/json"
	zxcvbn "github.com/nbutton23/zxcvbn-go"
)

const (
	// maxUserPassLen is the maximum length of the username or password allowed
	maxUserPassLen = 1024

	// requiredPassScore defines the score a password must achieve to be accepted
	// as a password with strong characteristics by the zxcvbn package
	//
	// The scoring mechanism defined is as follows;
	//
	// 0 # too guessable: risky password. (guesses < 10^3)
	// 1 # very guessable: protection from throttled online attacks. (guesses < 10^6)
	// 2 # somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)
	// 3 # safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)
	// 4 # very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)
	requiredPassScore = 2
)

var (
	errEmptyUsername     = errors.New("username can't be the empty string")
	errUserPassMaxLength = fmt.Errorf("CreateUser call rejected due to username or password exceeding maximum length of %d chars", maxUserPassLen)
	errWeakPassword      = errors.New("Failed to create user as the given password is too weak. A stronger password is one of 8 or more characters containing attributes of upper and lowercase letters, numbers, and/or special characters")
)

// KeyValuePair ...
type KeyValuePair struct {
	Key   []byte `serialize:"true"`
	Value []byte `serialize:"true"`
}

// UserDB describes the full content of a user
type UserDB struct {
	User `serialize:"true"`
	Data []KeyValuePair `serialize:"true"`
}

// Keystore is the RPC interface for keystore management
type Keystore struct {
	lock sync.Mutex
	log  logging.Logger

	codec codec.Codec

	// Key: username
	// Value: The user with that name
	users map[string]*User

	// Used to persist users and their data
	userDB database.Database
	bcDB   database.Database
	//           BaseDB
	//          /      \
	//    UserDB        BlockchainDB
	//                 /      |     \
	//               Usr     Usr    Usr
	//            /   |   \
	//          BID  BID  BID
}

// Initialize the keystore
func (ks *Keystore) Initialize(log logging.Logger, db database.Database) {
	ks.log = log
	ks.codec = codec.NewDefault()
	ks.users = make(map[string]*User)
	ks.userDB = prefixdb.New([]byte("users"), db)
	ks.bcDB = prefixdb.New([]byte("bcs"), db)
}

// CreateHandler returns a new service object that can send requests to thisAPI.
func (ks *Keystore) CreateHandler() *common.HTTPHandler {
	newServer := rpc.NewServer()
	codec := jsoncodec.NewCodec()
	newServer.RegisterCodec(codec, "application/json")
	newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
	newServer.RegisterService(ks, "keystore")
	return &common.HTTPHandler{LockOptions: common.NoLock, Handler: newServer}
}

// Get the user whose name is [username]
func (ks *Keystore) getUser(username string) (*User, error) {
	// If the user is already in memory, return it
	usr, exists := ks.users[username]
	if exists {
		return usr, nil
	}
	// The user is not in memory; try the database
	usrBytes, err := ks.userDB.Get([]byte(username))
	if err != nil { // Most likely bc user doesn't exist in database
		return nil, err
	}

	usr = &User{}
	return usr, ks.codec.Unmarshal(usrBytes, usr)
}

// CreateUserArgs are arguments for passing into CreateUser requests
type CreateUserArgs struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// CreateUserReply is the response from calling CreateUser
type CreateUserReply struct {
	Success bool `json:"success"`
}

// CreateUser creates an empty user with the provided username and password
func (ks *Keystore) CreateUser(_ *http.Request, args *CreateUserArgs, reply *CreateUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("CreateUser called with %.*s", maxUserPassLen, args.Username)

	if len(args.Username) > maxUserPassLen || len(args.Password) > maxUserPassLen {
		return errUserPassMaxLength
	}

	if args.Username == "" {
		return errEmptyUsername
	}
	if usr, err := ks.getUser(args.Username); err == nil || usr != nil {
		return fmt.Errorf("user already exists: %s", args.Username)
	}

	if zxcvbn.PasswordStrength(args.Password, nil).Score < requiredPassScore {
		return errWeakPassword
	}

	usr := &User{}
	if err := usr.Initialize(args.Password); err != nil {
		return err
	}

	usrBytes, err := ks.codec.Marshal(usr)
	if err != nil {
		return err
	}

	if err := ks.userDB.Put([]byte(args.Username), usrBytes); err != nil {
		return err
	}
	ks.users[args.Username] = usr
	reply.Success = true
	return nil
}

// ListUsersArgs are the arguments to ListUsers
type ListUsersArgs struct{}

// ListUsersReply is the reply from ListUsers
type ListUsersReply struct {
	Users []string `json:"users"`
}

// ListUsers lists all the registered usernames
func (ks *Keystore) ListUsers(_ *http.Request, args *ListUsersArgs, reply *ListUsersReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("ListUsers called")

	reply.Users = []string{}

	it := ks.userDB.NewIterator()
	defer it.Release()
	for it.Next() {
		reply.Users = append(reply.Users, string(it.Key()))
	}
	return it.Error()
}

// ExportUserArgs are the arguments to ExportUser
type ExportUserArgs struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// ExportUserReply is the reply from ExportUser
type ExportUserReply struct {
	User formatting.CB58 `json:"user"`
}

// ExportUser exports a serialized encoding of a user's information complete with encrypted database values
func (ks *Keystore) ExportUser(_ *http.Request, args *ExportUserArgs, reply *ExportUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("ExportUser called for %s", args.Username)

	usr, err := ks.getUser(args.Username)
	if err != nil {
		return err
	}
	if !usr.CheckPassword(args.Password) {
		return fmt.Errorf("incorrect password for user %q", args.Username)
	}

	userDB := prefixdb.New([]byte(args.Username), ks.bcDB)

	userData := UserDB{
		User: *usr,
	}

	it := userDB.NewIterator()
	defer it.Release()
	for it.Next() {
		userData.Data = append(userData.Data, KeyValuePair{
			Key:   it.Key(),
			Value: it.Value(),
		})
	}
	if err := it.Error(); err != nil {
		return err
	}

	b, err := ks.codec.Marshal(&userData)
	if err != nil {
		return err
	}
	reply.User.Bytes = b
	return nil
}

// ImportUserArgs are arguments for ImportUser
type ImportUserArgs struct {
	Username string          `json:"username"`
	Password string          `json:"password"`
	User     formatting.CB58 `json:"user"`
}

// ImportUserReply is the response for ImportUser
type ImportUserReply struct {
	Success bool `json:"success"`
}

// ImportUser imports a serialized encoding of a user's information complete with encrypted database values, integrity checks the password, and adds it to the database
func (ks *Keystore) ImportUser(r *http.Request, args *ImportUserArgs, reply *ImportUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("ImportUser called for %s", args.Username)

	if args.Username == "" {
		return errEmptyUsername
	}

	if usr, err := ks.getUser(args.Username); err == nil || usr != nil {
		return fmt.Errorf("user already exists: %s", args.Username)
	}

	userData := UserDB{}
	if err := ks.codec.Unmarshal(args.User.Bytes, &userData); err != nil {
		return err
	}
	if !userData.User.CheckPassword(args.Password) {
		return fmt.Errorf("incorrect password for user %q", args.Username)
	}

	usrBytes, err := ks.codec.Marshal(&userData.User)
	if err != nil {
		return err
	}

	userBatch := ks.userDB.NewBatch()
	if err := userBatch.Put([]byte(args.Username), usrBytes); err != nil {
		return err
	}

	userDataDB := prefixdb.New([]byte(args.Username), ks.bcDB)
	dataBatch := userDataDB.NewBatch()
	for _, kvp := range userData.Data {
		dataBatch.Put(kvp.Key, kvp.Value)
	}

	if err := atomic.WriteAll(dataBatch, userBatch); err != nil {
		return err
	}

	ks.users[args.Username] = &userData.User

	reply.Success = true
	return nil
}

// DeleteUserArgs are arguments for passing into DeleteUser requests
type DeleteUserArgs struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// DeleteUserReply is the response from calling DeleteUser
type DeleteUserReply struct {
	Success bool `json:"success"`
}

// DeleteUser deletes user with the provided username and password.
func (ks *Keystore) DeleteUser(_ *http.Request, args *DeleteUserArgs, reply *DeleteUserReply) error {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	ks.log.Verbo("DeleteUser called with %s", args.Username)

	if args.Username == "" {
		return errEmptyUsername
	}

	// check if user exists and valid user.
	usr, err := ks.getUser(args.Username)
	switch {
	case err != nil || usr == nil:
		return fmt.Errorf("user doesn't exist: %s", args.Username)
	case !usr.CheckPassword(args.Password):
		return fmt.Errorf("incorrect password for user %q", args.Username)
	}

	userNameBytes := []byte(args.Username)
	userBatch := ks.userDB.NewBatch()
	if err := userBatch.Delete(userNameBytes); err != nil {
		return err
	}

	userDataDB := prefixdb.New(userNameBytes, ks.bcDB)
	dataBatch := userDataDB.NewBatch()

	it := userDataDB.NewIterator()
	defer it.Release()

	for it.Next() {
		if err = dataBatch.Delete(it.Key()); err != nil {
			return err
		}
	}

	if err = it.Error(); err != nil {
		return err
	}

	if err := atomic.WriteAll(dataBatch, userBatch); err != nil {
		return err
	}

	// delete from users map.
	delete(ks.users, args.Username)

	reply.Success = true
	return nil
}

// NewBlockchainKeyStore ...
func (ks *Keystore) NewBlockchainKeyStore(blockchainID ids.ID) *BlockchainKeystore {
	return &BlockchainKeystore{
		blockchainID: blockchainID,
		ks:           ks,
	}
}

// GetDatabase ...
func (ks *Keystore) GetDatabase(bID ids.ID, username, password string) (database.Database, error) {
	ks.lock.Lock()
	defer ks.lock.Unlock()

	usr, err := ks.getUser(username)
	if err != nil {
		return nil, err
	}
	if !usr.CheckPassword(password) {
		return nil, fmt.Errorf("incorrect password for user %q", username)
	}

	userDB := prefixdb.New([]byte(username), ks.bcDB)
	bcDB := prefixdb.NewNested(bID.Bytes(), userDB)
	encDB, err := encdb.New([]byte(password), bcDB)

	if err != nil {
		return nil, err
	}

	return encDB, nil
}
<|endoftext|>


<commit_before>package apiservermsgs

/*
Copyright 2019 Crunchy Data Solutions, Inc.
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.
*/

import ()

const PGO_VERSION = "3.5.2"

// Ok status
const Ok = "ok"
const Error = "error"

// Status ...
type Status struct {
	Code string
	Msg  string
}

type BasicAuthCredentials struct {
	Username     string
	Password     string
	APIServerURL string
}
<commit_msg>update version to 3.5.2-rc3<commit_after>package apiservermsgs

/*
Copyright 2019 Crunchy Data Solutions, Inc.
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.
*/

import ()

const PGO_VERSION = "3.5.2-rc3"

// Ok status
const Ok = "ok"
const Error = "error"

// Status ...
type Status struct {
	Code string
	Msg  string
}

type BasicAuthCredentials struct {
	Username     string
	Password     string
	APIServerURL string
}
<|endoftext|>