<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package keybase

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"runtime/trace"
	"sync"
	"time"

	"strings"

	"github.com/keybase/client/go/externals"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/service"
	"github.com/keybase/client/go/uidmap"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"github.com/keybase/kbfs/env"
	"github.com/keybase/kbfs/fsrpc"
	"github.com/keybase/kbfs/libgit"
	"github.com/keybase/kbfs/libhttpserver"
	"github.com/keybase/kbfs/libkbfs"
	"github.com/keybase/kbfs/simplefs"
)

var kbCtx *libkb.GlobalContext
var conn net.Conn
var startOnce sync.Once
var logSendContext libkb.LogSendContext
var kbfsConfig libkbfs.Config

type ExternalDNSNSFetcher interface {
	GetServers() []byte
}

type dnsNSFetcher struct {
	externalFetcher ExternalDNSNSFetcher
}

func newDNSNSFetcher(d ExternalDNSNSFetcher) dnsNSFetcher {
	return dnsNSFetcher{
		externalFetcher: d,
	}
}

func (d dnsNSFetcher) processExternalResult(raw []byte) []string {
	return strings.Split(string(raw), ",")
}

func (d dnsNSFetcher) GetServers() []string {
	if d.externalFetcher != nil {
		return d.processExternalResult(d.externalFetcher.GetServers())
	}
	return getDNSServers()
}

var _ libkb.DNSNameServerFetcher = dnsNSFetcher{}

// InitOnce runs the Keybase services (only runs one time)
func InitOnce(homeDir string, logFile string, runModeStr string, accessGroupOverride bool,
	dnsNSFetcher ExternalDNSNSFetcher) {
	startOnce.Do(func() {
		if err := Init(homeDir, logFile, runModeStr, accessGroupOverride, dnsNSFetcher); err != nil {
			kbCtx.Log.Errorf("Init error: %s", err)
		}
	})
}

// Init runs the Keybase services
func Init(homeDir string, logFile string, runModeStr string, accessGroupOverride bool,
	externalDNSNSFetcher ExternalDNSNSFetcher) error {
	fmt.Println("Go: Initializing")
	if logFile != "" {
		fmt.Printf("Go: Using log: %s\n", logFile)
	}

	// Reduce OS threads on mobile so we don't have too much contention with JS thread
	oldProcs := runtime.GOMAXPROCS(0)
	newProcs := oldProcs / 2
	runtime.GOMAXPROCS(newProcs)
	fmt.Printf("Go: setting GOMAXPROCS to: %d previous: %d\n", newProcs, oldProcs)

	startTrace(logFile)

	dnsNSFetcher := newDNSNSFetcher(externalDNSNSFetcher)
	dnsServers := dnsNSFetcher.GetServers()
	for _, srv := range dnsServers {
		fmt.Printf("Go: DNS Server: %s\n", srv)
	}

	kbCtx = libkb.NewGlobalContext()
	kbCtx.Init()
	kbCtx.SetServices(externals.GetServices())

	// 10k uid -> FullName cache entries allowed
	kbCtx.SetUIDMapper(uidmap.NewUIDMap(10000))
	usage := libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
	runMode, err := libkb.StringToRunMode(runModeStr)
	if err != nil {
		return err
	}
	config := libkb.AppConfig{
		HomeDir:                        homeDir,
		LogFile:                        logFile,
		RunMode:                        runMode,
		Debug:                          true,
		LocalRPCDebug:                  "",
		VDebugSetting:                  "mobile", // use empty string for same logging as desktop default
		SecurityAccessGroupOverride:    accessGroupOverride,
		ChatInboxSourceLocalizeThreads: 5,
	}
	err = kbCtx.Configure(config, usage)
	if err != nil {
		return err
	}

	svc := service.NewService(kbCtx, false)
	err = svc.StartLoopbackServer()
	if err != nil {
		return err
	}
	kbCtx.SetService()
	uir := service.NewUIRouter(kbCtx)
	kbCtx.SetUIRouter(uir)
	kbCtx.SetDNSNameServerFetcher(dnsNSFetcher)
	svc.SetupCriticalSubServices()
	svc.RunBackgroundOperations(uir)

	serviceLog := config.GetLogFile()
	logs := libkb.Logs{
		Service: serviceLog,
	}

	logSendContext = libkb.LogSendContext{
		Contextified: libkb.NewContextified(kbCtx),
		Logs:         logs,
	}

	// open the connection
	err = Reset()
	if err != nil {
		return err
	}

	go func() {
		kbfsCtx := env.NewContextFromGlobalContext(kbCtx)
		kbfsParams := libkbfs.DefaultInitParams(kbfsCtx)
		// Setting this flag will enable KBFS debug logging to always
		// be true in a mobile setting. Change these back to the
		// commented-out values if we need to make a mobile release
		// before KBFS-on-mobile is ready.
		kbfsParams.Debug = true                         // false
		kbfsParams.Mode = libkbfs.InitConstrainedString // libkbfs.InitMinimalString
		kbfsParams.LocalHTTPServer = &libhttpserver.Server{}
		kbfsConfig, _ = libkbfs.Init(
			context.Background(), kbfsCtx, kbfsParams, serviceCn{}, func() {},
			kbCtx.Log)
	}()

	return nil
}

type serviceCn struct {
	ctx *libkb.GlobalContext
}

func (s serviceCn) NewKeybaseService(config libkbfs.Config, params libkbfs.InitParams, ctx libkbfs.Context, log logger.Logger) (libkbfs.KeybaseService, error) {
	keybaseService := libkbfs.NewKeybaseDaemonRPC(
		config, ctx, log, true, simplefs.NewSimpleFS, nil)
	// TODO: plumb the func somewhere it can be called on shutdown?
	gitrpc, _ := libgit.NewRPCHandlerWithCtx(ctx, config, nil)
	keybaseService.AddProtocols([]rpc.Protocol{
		keybase1.FsProtocol(fsrpc.NewFS(config, log)),
		keybase1.KBFSGitProtocol(gitrpc),
	})
	return keybaseService, nil
}

func (s serviceCn) NewCrypto(config libkbfs.Config, params libkbfs.InitParams, ctx libkbfs.Context, log logger.Logger) (libkbfs.Crypto, error) {
	return libkbfs.NewCryptoClientRPC(config, ctx), nil
}

// LogSend sends a log to Keybase
func LogSend(status string, feedback string, sendLogs bool, uiLogPath, traceDir string) (string, error) {
	logSendContext.Logs.Desktop = uiLogPath
	logSendContext.Logs.Trace = traceDir
	env := kbCtx.Env
	return logSendContext.LogSend(status, feedback, sendLogs, 5*1024*1024, env.GetUID(), env.GetInstallID())
}

// WriteB64 sends a base64 encoded msgpack rpc payload
func WriteB64(str string) error {
	data, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return fmt.Errorf("Base64 decode error: %s; %s", err, str)
	}
	n, err := conn.Write(data)
	if err != nil {
		return fmt.Errorf("Write error: %s", err)
	}
	if n != len(data) {
		return errors.New("Did not write all the data")
	}
	return nil
}

const targetBufferSize = 50 * 1024

// bufferSize must be divisible by 3 to ensure that we don't split
// our b64 encode across a payload boundary if we go over our buffer
// size.
const bufferSize = targetBufferSize - (targetBufferSize % 3)

// buffer for the conn.Read
var buffer = make([]byte, bufferSize)

// ReadB64 is a blocking read for base64 encoded msgpack rpc data.
// It is called serially by the mobile run loops.
func ReadB64() (string, error) {
	n, err := conn.Read(buffer)
	if n > 0 && err == nil {
		str := base64.StdEncoding.EncodeToString(buffer[0:n])
		return str, nil
	}

	if err != nil {
		// Attempt to fix the connection
		Reset()
		return "", fmt.Errorf("Read error: %s", err)
	}

	return "", nil
}

// Reset resets the socket connection
func Reset() error {
	if conn != nil {
		conn.Close()
	}

	var err error
	conn, err = kbCtx.LoopbackListener.Dial()
	if err != nil {
		return fmt.Errorf("Socket error: %s", err)
	}
	return nil
}

// ForceGC Forces a gc
func ForceGC() {
	fmt.Printf("Flushing global caches\n")
	kbCtx.FlushCaches()
	fmt.Printf("Done flushing global caches\n")

	fmt.Printf("Starting force gc\n")
	debug.FreeOSMemory()
	fmt.Printf("Done force gc\n")
}

// Version returns semantic version string
func Version() string {
	return libkb.VersionString()
}

func SetAppStateForeground() {
	defer kbCtx.Trace("SetAppStateForeground", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_FOREGROUND)
}
func SetAppStateBackground() {
	defer kbCtx.Trace("SetAppStateBackground", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_BACKGROUND)
}
func SetAppStateInactive() {
	defer kbCtx.Trace("SetAppStateInactive", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_INACTIVE)
}
func SetAppStateBackgroundActive() {
	defer kbCtx.Trace("SetAppStateBackgroundActive", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_BACKGROUNDACTIVE)
}

// AppWillExit is called reliably on iOS when the app is about to terminate
// not as reliably on android
func AppWillExit() {
	defer kbCtx.Trace("AppWillExit", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_BACKGROUNDFINAL)
}

// AppDidEnterBackground notifies the service that the app is in the background
// [iOS] returning true will request about ~3mins from iOS to continue execution
func AppDidEnterBackground() bool {
	defer kbCtx.Trace("AppDidEnterBackground", func() error { return nil })()
	SetAppStateBackground()
	return false
}

func startTrace(logFile string) {
	if os.Getenv("KEYBASE_TRACE_MOBILE") != "1" {
		return
	}

	tname := filepath.Join(filepath.Dir(logFile), "svctrace.out")
	f, err := os.Create(tname)
	if err != nil {
		fmt.Printf("error creating %s\n", tname)
		return
	}
	fmt.Printf("Go: starting trace %s\n", tname)
	trace.Start(f)
	go func() {
		fmt.Printf("Go: sleeping 30s for trace\n")
		time.Sleep(30 * time.Second)
		fmt.Printf("Go: stopping trace %s\n", tname)
		trace.Stop()
		time.Sleep(5 * time.Second)
		fmt.Printf("Go: trace stopped\n")
	}()
}
<commit_msg>move libhttpserver initialization into SimpleFS (#11612)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package keybase

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"runtime/trace"
	"sync"
	"time"

	"strings"

	"github.com/keybase/client/go/externals"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/service"
	"github.com/keybase/client/go/uidmap"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"github.com/keybase/kbfs/env"
	"github.com/keybase/kbfs/fsrpc"
	"github.com/keybase/kbfs/libgit"
	"github.com/keybase/kbfs/libkbfs"
	"github.com/keybase/kbfs/simplefs"
)

var kbCtx *libkb.GlobalContext
var conn net.Conn
var startOnce sync.Once
var logSendContext libkb.LogSendContext
var kbfsConfig libkbfs.Config

type ExternalDNSNSFetcher interface {
	GetServers() []byte
}

type dnsNSFetcher struct {
	externalFetcher ExternalDNSNSFetcher
}

func newDNSNSFetcher(d ExternalDNSNSFetcher) dnsNSFetcher {
	return dnsNSFetcher{
		externalFetcher: d,
	}
}

func (d dnsNSFetcher) processExternalResult(raw []byte) []string {
	return strings.Split(string(raw), ",")
}

func (d dnsNSFetcher) GetServers() []string {
	if d.externalFetcher != nil {
		return d.processExternalResult(d.externalFetcher.GetServers())
	}
	return getDNSServers()
}

var _ libkb.DNSNameServerFetcher = dnsNSFetcher{}

// InitOnce runs the Keybase services (only runs one time)
func InitOnce(homeDir string, logFile string, runModeStr string, accessGroupOverride bool,
	dnsNSFetcher ExternalDNSNSFetcher) {
	startOnce.Do(func() {
		if err := Init(homeDir, logFile, runModeStr, accessGroupOverride, dnsNSFetcher); err != nil {
			kbCtx.Log.Errorf("Init error: %s", err)
		}
	})
}

// Init runs the Keybase services
func Init(homeDir string, logFile string, runModeStr string, accessGroupOverride bool,
	externalDNSNSFetcher ExternalDNSNSFetcher) error {
	fmt.Println("Go: Initializing")
	if logFile != "" {
		fmt.Printf("Go: Using log: %s\n", logFile)
	}

	// Reduce OS threads on mobile so we don't have too much contention with JS thread
	oldProcs := runtime.GOMAXPROCS(0)
	newProcs := oldProcs / 2
	runtime.GOMAXPROCS(newProcs)
	fmt.Printf("Go: setting GOMAXPROCS to: %d previous: %d\n", newProcs, oldProcs)

	startTrace(logFile)

	dnsNSFetcher := newDNSNSFetcher(externalDNSNSFetcher)
	dnsServers := dnsNSFetcher.GetServers()
	for _, srv := range dnsServers {
		fmt.Printf("Go: DNS Server: %s\n", srv)
	}

	kbCtx = libkb.NewGlobalContext()
	kbCtx.Init()
	kbCtx.SetServices(externals.GetServices())

	// 10k uid -> FullName cache entries allowed
	kbCtx.SetUIDMapper(uidmap.NewUIDMap(10000))
	usage := libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
	runMode, err := libkb.StringToRunMode(runModeStr)
	if err != nil {
		return err
	}
	config := libkb.AppConfig{
		HomeDir:                        homeDir,
		LogFile:                        logFile,
		RunMode:                        runMode,
		Debug:                          true,
		LocalRPCDebug:                  "",
		VDebugSetting:                  "mobile", // use empty string for same logging as desktop default
		SecurityAccessGroupOverride:    accessGroupOverride,
		ChatInboxSourceLocalizeThreads: 5,
	}
	err = kbCtx.Configure(config, usage)
	if err != nil {
		return err
	}

	svc := service.NewService(kbCtx, false)
	err = svc.StartLoopbackServer()
	if err != nil {
		return err
	}
	kbCtx.SetService()
	uir := service.NewUIRouter(kbCtx)
	kbCtx.SetUIRouter(uir)
	kbCtx.SetDNSNameServerFetcher(dnsNSFetcher)
	svc.SetupCriticalSubServices()
	svc.RunBackgroundOperations(uir)

	serviceLog := config.GetLogFile()
	logs := libkb.Logs{
		Service: serviceLog,
	}

	logSendContext = libkb.LogSendContext{
		Contextified: libkb.NewContextified(kbCtx),
		Logs:         logs,
	}

	// open the connection
	err = Reset()
	if err != nil {
		return err
	}

	go func() {
		kbfsCtx := env.NewContextFromGlobalContext(kbCtx)
		kbfsParams := libkbfs.DefaultInitParams(kbfsCtx)
		// Setting this flag will enable KBFS debug logging to always
		// be true in a mobile setting. Change these back to the
		// commented-out values if we need to make a mobile release
		// before KBFS-on-mobile is ready.
		kbfsParams.Debug = true                         // false
		kbfsParams.Mode = libkbfs.InitConstrainedString // libkbfs.InitMinimalString
		kbfsConfig, _ = libkbfs.Init(
			context.Background(), kbfsCtx, kbfsParams, serviceCn{}, func() {},
			kbCtx.Log)
	}()

	return nil
}

type serviceCn struct {
	ctx *libkb.GlobalContext
}

func (s serviceCn) NewKeybaseService(config libkbfs.Config, params libkbfs.InitParams, ctx libkbfs.Context, log logger.Logger) (libkbfs.KeybaseService, error) {
	keybaseService := libkbfs.NewKeybaseDaemonRPC(
		config, ctx, log, true, simplefs.NewSimpleFS, nil)
	// TODO: plumb the func somewhere it can be called on shutdown?
	gitrpc, _ := libgit.NewRPCHandlerWithCtx(ctx, config, nil)
	keybaseService.AddProtocols([]rpc.Protocol{
		keybase1.FsProtocol(fsrpc.NewFS(config, log)),
		keybase1.KBFSGitProtocol(gitrpc),
	})
	return keybaseService, nil
}

func (s serviceCn) NewCrypto(config libkbfs.Config, params libkbfs.InitParams, ctx libkbfs.Context, log logger.Logger) (libkbfs.Crypto, error) {
	return libkbfs.NewCryptoClientRPC(config, ctx), nil
}

// LogSend sends a log to Keybase
func LogSend(status string, feedback string, sendLogs bool, uiLogPath, traceDir string) (string, error) {
	logSendContext.Logs.Desktop = uiLogPath
	logSendContext.Logs.Trace = traceDir
	env := kbCtx.Env
	return logSendContext.LogSend(status, feedback, sendLogs, 5*1024*1024, env.GetUID(), env.GetInstallID())
}

// WriteB64 sends a base64 encoded msgpack rpc payload
func WriteB64(str string) error {
	data, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return fmt.Errorf("Base64 decode error: %s; %s", err, str)
	}
	n, err := conn.Write(data)
	if err != nil {
		return fmt.Errorf("Write error: %s", err)
	}
	if n != len(data) {
		return errors.New("Did not write all the data")
	}
	return nil
}

const targetBufferSize = 50 * 1024

// bufferSize must be divisible by 3 to ensure that we don't split
// our b64 encode across a payload boundary if we go over our buffer
// size.
const bufferSize = targetBufferSize - (targetBufferSize % 3)

// buffer for the conn.Read
var buffer = make([]byte, bufferSize)

// ReadB64 is a blocking read for base64 encoded msgpack rpc data.
// It is called serially by the mobile run loops.
func ReadB64() (string, error) {
	n, err := conn.Read(buffer)
	if n > 0 && err == nil {
		str := base64.StdEncoding.EncodeToString(buffer[0:n])
		return str, nil
	}

	if err != nil {
		// Attempt to fix the connection
		Reset()
		return "", fmt.Errorf("Read error: %s", err)
	}

	return "", nil
}

// Reset resets the socket connection
func Reset() error {
	if conn != nil {
		conn.Close()
	}

	var err error
	conn, err = kbCtx.LoopbackListener.Dial()
	if err != nil {
		return fmt.Errorf("Socket error: %s", err)
	}
	return nil
}

// ForceGC Forces a gc
func ForceGC() {
	fmt.Printf("Flushing global caches\n")
	kbCtx.FlushCaches()
	fmt.Printf("Done flushing global caches\n")

	fmt.Printf("Starting force gc\n")
	debug.FreeOSMemory()
	fmt.Printf("Done force gc\n")
}

// Version returns semantic version string
func Version() string {
	return libkb.VersionString()
}

func SetAppStateForeground() {
	defer kbCtx.Trace("SetAppStateForeground", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_FOREGROUND)
}
func SetAppStateBackground() {
	defer kbCtx.Trace("SetAppStateBackground", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_BACKGROUND)
}
func SetAppStateInactive() {
	defer kbCtx.Trace("SetAppStateInactive", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_INACTIVE)
}
func SetAppStateBackgroundActive() {
	defer kbCtx.Trace("SetAppStateBackgroundActive", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_BACKGROUNDACTIVE)
}

// AppWillExit is called reliably on iOS when the app is about to terminate
// not as reliably on android
func AppWillExit() {
	defer kbCtx.Trace("AppWillExit", func() error { return nil })()
	kbCtx.AppState.Update(keybase1.AppState_BACKGROUNDFINAL)
}

// AppDidEnterBackground notifies the service that the app is in the background
// [iOS] returning true will request about ~3mins from iOS to continue execution
func AppDidEnterBackground() bool {
	defer kbCtx.Trace("AppDidEnterBackground", func() error { return nil })()
	SetAppStateBackground()
	return false
}

func startTrace(logFile string) {
	if os.Getenv("KEYBASE_TRACE_MOBILE") != "1" {
		return
	}

	tname := filepath.Join(filepath.Dir(logFile), "svctrace.out")
	f, err := os.Create(tname)
	if err != nil {
		fmt.Printf("error creating %s\n", tname)
		return
	}
	fmt.Printf("Go: starting trace %s\n", tname)
	trace.Start(f)
	go func() {
		fmt.Printf("Go: sleeping 30s for trace\n")
		time.Sleep(30 * time.Second)
		fmt.Printf("Go: stopping trace %s\n", tname)
		trace.Stop()
		time.Sleep(5 * time.Second)
		fmt.Printf("Go: trace stopped\n")
	}()
}
<|endoftext|>


<commit_before>package cpu

import (
	cs "github.com/bnagy/gapstone"
	"github.com/pkg/errors"

	"github.com/lunixbochs/usercorn/go/models"
)

type Capstone struct {
	Arch, Mode int

	cs *cs.Engine
	// FIXME: there's a special case on every capstone just for thumb
	thumb *Capstone
}

func (c *Capstone) Open() (err error) {
	engine, err := cs.New(c.Arch, uint(c.Mode))
	if err == nil {
		c.cs = &engine
	}
	return errors.Wrap(err, "cs.New() failed")
}

func (c *Capstone) Dis(mem []byte, addr uint64) ([]models.Ins, error) {
	if c.cs == nil {
		if err := c.Open(); err != nil {
			return nil, err
		}
	}
	// FIXME: hack, thumb detection should be injected by the ARM arch
	// detect thumb
	if len(mem) == 2 && c.Arch == cs.CS_ARCH_ARM && c.Mode == cs.CS_MODE_ARM {
		if c.thumb == nil {
			c.thumb = &Capstone{Arch: cs.CS_ARCH_ARM, Mode: cs.CS_MODE_THUMB}
		}
		return c.thumb.Dis(mem, addr)
	}
	dis, err := c.cs.Disasm(mem, addr, 0)
	if err != nil {
		return nil, errors.Wrap(err, "capstone disassembly failed")
	}
	ret := make([]models.Ins, len(dis))
	for i, ins := range dis {
		ret[i] = csIns(ins)
	}
	return ret, nil
}

// wrapper to make *gapstone.Instruction conform to the models.Ins interface
type csIns cs.Instruction

func (c csIns) Addr() uint64     { return uint64(c.Address) }
func (c csIns) Bytes() []byte    { return cs.Instruction(c).Bytes }
func (c csIns) Mnemonic() string { return cs.Instruction(c).Mnemonic }
func (c csIns) OpStr() string    { return cs.Instruction(c).OpStr }
<commit_msg>add discache back to capstone<commit_after>package cpu

import (
	"bytes"
	cs "github.com/bnagy/gapstone"
	"github.com/pkg/errors"
	"sync"

	"github.com/lunixbochs/usercorn/go/models"
)

type discacheEntry struct {
	addr uint64
	mem  []byte
	dis  []models.Ins
}

type discache struct {
	sync.RWMutex
	cache map[uint64]*discacheEntry
}

func (d *discache) Get(addr uint64, mem []byte) *discacheEntry {
	d.RLock()
	defer d.RUnlock()

	if ent, ok := d.cache[addr]; ok {
		if bytes.Equal(mem, ent.mem) {
			return ent
		}
	}
	return nil
}

func (d *discache) Put(addr uint64, mem []byte, dis []models.Ins) {
	d.Lock()
	defer d.Unlock()

	d.cache[addr] = &discacheEntry{
		addr: addr,
		mem:  mem,
		dis:  dis,
	}
}

type Capstone struct {
	Arch, Mode int

	cs *cs.Engine
	// FIXME: there's a special case on every capstone just for thumb
	thumb *Capstone
	dc    discache
}

func (c *Capstone) Open() (err error) {
	engine, err := cs.New(c.Arch, uint(c.Mode))
	if err == nil {
		c.cs = &engine
		c.dc.cache = make(map[uint64]*discacheEntry)
	}
	return errors.Wrap(err, "cs.New() failed")
}

func (c *Capstone) Dis(mem []byte, addr uint64) ([]models.Ins, error) {
	if c.cs == nil {
		if err := c.Open(); err != nil {
			return nil, err
		}
	}
	// FIXME: hack, thumb detection should be injected by the ARM arch
	// detect thumb
	if len(mem) == 2 && c.Arch == cs.CS_ARCH_ARM && c.Mode == cs.CS_MODE_ARM {
		if c.thumb == nil {
			c.thumb = &Capstone{Arch: cs.CS_ARCH_ARM, Mode: cs.CS_MODE_THUMB}
		}
		return c.thumb.Dis(mem, addr)
	}
	if ent := c.dc.Get(addr, mem); ent != nil {
		return ent.dis, nil
	}
	dis, err := c.cs.Disasm(mem, addr, 0)
	if err != nil {
		return nil, errors.Wrap(err, "capstone disassembly failed")
	}
	ret := make([]models.Ins, len(dis))
	for i, ins := range dis {
		ret[i] = csIns(ins)
	}
	c.dc.Put(addr, mem, ret)
	return ret, nil
}

// wrapper to make *gapstone.Instruction conform to the models.Ins interface
type csIns cs.Instruction

func (c csIns) Addr() uint64     { return uint64(c.Address) }
func (c csIns) Bytes() []byte    { return cs.Instruction(c).Bytes }
func (c csIns) Mnemonic() string { return cs.Instruction(c).Mnemonic }
func (c csIns) OpStr() string    { return cs.Instruction(c).OpStr }
<|endoftext|>


<commit_before>package exec

import (
	"fmt"
	"strings"

	"github.com/freneticmonkey/migrate/go/metadata"
	"github.com/freneticmonkey/migrate/go/migration"
	"github.com/freneticmonkey/migrate/go/table"
	"github.com/freneticmonkey/migrate/go/util"
)

// Options A helper struct for parameters when executing a Migration
type Options struct {
	MID              int64
	Dryrun           bool
	Force            bool
	Rollback         bool
	PTODisabled      bool
	AllowDestructive bool
	Migration        *migration.Migration
	Sandbox          bool
}

// Exec Apply the migration to the project database.  The parmeters can be used to just execute a dryrun, force past
// any validity checks, or disable using pt-online-schema-change.
func Exec(options Options) (err error) {

	mid := options.MID
	dryrun := options.Dryrun
	force := options.Force
	rollback := options.Rollback
	ptodisbled := options.PTODisabled
	allowDestructive := options.AllowDestructive
	m := options.Migration

	var statement string
	var output string
	var success bool
	var action string

	// If a Migration ID was supplied in the Migration Options, then attempt to load from the DB
	if mid > 0 {
		m, err = migration.Load(mid)
		if util.ErrorCheckf(err, "Couldn't load Migration: [%d] from the Management DB", mid) {
			return err
		}
	} else {
		return fmt.Errorf("Migration failed.  Invalid Migration Id: [%d]", mid)
	}

	// TODO: Update the Migration state at the end of the Migration!!!!

	// has the migration been approved for migration or if it is being forced
	// Assumes that this migration hasn't already been applied since the Load statement above
	if m.Status == migration.Approved || force {

		// Validate the migration
		var isLatest bool
		var migrationRunning bool
		var lm migration.Migration
		var inProgressID int64
		var failReason string

		// By default assume that this isn't the latest migration
		isLatest = false
		// Clearly an invalid Migration ID
		inProgressID = -1
		// By default we assume that another migration is running until proven otherwise
		migrationRunning = true

		// If we aren't knowingly applying an older state (rollback)
		if !rollback {
			// Ensure that this migration is the latest migration known to the DB
			lm, err = migration.GetLatest()
			if err != nil {
				failReason = fmt.Sprintf("Couldn't get latest Migration from DB: ERROR: %v", err)
			} else {
				if lm.MID == mid {
					isLatest = true
				} else {
					failReason = fmt.Sprintf("Migration: [%d] has been automatically depreciated by a Migration request with a newer schema from Git", mid)

					// Mark the migration as depreciated so that it won't be run again.
					m.Status = migration.Depreciated
					m.Update()
				}
			}
		}

		// Ensure that another migation isn't already in progress
		inProgressID, err = InProgressID()
		if err != nil {
			failReason = fmt.Sprintf("Couldn't determine if any Migrations were InProgress from DB: ERROR: %v", err)
		} else {
			if inProgressID == 0 {
				migrationRunning = false
			} else {
				failReason = fmt.Sprintf("Migration: [%d] cannot be run because another Migration: [%d] is already running", mid, inProgressID)
			}
		}

		// Check the migration for destructive changes, and verify if they are allowed.
		unapprovedDestructive := false
		destructiveChanges := []string{}
		for _, step := range m.Steps {
			// If Destructive
			if step.Op != table.Add {
				destructiveChanges = append(destructiveChanges, step.Forward)

				// If not destruction not approved - fail
				if !options.AllowDestructive {
					unapprovedDestructive = true
					failReason = fmt.Sprintf("Migration: [%d] cannot be applied because it contains destructive change(s): [%s] without use of the --allow-destructive flag", mid, step.Forward)
					break
				}
			}
		}

		// if not forced, and  prompt for destructive approval
		if !options.Force && len(destructiveChanges) > 0 && !unapprovedDestructive {
			util.LogWarn("The following DESTRUCTIVE changes have been detected.")
			util.LogAttentionf("\t%s", strings.Join(destructiveChanges, "\n\t"))
			action, err = util.SelectAction("Do you wish to continue? (y/n)", []string{"y", "n"})

			// Fail if not approved, or there was some kind of error reading input
			if action != "y" || err != nil {
				failReason = fmt.Sprintf("Migration: [%d] cannot be applied because it contains destructive change(s).", mid)
				unapprovedDestructive = true
			}
		}

		// We assume that everything is ok by default
		migrationCanExecute := true

		// If the migration isn't a rollback, ensure that it's the latest migration
		if !rollback && !isLatest {

			// if it's the sandbox we can ignore this fail state
			if !options.Sandbox {
				// If not, can't run
				migrationCanExecute = false
				failReason = fmt.Sprintf("Migration is too old to apply.  Use --rollback to force")
			}
		}

		// If there's a problem with destructive changes
		if unapprovedDestructive {
			migrationCanExecute = false
		}

		// If there's another migration already running
		if migrationRunning {
			migrationCanExecute = false
		}

		// If this migration can execute, then start applying it
		if migrationCanExecute {

			// Flag the migration as running
			if !dryrun && !m.Sandbox {
				m.Status = migration.InProgress
				err = m.Update()

				// If there was a problem updating
				if err != nil {
					return err
				}
			}

			// for each step in the migration
			for i := 0; i < len(m.Steps); i++ {
				step := m.Steps[i]

				var md *metadata.Metadata
				isDestructive := (step.Op != table.Add)

				// check if ptodisabled is true
				usePTO := !ptodisbled

				// Check if create or drop table.
				md, err = metadata.Load(step.MDID)

				if !util.ErrorCheckf(err, "The Metadata: [%d] for Step: [%d] couldn't be loaded from the Management DB", step.MDID, step.SID) {

					// if PTO can be used, and this migration is changing a table and
					// the modification is either a CREATE OR DROP TABLE.
					if usePTO && md.IsTable() && (step.Op == table.Add || step.Op == table.Del) {
						// if so, use the regular go sql driver to execute the migration
						usePTO = false
					}

					// If the Step has been approved to be applied
					if step.Status == migration.Approved || options.Sandbox {

						success = false
						statement = step.Forward

						if dryrun {

							if !allowDestructive && isDestructive {
								util.LogAttentionf("(DRYRUN) Skipping Migration Step: [%d]: Unapproved destructive change", step.SID)
							} else {
								// execute a dryrun of the migration step
								if usePTO {
									output, err = executePTO(statement, dryrun)
								} else {
									// otherwise use the regular go sql driver
									output, err = ExecuteSQL(statement, dryrun)
									util.ErrorCheckf(err, "Migration Step: ALTER TABLE Failed: [%v]", err)

								}
								util.LogAttentionf("(DRYRUN) Migration Step: [%d]\n%s", step.SID, output)
							}

							// Dryrun successful
							success = true

						} else {
							// If the change is destructive and it hasn't been approved, skip it
							if !allowDestructive && isDestructive {
								m.Steps[i].Output = fmt.Sprintf("Skipping Destructive Migration Step: [%d]: Unapproved destructive change", step.SID)
								m.Steps[i].Status = migration.Skipped

							} else {

								// Indicate that the step is going to be applied
								m.Steps[i].Status = migration.InProgress
								err = m.Steps[i].Update()
								if util.ErrorCheck(err) {
									return err
								}

								// execute the migration
								if usePTO {
									output, err = executePTO(statement, dryrun)
								} else {
									// otherwise use the regular go sql driver
									output, err = ExecuteSQL(statement, dryrun)
									util.ErrorCheckf(err, "Migration Step: ALTER TABLE Failed: [%v]", err)
								}

								if !util.ErrorCheckf(err, "Migration Step: [%d] Apply Failed with ERROR: ", output) {
									// Record the result into the step table
									m.Steps[i].Output = output

									if force {
										m.Steps[i].Status = migration.Forced
									} else {
										m.Steps[i].Status = migration.Complete
									}

									// Message that the migration step was successful
									success = true

								} else {

									// Record the step failure into the DB
									failReason = fmt.Sprintf("Failed with Error: %v", err)
									m.Steps[i].Output = failReason
									m.Steps[i].Status = migration.Failed
									err = step.Update()

									if err != nil {
										return err
									}

									failReason = fmt.Sprintf("Step: [%d] ", step.SID) + failReason

									// Record the Migration as failed into the DB
									m.Status = migration.Failed
									err = m.Update()

									if err != nil {
										return err
									}

									// Format an error message
									err = fmt.Errorf("Migration with ID: [%d] failed during apply. Reason: %s", m.MID, failReason)

									success = false
									break
								}
							}

							// Record the result of the migration
							err = m.Steps[i].Update()

							// Die immediately because there's some kind of DB connectivity issue
							if err != nil {
								return err
							}

							// If necessary, update the Metadata in the database
							err = m.Steps[i].UpdateMetadata()

							// Die immediately because there's some kind of DB connectivity issue
							if err != nil {
								return err
							}

						}
					} else {
						util.LogWarnf("Migration Step: [%d] isn't approved to be applied. Skipping.", step.SID)

						// A skipped step is still successful
						success = true
					}
				}

				// If unsuccessful, halt the migration
				if !success {
					util.LogWarn("Migration Step Failed.  Halting migration.")
					break
				}

				// Finished Migration Step
			}

			// Store success in the database
			if success {
				if !dryrun {
					if force {
						m.Status = migration.Forced
					} else {
						m.Status = migration.Complete
					}
					err = m.Update()
					if err != nil {
						return err
					}
					util.LogInfof("Migration with ID: [%d] completed successfully.", m.MID)
				} else {
					util.LogInfof("(DRYRUN) Migration with ID: [%d] completed successfully.", m.MID)
				}

			}

		} else {
			err = fmt.Errorf("Migration with ID: [%d] failed validation. Reason: %s", m.MID, failReason)
		}
	} else {
		err = fmt.Errorf("Migration with id: [%d] has not been approved for migration.  Migration failed.", m.MID)
	}

	return err
}
<commit_msg>Fixed broken sandbox migrations<commit_after>package exec

import (
	"fmt"
	"strings"

	"github.com/freneticmonkey/migrate/go/metadata"
	"github.com/freneticmonkey/migrate/go/migration"
	"github.com/freneticmonkey/migrate/go/table"
	"github.com/freneticmonkey/migrate/go/util"
)

// Options A helper struct for parameters when executing a Migration
type Options struct {
	MID              int64
	Dryrun           bool
	Force            bool
	Rollback         bool
	PTODisabled      bool
	AllowDestructive bool
	Migration        *migration.Migration
	Sandbox          bool
}

// Exec Apply the migration to the project database.  The parmeters can be used to just execute a dryrun, force past
// any validity checks, or disable using pt-online-schema-change.
func Exec(options Options) (err error) {

	mid := options.MID
	dryrun := options.Dryrun
	force := options.Force
	rollback := options.Rollback
	ptodisbled := options.PTODisabled
	allowDestructive := options.AllowDestructive
	m := options.Migration

	var statement string
	var output string
	var success bool
	var action string

	// If a Migration ID was supplied in the Migration Options, then attempt to load from the DB
	if mid > 0 {
		m, err = migration.Load(mid)
		if util.ErrorCheckf(err, "Couldn't load Migration: [%d] from the Management DB", mid) {
			return err
		}
	} else if !(options.Sandbox && m != nil) {
		return fmt.Errorf("Migration failed.  Invalid Migration Id: [%d]", mid)
	}

	// TODO: Update the Migration state at the end of the Migration!!!!

	// has the migration been approved for migration or if it is being forced
	// Assumes that this migration hasn't already been applied since the Load statement above
	if m.Status == migration.Approved || force {

		// Validate the migration
		var isLatest bool
		var migrationRunning bool
		var lm migration.Migration
		var inProgressID int64
		var failReason string

		// By default assume that this isn't the latest migration
		isLatest = false
		// Clearly an invalid Migration ID
		inProgressID = -1
		// By default we assume that another migration is running until proven otherwise
		migrationRunning = true

		// If we aren't knowingly applying an older state (rollback)
		if !rollback {
			// Ensure that this migration is the latest migration known to the DB
			lm, err = migration.GetLatest()
			if err != nil {
				failReason = fmt.Sprintf("Couldn't get latest Migration from DB: ERROR: %v", err)
			} else {
				if lm.MID == mid {
					isLatest = true
				} else {
					failReason = fmt.Sprintf("Migration: [%d] has been automatically depreciated by a Migration request with a newer schema from Git", mid)

					// Mark the migration as depreciated so that it won't be run again.
					m.Status = migration.Depreciated
					m.Update()
				}
			}
		}

		// Ensure that another migation isn't already in progress
		inProgressID, err = InProgressID()
		if err != nil {
			failReason = fmt.Sprintf("Couldn't determine if any Migrations were InProgress from DB: ERROR: %v", err)
		} else {
			if inProgressID == 0 {
				migrationRunning = false
			} else {
				failReason = fmt.Sprintf("Migration: [%d] cannot be run because another Migration: [%d] is already running", mid, inProgressID)
			}
		}

		// Check the migration for destructive changes, and verify if they are allowed.
		unapprovedDestructive := false
		destructiveChanges := []string{}
		for _, step := range m.Steps {
			// If Destructive
			if step.Op != table.Add {
				destructiveChanges = append(destructiveChanges, step.Forward)

				// If not destruction not approved - fail
				if !options.AllowDestructive {
					unapprovedDestructive = true
					failReason = fmt.Sprintf("Migration: [%d] cannot be applied because it contains destructive change(s): [%s] without use of the --allow-destructive flag", mid, step.Forward)
					break
				}
			}
		}

		// if not forced, and  prompt for destructive approval
		if !options.Force && len(destructiveChanges) > 0 && !unapprovedDestructive {
			util.LogWarn("The following DESTRUCTIVE changes have been detected.")
			util.LogAttentionf("\t%s", strings.Join(destructiveChanges, "\n\t"))
			action, err = util.SelectAction("Do you wish to continue? (y/n)", []string{"y", "n"})

			// Fail if not approved, or there was some kind of error reading input
			if action != "y" || err != nil {
				failReason = fmt.Sprintf("Migration: [%d] cannot be applied because it contains destructive change(s).", mid)
				unapprovedDestructive = true
			}
		}

		// We assume that everything is ok by default
		migrationCanExecute := true

		// If the migration isn't a rollback, ensure that it's the latest migration
		if !rollback && !isLatest {

			// if it's the sandbox we can ignore this fail state
			if !options.Sandbox {
				// If not, can't run
				migrationCanExecute = false
				failReason = fmt.Sprintf("Migration is too old to apply.  Use --rollback to force")
			}
		}

		// If there's a problem with destructive changes
		if unapprovedDestructive {
			migrationCanExecute = false
		}

		// If there's another migration already running
		if migrationRunning {
			migrationCanExecute = false
		}

		// If this migration can execute, then start applying it
		if migrationCanExecute {

			// Flag the migration as running
			if !dryrun && !m.Sandbox {
				m.Status = migration.InProgress
				err = m.Update()

				// If there was a problem updating
				if err != nil {
					return err
				}
			}

			// for each step in the migration
			for i := 0; i < len(m.Steps); i++ {
				step := m.Steps[i]

				var md *metadata.Metadata
				isDestructive := (step.Op != table.Add)

				// check if ptodisabled is true
				usePTO := !ptodisbled

				// Check if create or drop table.
				md, err = metadata.Load(step.MDID)

				if !util.ErrorCheckf(err, "The Metadata: [%d] for Step: [%d] couldn't be loaded from the Management DB", step.MDID, step.SID) {

					// if PTO can be used, and this migration is changing a table and
					// the modification is either a CREATE OR DROP TABLE.
					if usePTO && md.IsTable() && (step.Op == table.Add || step.Op == table.Del) {
						// if so, use the regular go sql driver to execute the migration
						usePTO = false
					}

					// If the Step has been approved to be applied
					if step.Status == migration.Approved || options.Sandbox {

						success = false
						statement = step.Forward

						if dryrun {

							if !allowDestructive && isDestructive {
								util.LogAttentionf("(DRYRUN) Skipping Migration Step: [%d]: Unapproved destructive change", step.SID)
							} else {
								// execute a dryrun of the migration step
								if usePTO {
									output, err = executePTO(statement, dryrun)
								} else {
									// otherwise use the regular go sql driver
									output, err = ExecuteSQL(statement, dryrun)
									util.ErrorCheckf(err, "Migration Step: ALTER TABLE Failed: [%v]", err)

								}
								util.LogAttentionf("(DRYRUN) Migration Step: [%d]\n%s", step.SID, output)
							}

							// Dryrun successful
							success = true

						} else {
							// If the change is destructive and it hasn't been approved, skip it
							if !allowDestructive && isDestructive {
								m.Steps[i].Output = fmt.Sprintf("Skipping Destructive Migration Step: [%d]: Unapproved destructive change", step.SID)
								m.Steps[i].Status = migration.Skipped

							} else {

								// Indicate that the step is going to be applied
								m.Steps[i].Status = migration.InProgress
								err = m.Steps[i].Update()
								if util.ErrorCheck(err) {
									return err
								}

								// execute the migration
								if usePTO {
									output, err = executePTO(statement, dryrun)
								} else {
									// otherwise use the regular go sql driver
									output, err = ExecuteSQL(statement, dryrun)
									util.ErrorCheckf(err, "Migration Step: ALTER TABLE Failed: [%v]", err)
								}

								if !util.ErrorCheckf(err, "Migration Step: [%d] Apply Failed with ERROR: ", output) {
									// Record the result into the step table
									m.Steps[i].Output = output

									if force {
										m.Steps[i].Status = migration.Forced
									} else {
										m.Steps[i].Status = migration.Complete
									}

									// Message that the migration step was successful
									success = true

								} else {

									// Record the step failure into the DB
									failReason = fmt.Sprintf("Failed with Error: %v", err)
									m.Steps[i].Output = failReason
									m.Steps[i].Status = migration.Failed
									err = step.Update()

									if err != nil {
										return err
									}

									failReason = fmt.Sprintf("Step: [%d] ", step.SID) + failReason

									// Record the Migration as failed into the DB
									m.Status = migration.Failed
									err = m.Update()

									if err != nil {
										return err
									}

									// Format an error message
									err = fmt.Errorf("Migration with ID: [%d] failed during apply. Reason: %s", m.MID, failReason)

									success = false
									break
								}
							}

							// Record the result of the migration
							err = m.Steps[i].Update()

							// Die immediately because there's some kind of DB connectivity issue
							if err != nil {
								return err
							}

							// If necessary, update the Metadata in the database
							err = m.Steps[i].UpdateMetadata()

							// Die immediately because there's some kind of DB connectivity issue
							if err != nil {
								return err
							}

						}
					} else {
						util.LogWarnf("Migration Step: [%d] isn't approved to be applied. Skipping.", step.SID)

						// A skipped step is still successful
						success = true
					}
				}

				// If unsuccessful, halt the migration
				if !success {
					util.LogWarn("Migration Step Failed.  Halting migration.")
					break
				}

				// Finished Migration Step
			}

			// Store success in the database
			if success {
				if !dryrun {
					if force {
						m.Status = migration.Forced
					} else {
						m.Status = migration.Complete
					}
					err = m.Update()
					if err != nil {
						return err
					}
					util.LogInfof("Migration with ID: [%d] completed successfully.", m.MID)
				} else {
					util.LogInfof("(DRYRUN) Migration with ID: [%d] completed successfully.", m.MID)
				}

			}

		} else {
			err = fmt.Errorf("Migration with ID: [%d] failed validation. Reason: %s", m.MID, failReason)
		}
	} else {
		err = fmt.Errorf("Migration with id: [%d] has not been approved for migration.  Migration failed.", m.MID)
	}

	return err
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"path/filepath"
	"strings"

	"github.com/keybase/client/go/encrypteddb"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/chat"
	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/offline"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/client/go/tlfupgrade"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

type KBFSHandler struct {
	*BaseHandler
	libkb.Contextified
	globals.ChatContextified
	service *Service
}

func NewKBFSHandler(xp rpc.Transporter, g *libkb.GlobalContext, cg *globals.ChatContext, service *Service) *KBFSHandler {
	return &KBFSHandler{
		BaseHandler:      NewBaseHandler(g, xp),
		Contextified:     libkb.NewContextified(g),
		ChatContextified: globals.NewChatContextified(cg),
		service:          service,
	}
}

func (h *KBFSHandler) FSOnlineStatusChangedEvent(_ context.Context, online bool) error {
	h.G().NotifyRouter.HandleFSOnlineStatusChanged(online)
	return nil
}

func (h *KBFSHandler) FSEvent(_ context.Context, arg keybase1.FSNotification) error {
	h.G().NotifyRouter.HandleFSActivity(arg)

	h.checkConversationRekey(arg)

	return nil
}

func (h *KBFSHandler) FSPathUpdate(_ context.Context, path string) error {
	h.G().NotifyRouter.HandleFSPathUpdated(path)
	return nil
}

func (h *KBFSHandler) FSEditList(ctx context.Context, arg keybase1.FSEditListArg) error {
	h.G().NotifyRouter.HandleFSEditListResponse(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSEditListRequest(ctx context.Context, arg keybase1.FSEditListRequest) error {
	h.G().NotifyRouter.HandleFSEditListRequest(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSSyncStatus(ctx context.Context, arg keybase1.FSSyncStatusArg) (err error) {
	h.G().NotifyRouter.HandleFSSyncStatus(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSSyncEvent(ctx context.Context, arg keybase1.FSPathSyncStatus) (err error) {
	h.G().NotifyRouter.HandleFSSyncEvent(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSOverallSyncEvent(
	_ context.Context, arg keybase1.FolderSyncStatus) (err error) {
	h.G().NotifyRouter.HandleFSOverallSyncStatusChanged(arg)
	return nil
}

func (h *KBFSHandler) FSFavoritesChangedEvent(_ context.Context) (err error) {
	h.G().NotifyRouter.HandleFSFavoritesChanged()
	return nil
}

func (h *KBFSHandler) FSSubscriptionNotifyEvent(_ context.Context, arg keybase1.FSSubscriptionNotifyEventArg) error {
	h.G().NotifyRouter.HandleFSSubscriptionNotify(keybase1.FSSubscriptionNotifyArg(arg))
	return nil
}

func (h *KBFSHandler) FSSubscriptionNotifyPathEvent(_ context.Context, arg keybase1.FSSubscriptionNotifyPathEventArg) error {
	h.G().NotifyRouter.HandleFSSubscriptionNotifyPath(keybase1.FSSubscriptionNotifyPathArg(arg))
	return nil
}

// checkConversationRekey looks for rekey finished notifications and tries to
// find any conversations associated with the rekeyed TLF.  If it finds any,
// it will send ChatThreadsStale notifications for them.
func (h *KBFSHandler) checkConversationRekey(arg keybase1.FSNotification) {
	if arg.NotificationType != keybase1.FSNotificationType_REKEYING {
		return
	}
	h.G().Log.Debug("received rekey notification for %s, code: %v", arg.Filename, arg.StatusCode)
	if arg.StatusCode != keybase1.FSStatusCode_FINISH {
		return
	}

	uid := h.G().Env.GetUID()
	if uid.IsNil() {
		h.G().Log.Debug("received rekey finished notification for %s, but have no UID", arg.Filename)
		return
	}

	h.G().Log.Debug("received rekey finished notification for %s, checking for conversations", arg.Filename)

	h.notifyConversation(uid, arg.Filename)
}

// findFolderList returns the type of KBFS folder list containing the
// given file, e.g., "private", "public", "team", etc.
func findFolderList(filename string) string {
	// KBFS always sets the filenames in the protocol to be like
	// `/keybase/private/alice/...`, regardless of the OS.  So we just
	// need to split by `/` and take the third component.
	components := strings.Split(filename, "/")
	if len(components) < 3 {
		return ""
	}
	return components[2]
}

func (h *KBFSHandler) notifyConversation(uid keybase1.UID, filename string) {
	tlf := filepath.Base(filename)
	public := findFolderList(filename) == "public"

	g := globals.NewContext(h.G(), h.ChatG())
	ctx := globals.ChatCtx(context.Background(), g, keybase1.TLFIdentifyBehavior_CHAT_SKIP,
		nil, chat.NewCachingIdentifyNotifier(g))
	h.ChatG().FetchRetrier.Rekey(ctx, tlf, chat1.ConversationMembersType_KBFS, public)
}

func (h *KBFSHandler) CreateTLF(ctx context.Context, arg keybase1.CreateTLFArg) error {
	return teams.CreateTLF(ctx, h.G(), arg)
}

func (h *KBFSHandler) GetKBFSTeamSettings(ctx context.Context, arg keybase1.GetKBFSTeamSettingsArg) (ret keybase1.KBFSTeamSettings, err error) {
	mctx := libkb.NewMetaContext(ctx, h.G()).WithLogTag("SETTINGS")
	loader := func(mctx libkb.MetaContext) (interface{}, error) {
		return teams.GetKBFSTeamSettings(mctx.Ctx(), mctx.G(), arg.TeamID.IsPublic(), arg.TeamID)
	}
	servedRet, err := h.service.offlineRPCCache.Serve(mctx, arg.Oa, offline.Version(1), "kbfs.getKBFSTeamSettings", false, arg, &ret, loader)
	if err != nil {
		return keybase1.KBFSTeamSettings{}, err
	}
	if s, ok := servedRet.(keybase1.KBFSTeamSettings); ok {
		ret = s
	}
	return ret, nil
}

func (h *KBFSHandler) UpgradeTLF(ctx context.Context, arg keybase1.UpgradeTLFArg) error {
	return tlfupgrade.UpgradeTLFForKBFS(ctx, h.G(), arg.TlfName, arg.Public)
}

// getKeyFn returns a function that gets an encryption key for storing
// favorites.
func (h *KBFSHandler) getKeyFn() func(context.Context) ([32]byte, error) {
	keyFn := func(ctx context.Context) ([32]byte, error) {
		return encrypteddb.GetSecretBoxKey(ctx, h.G(),
			libkb.EncryptionReasonKBFSFavorites, "encrypting kbfs favorites")
	}
	return keyFn
}

// EncryptFavorites encrypts cached favorites to store on disk.
func (h *KBFSHandler) EncryptFavorites(ctx context.Context,
	dataToDecrypt []byte) (res []byte, err error) {
	return encrypteddb.EncodeBox(ctx, dataToDecrypt, h.getKeyFn())
}

// DecryptFavorites decrypts cached favorites stored on disk.
func (h *KBFSHandler) DecryptFavorites(ctx context.Context,
	dataToEncrypt []byte) (res []byte, err error) {
	err = encrypteddb.DecodeBox(ctx, dataToEncrypt, h.getKeyFn(), res)
	return res, err
}
<commit_msg>service: decrypt favorites into slice pointer, not slice itself<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"path/filepath"
	"strings"

	"github.com/keybase/client/go/encrypteddb"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/chat"
	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/offline"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/client/go/tlfupgrade"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

type KBFSHandler struct {
	*BaseHandler
	libkb.Contextified
	globals.ChatContextified
	service *Service
}

func NewKBFSHandler(xp rpc.Transporter, g *libkb.GlobalContext, cg *globals.ChatContext, service *Service) *KBFSHandler {
	return &KBFSHandler{
		BaseHandler:      NewBaseHandler(g, xp),
		Contextified:     libkb.NewContextified(g),
		ChatContextified: globals.NewChatContextified(cg),
		service:          service,
	}
}

func (h *KBFSHandler) FSOnlineStatusChangedEvent(_ context.Context, online bool) error {
	h.G().NotifyRouter.HandleFSOnlineStatusChanged(online)
	return nil
}

func (h *KBFSHandler) FSEvent(_ context.Context, arg keybase1.FSNotification) error {
	h.G().NotifyRouter.HandleFSActivity(arg)

	h.checkConversationRekey(arg)

	return nil
}

func (h *KBFSHandler) FSPathUpdate(_ context.Context, path string) error {
	h.G().NotifyRouter.HandleFSPathUpdated(path)
	return nil
}

func (h *KBFSHandler) FSEditList(ctx context.Context, arg keybase1.FSEditListArg) error {
	h.G().NotifyRouter.HandleFSEditListResponse(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSEditListRequest(ctx context.Context, arg keybase1.FSEditListRequest) error {
	h.G().NotifyRouter.HandleFSEditListRequest(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSSyncStatus(ctx context.Context, arg keybase1.FSSyncStatusArg) (err error) {
	h.G().NotifyRouter.HandleFSSyncStatus(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSSyncEvent(ctx context.Context, arg keybase1.FSPathSyncStatus) (err error) {
	h.G().NotifyRouter.HandleFSSyncEvent(ctx, arg)
	return nil
}

func (h *KBFSHandler) FSOverallSyncEvent(
	_ context.Context, arg keybase1.FolderSyncStatus) (err error) {
	h.G().NotifyRouter.HandleFSOverallSyncStatusChanged(arg)
	return nil
}

func (h *KBFSHandler) FSFavoritesChangedEvent(_ context.Context) (err error) {
	h.G().NotifyRouter.HandleFSFavoritesChanged()
	return nil
}

func (h *KBFSHandler) FSSubscriptionNotifyEvent(_ context.Context, arg keybase1.FSSubscriptionNotifyEventArg) error {
	h.G().NotifyRouter.HandleFSSubscriptionNotify(keybase1.FSSubscriptionNotifyArg(arg))
	return nil
}

func (h *KBFSHandler) FSSubscriptionNotifyPathEvent(_ context.Context, arg keybase1.FSSubscriptionNotifyPathEventArg) error {
	h.G().NotifyRouter.HandleFSSubscriptionNotifyPath(keybase1.FSSubscriptionNotifyPathArg(arg))
	return nil
}

// checkConversationRekey looks for rekey finished notifications and tries to
// find any conversations associated with the rekeyed TLF.  If it finds any,
// it will send ChatThreadsStale notifications for them.
func (h *KBFSHandler) checkConversationRekey(arg keybase1.FSNotification) {
	if arg.NotificationType != keybase1.FSNotificationType_REKEYING {
		return
	}
	h.G().Log.Debug("received rekey notification for %s, code: %v", arg.Filename, arg.StatusCode)
	if arg.StatusCode != keybase1.FSStatusCode_FINISH {
		return
	}

	uid := h.G().Env.GetUID()
	if uid.IsNil() {
		h.G().Log.Debug("received rekey finished notification for %s, but have no UID", arg.Filename)
		return
	}

	h.G().Log.Debug("received rekey finished notification for %s, checking for conversations", arg.Filename)

	h.notifyConversation(uid, arg.Filename)
}

// findFolderList returns the type of KBFS folder list containing the
// given file, e.g., "private", "public", "team", etc.
func findFolderList(filename string) string {
	// KBFS always sets the filenames in the protocol to be like
	// `/keybase/private/alice/...`, regardless of the OS.  So we just
	// need to split by `/` and take the third component.
	components := strings.Split(filename, "/")
	if len(components) < 3 {
		return ""
	}
	return components[2]
}

func (h *KBFSHandler) notifyConversation(uid keybase1.UID, filename string) {
	tlf := filepath.Base(filename)
	public := findFolderList(filename) == "public"

	g := globals.NewContext(h.G(), h.ChatG())
	ctx := globals.ChatCtx(context.Background(), g, keybase1.TLFIdentifyBehavior_CHAT_SKIP,
		nil, chat.NewCachingIdentifyNotifier(g))
	h.ChatG().FetchRetrier.Rekey(ctx, tlf, chat1.ConversationMembersType_KBFS, public)
}

func (h *KBFSHandler) CreateTLF(ctx context.Context, arg keybase1.CreateTLFArg) error {
	return teams.CreateTLF(ctx, h.G(), arg)
}

func (h *KBFSHandler) GetKBFSTeamSettings(ctx context.Context, arg keybase1.GetKBFSTeamSettingsArg) (ret keybase1.KBFSTeamSettings, err error) {
	mctx := libkb.NewMetaContext(ctx, h.G()).WithLogTag("SETTINGS")
	loader := func(mctx libkb.MetaContext) (interface{}, error) {
		return teams.GetKBFSTeamSettings(mctx.Ctx(), mctx.G(), arg.TeamID.IsPublic(), arg.TeamID)
	}
	servedRet, err := h.service.offlineRPCCache.Serve(mctx, arg.Oa, offline.Version(1), "kbfs.getKBFSTeamSettings", false, arg, &ret, loader)
	if err != nil {
		return keybase1.KBFSTeamSettings{}, err
	}
	if s, ok := servedRet.(keybase1.KBFSTeamSettings); ok {
		ret = s
	}
	return ret, nil
}

func (h *KBFSHandler) UpgradeTLF(ctx context.Context, arg keybase1.UpgradeTLFArg) error {
	return tlfupgrade.UpgradeTLFForKBFS(ctx, h.G(), arg.TlfName, arg.Public)
}

// getKeyFn returns a function that gets an encryption key for storing
// favorites.
func (h *KBFSHandler) getKeyFn() func(context.Context) ([32]byte, error) {
	keyFn := func(ctx context.Context) ([32]byte, error) {
		return encrypteddb.GetSecretBoxKey(ctx, h.G(),
			libkb.EncryptionReasonKBFSFavorites, "encrypting kbfs favorites")
	}
	return keyFn
}

// EncryptFavorites encrypts cached favorites to store on disk.
func (h *KBFSHandler) EncryptFavorites(ctx context.Context,
	dataToDecrypt []byte) (res []byte, err error) {
	return encrypteddb.EncodeBox(ctx, dataToDecrypt, h.getKeyFn())
}

// DecryptFavorites decrypts cached favorites stored on disk.
func (h *KBFSHandler) DecryptFavorites(ctx context.Context,
	dataToEncrypt []byte) (res []byte, err error) {
	err = encrypteddb.DecodeBox(ctx, dataToEncrypt, h.getKeyFn(), &res)
	return res, err
}
<|endoftext|>


<commit_before>// 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/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com
package oval

import (
	"os/exec"
	"strings"
)

const (
	_ = iota
	RPM_EXACT_MATCH
	RPM_SUBSTRING_MATCH
)

type rpmRequest struct {
	out       chan rpmResponse
	name      string
	matchtype int
}

type rpmResponse struct {
	pkgdata []rpmPackage
}

type rpmPackage struct {
	name    string
	version string
}

func (r *rpmPackage) externalize() (ret ExternalizedPackage) {
	ret.Name = r.name
	ret.Version = r.version
	return ret
}

func (obj *GRPMInfoTest) execute(od *GOvalDefinitions) bool {
	v := od.getObject(obj.Object.ObjectRef)
	if v == nil {
		panic("unknown object in test execution!")
	}
	// XXX We should validate the object type here.
	o := v.(*GRPMInfoObj)

	s := od.getState(obj.State.StateRef)
	if s == nil {
		panic("unknown state in test execution")
	}
	// XXX We should validate the state type here.
	state := s.(*GRPMInfoState)

	return state.evaluate(o)
}

func (state *GRPMInfoState) evaluate(obj *GRPMInfoObj) bool {
	debugPrint("[rpminfo_state] evaluate %v\n", state.ID)

	transpkg := obj.Name
	if parserCfg.centosRedhatKludge != 0 {
		transpkg = centosRedhatPackageTranslate(transpkg)
	}

	rif := rpmRequest{}
	rif.out = make(chan rpmResponse)
	rif.name = transpkg
	dmgr.rpm.schan <- rif
	resp := <-rif.out

	// If we get nothing back the package isn't installed.
	if len(resp.pkgdata) == 0 {
		debugPrint("[rpminfo_state] doesn't look like %v is installed\n", transpkg)
		return false
	}

	// XXX It's possible multiple responses can be returned, right now we
	// just select the first one but we should probably sort and use the
	// latest.
	pkgname := resp.pkgdata[0].name
	pkgversion := resp.pkgdata[0].version
	debugPrint("[rpminfo_state] %v installed, %v\n", pkgname, pkgversion)

	// If it's simply a key ID check, just simulate TRUE detection here.
	if len(state.SigKeyID.Value) > 0 {
		return true
	} else if len(state.EVRCheck.Value) > 0 {
		evrop := evrLookupOperation(state.EVRCheck.Operation)
		if evrop == EVROP_UNKNOWN {
			panic("evaluate: unknown evr comparison operation")
		}
		return evrCompare(evrop, pkgversion, state.EVRCheck.Value)
	} else if len(state.VersionCheck.Value) > 0 {
		return versionPtrnMatch(pkgversion, state.VersionCheck.Value)
	}

	return false
}

func (r *GRPMInfoObj) prepare() {
}

type rpmDataMgr struct {
	schan    chan rpmRequest
	pkglist  []rpmPackage
	prepared bool
}

func (d *rpmDataMgr) makeRequest(arg string, matchType int) rpmResponse {
	rif := rpmRequest{}
	rif.out = make(chan rpmResponse)
	rif.name = arg
	rif.matchtype = matchType
	dmgr.rpm.schan <- rif
	return <-rif.out
}

func (d *rpmDataMgr) init() {
	debugPrint("initializing rpm data manager\n")
	d.schan = make(chan rpmRequest)
}

func (d *rpmDataMgr) prepare() {
	d.pkglist = rpmGetPackages()
	d.prepared = true
}

func (d *rpmDataMgr) build_response(req rpmRequest) rpmResponse {
	ret := rpmResponse{}

	for _, x := range d.pkglist {
		switch req.matchtype {
		case DPKG_EXACT_MATCH:
			if req.name == x.name {
				ret.pkgdata = append(ret.pkgdata, x)
			}
		case DPKG_SUBSTRING_MATCH:
			if strings.Contains(x.name, req.name) {
				ret.pkgdata = append(ret.pkgdata, x)
			}
		default:
			panic("invalid rpm match type specified")
		}
	}

	return ret
}

func (d *rpmDataMgr) run() {
	debugPrint("Starting rpm data manager\n")

	for {
		r, ok := <-d.schan
		if ok == false {
			debugPrint("Stopping rpm data manager\n")
			return
		}
		r.out <- d.build_response(r)
	}
}

func rpmGetPackages() []rpmPackage {
	ret := make([]rpmPackage, 0)

	c := exec.Command("rpm", "-qa", "--queryformat", "%{NAME} %{EVR}\\n")
	buf, ok := c.Output()
	if ok != nil {
		return nil
	}

	slist := strings.Split(string(buf), "\n")
	for _, x := range slist {
		s := strings.Fields(x)

		if len(s) < 2 {
			continue
		}
		newpkg := rpmPackage{s[0], s[1]}
		ret = append(ret, newpkg)
	}
	return ret
}
<commit_msg>fix a couple bugs related to last commit<commit_after>// 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/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com
package oval

import (
	"os/exec"
	"strings"
)

const (
	_ = iota
	RPM_EXACT_MATCH
	RPM_SUBSTRING_MATCH
)

type rpmRequest struct {
	out       chan rpmResponse
	name      string
	matchtype int
}

type rpmResponse struct {
	pkgdata []rpmPackage
}

type rpmPackage struct {
	name    string
	version string
}

func (r *rpmPackage) externalize() (ret ExternalizedPackage) {
	ret.Name = r.name
	ret.Version = r.version
	return ret
}

func (obj *GRPMInfoTest) execute(od *GOvalDefinitions) bool {
	v := od.getObject(obj.Object.ObjectRef)
	if v == nil {
		panic("unknown object in test execution!")
	}
	// XXX We should validate the object type here.
	o := v.(*GRPMInfoObj)

	s := od.getState(obj.State.StateRef)
	if s == nil {
		panic("unknown state in test execution")
	}
	// XXX We should validate the state type here.
	state := s.(*GRPMInfoState)

	return state.evaluate(o)
}

func (state *GRPMInfoState) evaluate(obj *GRPMInfoObj) bool {
	debugPrint("[rpminfo_state] evaluate %v\n", state.ID)

	transpkg := obj.Name
	if parserCfg.centosRedhatKludge != 0 {
		transpkg = centosRedhatPackageTranslate(transpkg)
	}

	resp := dmgr.rpm.makeRequest(obj.Name, RPM_EXACT_MATCH)

	// If we get nothing back the package isn't installed.
	if len(resp.pkgdata) == 0 {
		debugPrint("[rpminfo_state] doesn't look like %v is installed\n", transpkg)
		return false
	}

	// XXX It's possible multiple responses can be returned, right now we
	// just select the first one but we should probably sort and use the
	// latest.
	pkgname := resp.pkgdata[0].name
	pkgversion := resp.pkgdata[0].version
	debugPrint("[rpminfo_state] %v installed, %v\n", pkgname, pkgversion)

	// If it's simply a key ID check, just simulate TRUE detection here.
	if len(state.SigKeyID.Value) > 0 {
		return true
	} else if len(state.EVRCheck.Value) > 0 {
		evrop := evrLookupOperation(state.EVRCheck.Operation)
		if evrop == EVROP_UNKNOWN {
			panic("evaluate: unknown evr comparison operation")
		}
		return evrCompare(evrop, pkgversion, state.EVRCheck.Value)
	} else if len(state.VersionCheck.Value) > 0 {
		return versionPtrnMatch(pkgversion, state.VersionCheck.Value)
	}

	return false
}

func (r *GRPMInfoObj) prepare() {
}

type rpmDataMgr struct {
	schan    chan rpmRequest
	pkglist  []rpmPackage
	prepared bool
}

func (d *rpmDataMgr) makeRequest(arg string, matchType int) rpmResponse {
	rif := rpmRequest{}
	rif.out = make(chan rpmResponse)
	rif.name = arg
	rif.matchtype = matchType
	dmgr.rpm.schan <- rif
	return <-rif.out
}

func (d *rpmDataMgr) init() {
	debugPrint("initializing rpm data manager\n")
	d.schan = make(chan rpmRequest)
}

func (d *rpmDataMgr) prepare() {
	d.pkglist = rpmGetPackages()
	d.prepared = true
}

func (d *rpmDataMgr) build_response(req rpmRequest) rpmResponse {
	ret := rpmResponse{}

	for _, x := range d.pkglist {
		switch req.matchtype {
		case RPM_EXACT_MATCH:
			if req.name == x.name {
				ret.pkgdata = append(ret.pkgdata, x)
			}
		case RPM_SUBSTRING_MATCH:
			if strings.Contains(x.name, req.name) {
				ret.pkgdata = append(ret.pkgdata, x)
			}
		default:
			panic("invalid rpm match type specified")
		}
	}

	return ret
}

func (d *rpmDataMgr) run() {
	debugPrint("Starting rpm data manager\n")

	for {
		r, ok := <-d.schan
		if ok == false {
			debugPrint("Stopping rpm data manager\n")
			return
		}
		r.out <- d.build_response(r)
	}
}

func rpmGetPackages() []rpmPackage {
	ret := make([]rpmPackage, 0)

	c := exec.Command("rpm", "-qa", "--queryformat", "%{NAME} %{EVR}\\n")
	buf, ok := c.Output()
	if ok != nil {
		return nil
	}

	slist := strings.Split(string(buf), "\n")
	for _, x := range slist {
		s := strings.Fields(x)

		if len(s) < 2 {
			continue
		}
		newpkg := rpmPackage{s[0], s[1]}
		ret = append(ret, newpkg)
	}
	return ret
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vcs

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

// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
// TODO(cmang): Add tests for SVN and BZR.
func TestRepoRootForImportPath(t *testing.T) {
	tests := []struct {
		path string
		want *RepoRoot
	}{
		{
			"code.google.com/p/go",
			&RepoRoot{
				VCS:  vcsHg,
				Repo: "https://code.google.com/p/go",
			},
		},
		{
			"github.com/golang/groupcache",
			&RepoRoot{
				VCS:  vcsGit,
				Repo: "https://github.com/golang/groupcache",
			},
		},
	}

	for _, test := range tests {
		got, _ := RepoRootForImportPath(test.path, false)
		want := test.want
		if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
			t.Errorf("RepoRootForImport(%s) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
		}
	}
}

// Test that FromDir correctly inspects a given directory and returns the right VCS.
func TestFromDir(t *testing.T) {
	type testStruct struct {
		path string
		want *Cmd
	}

	tests := make([]testStruct, len(vcsList))
	tempDir := os.TempDir()

	for i, vcs := range vcsList {
		tests[i] = testStruct{
			filepath.Join(tempDir, vcs.Name, "."+vcs.Cmd),
			vcs,
		}
	}

	for _, test := range tests {
		os.MkdirAll(test.path, 0755)
		got, _, _ := FromDir(test.path, tempDir)
		if got.Name != test.want.Name {
			t.Errorf("FromDir(%s, %s) = %s, want %s", got, test.want)
		}
		os.RemoveAll(test.path)
	}
	os.RemoveAll(tempDir)
}
<commit_msg>go.tools/go/vcs: do not delete $TMPDIR during test runs<commit_after>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vcs

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
// TODO(cmang): Add tests for SVN and BZR.
func TestRepoRootForImportPath(t *testing.T) {
	tests := []struct {
		path string
		want *RepoRoot
	}{
		{
			"code.google.com/p/go",
			&RepoRoot{
				VCS:  vcsHg,
				Repo: "https://code.google.com/p/go",
			},
		},
		{
			"github.com/golang/groupcache",
			&RepoRoot{
				VCS:  vcsGit,
				Repo: "https://github.com/golang/groupcache",
			},
		},
	}

	for _, test := range tests {
		got, _ := RepoRootForImportPath(test.path, false)
		want := test.want
		if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
			t.Errorf("RepoRootForImport(%s) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
		}
	}
}

// Test that FromDir correctly inspects a given directory and returns the right VCS.
func TestFromDir(t *testing.T) {
	type testStruct struct {
		path string
		want *Cmd
	}

	tests := make([]testStruct, len(vcsList))
	tempDir, err := ioutil.TempDir("", "vcstest")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)

	for i, vcs := range vcsList {
		tests[i] = testStruct{
			filepath.Join(tempDir, vcs.Name, "."+vcs.Cmd),
			vcs,
		}
	}

	for _, test := range tests {
		os.MkdirAll(test.path, 0755)
		got, _, _ := FromDir(test.path, tempDir)
		if got.Name != test.want.Name {
			t.Errorf("FromDir(%s, %s) = %s, want %s", got, test.want)
		}
		os.RemoveAll(test.path)
	}
}
<|endoftext|>


<commit_before>package service

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/Jeffail/benthos/v3/lib/config"
	"github.com/Jeffail/benthos/v3/lib/input"
	"github.com/Jeffail/benthos/v3/lib/output"
	"github.com/Jeffail/benthos/v3/lib/processor"
	"github.com/Jeffail/benthos/v3/lib/service/blobl"
	"github.com/Jeffail/benthos/v3/lib/service/test"
	uconfig "github.com/Jeffail/benthos/v3/lib/util/config"
	"github.com/urfave/cli/v2"
)

//------------------------------------------------------------------------------

// Build stamps.
var (
	Version   string
	DateBuilt string
)

//------------------------------------------------------------------------------

// OptSetVersionStamp creates an opt func for setting the version and date built
// stamps that Benthos returns via --version and the /version endpoint. The
// traditional way of setting these values is via the build flags:
// -X github.com/Jeffail/benthos/v3/lib/service.Version=$(VERSION) and
// -X github.com/Jeffail/benthos/v3/lib/service.DateBuilt=$(DATE)
func OptSetVersionStamp(version, dateBuilt string) func() {
	return func() {
		Version = version
		DateBuilt = dateBuilt
	}
}

//------------------------------------------------------------------------------

func cmdVersion(version, dateBuild string) {
	fmt.Printf("Version: %v\nDate: %v\n", Version, DateBuilt)
	os.Exit(0)
}

//------------------------------------------------------------------------------

func addExpression(conf *config.Type, expression string) error {
	var inputTypes, processorTypes, outputTypes []string
	componentTypes := strings.Split(expression, "/")
	for i, str := range componentTypes {
		for _, t := range strings.Split(str, ",") {
			if t = strings.TrimSpace(t); len(t) > 0 {
				switch i {
				case 0:
					inputTypes = append(inputTypes, t)
				case 1:
					processorTypes = append(processorTypes, t)
				case 2:
					outputTypes = append(outputTypes, t)
				default:
					return errors.New("more component separators than expected")
				}
			}
		}
	}

	if lInputs := len(inputTypes); lInputs == 1 {
		t := inputTypes[0]
		if _, exists := input.Constructors[t]; exists {
			conf.Input.Type = t
		} else {
			return fmt.Errorf("unrecognised input type '%v'", t)
		}
	} else if lInputs > 1 {
		conf.Input.Type = input.TypeBroker
		for _, t := range inputTypes {
			c := input.NewConfig()
			if _, exists := input.Constructors[t]; exists {
				c.Type = t
			} else {
				return fmt.Errorf("unrecognised input type '%v'", t)
			}
			conf.Input.Broker.Inputs = append(conf.Input.Broker.Inputs, c)
		}
	}

	for _, t := range processorTypes {
		c := processor.NewConfig()
		if _, exists := processor.Constructors[t]; exists {
			c.Type = t
		} else {
			return fmt.Errorf("unrecognised processor type '%v'", t)
		}
		conf.Pipeline.Processors = append(conf.Pipeline.Processors, c)
	}

	if lOutputs := len(outputTypes); lOutputs == 1 {
		t := outputTypes[0]
		if _, exists := output.Constructors[t]; exists {
			conf.Output.Type = t
		} else {
			return fmt.Errorf("unrecognised output type '%v'", t)
		}
	} else if lOutputs > 1 {
		conf.Output.Type = output.TypeBroker
		for _, t := range outputTypes {
			c := output.NewConfig()
			if _, exists := output.Constructors[t]; exists {
				c.Type = t
			} else {
				return fmt.Errorf("unrecognised output type '%v'", t)
			}
			conf.Output.Broker.Outputs = append(conf.Output.Broker.Outputs, c)
		}
	}
	return nil
}

//------------------------------------------------------------------------------

// RunWithOpts runs the Benthos service after first applying opt funcs, which
// are used for specify service customisations.
func RunWithOpts(opts ...func()) {
	for _, opt := range opts {
		opt()
	}
	Run()
}

// Run the Benthos service, if the pipeline is started successfully then this
// call blocks until either the pipeline shuts down or a termination signal is
// received.
func Run() {
	app := &cli.App{
		Name:  "benthos",
		Usage: "A stream processor for mundane tasks - https://benthos.dev",
		Description: `
   Either run Benthos as a stream processor or choose a command:

   benthos list inputs
   benthos create kafka_balanced//file > ./config.yaml
   benthos -c ./config.yaml`[4:],
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:    "version",
				Aliases: []string{"v"},
				Value:   false,
				Usage:   "display version info, then exit",
			},
			&cli.StringFlag{
				Name:    "config",
				Aliases: []string{"c"},
				Value:   "",
				Usage:   "a path to a configuration file",
			},
			&cli.BoolFlag{
				Name:  "chilled",
				Value: false,
				Usage: "continue to execute a config containing linter errors",
			},
		},
		Action: func(c *cli.Context) error {
			if c.Bool("version") {
				cmdVersion(Version, DateBuilt)
			}
			if c.Args().Len() > 0 {
				fmt.Fprintf(os.Stderr, "Unrecognised command: %v\n", c.Args().First())
				cli.ShowAppHelp(c)
				os.Exit(1)
			}
			os.Exit(cmdService(c.String("config"), !c.Bool("chilled"), false, nil))
			return nil
		},
		Commands: []*cli.Command{
			{
				Name:  "echo",
				Usage: "Parse a config file and echo back a normalised version",
				Description: `
   This simple command is useful for sanity checking a config if it isn't
   behaving as expected, as it shows you a normalised version after environment
   variables have been resolved:

   benthos -c ./config.yaml echo | less`[4:],
				Action: func(c *cli.Context) error {
					readConfig(c.String("config"))
					outConf, err := conf.Sanitised()
					if err == nil {
						var configYAML []byte
						if configYAML, err = uconfig.MarshalYAML(outConf); err == nil {
							fmt.Println(string(configYAML))
						}
					}
					if err != nil {
						fmt.Fprintln(os.Stderr, fmt.Sprintf("Echo error: %v", err))
						os.Exit(1)
					}
					return nil
				},
			},
			{
				Name:  "lint",
				Usage: "Parse Benthos configs and report any linting errors",
				Description: `
   Exits with a status code 1 if any linting errors are detected:

   benthos -c target.yaml lint
   benthos lint ./configs/*.yaml
   benthos lint ./foo.yaml ./bar.yaml`[4:],
				Action: func(c *cli.Context) error {
					targets := c.Args().Slice()
					if conf := c.String("config"); len(conf) > 0 {
						targets = append(targets, conf)
					}
					var pathLints []string
					for _, target := range targets {
						if len(target) == 0 {
							continue
						}
						var conf = config.New()
						lints, err := config.Read(target, true, &conf)
						if err != nil {
							fmt.Fprintf(os.Stderr, "Configuration file read error: %v\n", err)
							os.Exit(1)
						}
						for _, l := range lints {
							pathLints = append(pathLints, target+": "+l)
						}
					}
					if len(pathLints) == 0 {
						os.Exit(0)
					}
					for _, lint := range pathLints {
						fmt.Fprintln(os.Stderr, lint)
					}
					os.Exit(1)
					return nil
				},
			},
			{
				Name:  "streams",
				Usage: "Run Benthos in streams mode",
				Description: `
   Run Benthos in streams mode, where multiple pipelines can be executed in a
   single process and can be created, updated and removed via REST HTTP
   endpoints.

   benthos streams ./path/to/stream/configs ./and/some/more
   benthos -c ./root_config.yaml streams ./path/to/stream/configs
   benthos -c ./root_config.yaml streams

   In streams mode the stream fields of a root target config (input, buffer,
   pipeline, output) will be ignored. Other fields will be shared across all
   loaded streams (resources, metrics, etc).

   For more information check out the docs at:
   https://benthos.dev/docs/guides/streams_mode/about`[4:],
				Action: func(c *cli.Context) error {
					os.Exit(cmdService(c.String("config"), !c.Bool("chilled"), true, c.Args().Slice()))
					return nil
				},
			},
			{
				Name:  "list",
				Usage: "List all Benthos component types",
				Description: `
   If any component types are explicitly listed then only types of those
   components will be shown.

   benthos list
   benthos list inputs output
   benthos list rate-limits buffers`[4:],
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:  "format",
						Value: "text",
						Usage: "Print the component list in a specific format. Options are text or json.",
					},
				},
				Action: func(c *cli.Context) error {
					listComponents(c)
					os.Exit(0)
					return nil
				},
			},
			{
				Name:  "create",
				Usage: "Create a new Benthos config",
				Description: `
   Prints a new Benthos config to stdout containing specified components
   according to an expression. The expression must take the form of three
   comma-separated lists of inputs, processors and outputs, divided by
   forward slashes:

   benthos create stdin/jmespath,awk/nats
   benthos create file,http_server/json/http_client

   If the expression is omitted a default config is created.`[4:],
				Action: func(c *cli.Context) error {
					if expression := c.Args().First(); len(expression) > 0 {
						if err := addExpression(&conf, expression); err != nil {
							fmt.Fprintln(os.Stderr, fmt.Sprintf("Generate error: %v", err))
							os.Exit(1)
						}
					}
					outConf, err := conf.Sanitised()
					if err == nil {
						var configYAML []byte
						if configYAML, err = uconfig.MarshalYAML(outConf); err == nil {
							fmt.Println(string(configYAML))
						}
					}
					if err != nil {
						fmt.Fprintln(os.Stderr, fmt.Sprintf("Generate error: %v", err))
						os.Exit(1)
					}
					return nil
				},
			},
			test.CliCommand(testSuffix),
			blobl.CliCommand(),
		},
	}

	app.OnUsageError = func(context *cli.Context, err error, isSubcommand bool) error {
		flags, notDeprecated := checkDeprecatedFlags(os.Args[1:])
		if !notDeprecated {
			fmt.Printf("Usage error: %v\n", err)
			cli.ShowAppHelp(context)
			return err
		}

		showVersion := flags.Bool(
			"version", false, "Display version info, then exit",
		)
		configPath := flags.String(
			"c", "", "Path to a configuration file",
		)

		flags.Usage = func() {
			cli.ShowAppHelp(context)
		}

		flags.Parse(os.Args[1:])
		if *showVersion {
			cmdVersion(Version, DateBuilt)
		}

		deprecatedExecute(*configPath, testSuffix)
		os.Exit(cmdService(*configPath, false, false, nil))
		return nil
	}

	app.Run(os.Args)
}

//------------------------------------------------------------------------------
<commit_msg>Add service opt func for defining string flags<commit_after>package service

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/Jeffail/benthos/v3/lib/config"
	"github.com/Jeffail/benthos/v3/lib/input"
	"github.com/Jeffail/benthos/v3/lib/output"
	"github.com/Jeffail/benthos/v3/lib/processor"
	"github.com/Jeffail/benthos/v3/lib/service/blobl"
	"github.com/Jeffail/benthos/v3/lib/service/test"
	uconfig "github.com/Jeffail/benthos/v3/lib/util/config"
	"github.com/urfave/cli/v2"
)

//------------------------------------------------------------------------------

// Build stamps.
var (
	Version   string
	DateBuilt string
)

//------------------------------------------------------------------------------

// OptSetVersionStamp creates an opt func for setting the version and date built
// stamps that Benthos returns via --version and the /version endpoint. The
// traditional way of setting these values is via the build flags:
// -X github.com/Jeffail/benthos/v3/lib/service.Version=$(VERSION) and
// -X github.com/Jeffail/benthos/v3/lib/service.DateBuilt=$(DATE)
func OptSetVersionStamp(version, dateBuilt string) func() {
	return func() {
		Version = version
		DateBuilt = dateBuilt
	}
}

//------------------------------------------------------------------------------

var customFlags []cli.Flag

// OptAddStringFlag registers a custom CLI flag for the standard Benthos run
// command.
func OptAddStringFlag(name, usage string, aliases []string, value string, destination *string) func() {
	return func() {
		customFlags = append(customFlags, &cli.StringFlag{
			Name:        name,
			Aliases:     aliases,
			Value:       value,
			Usage:       usage,
			Destination: destination,
		})
	}
}

//------------------------------------------------------------------------------

func cmdVersion(version, dateBuild string) {
	fmt.Printf("Version: %v\nDate: %v\n", Version, DateBuilt)
	os.Exit(0)
}

//------------------------------------------------------------------------------

func addExpression(conf *config.Type, expression string) error {
	var inputTypes, processorTypes, outputTypes []string
	componentTypes := strings.Split(expression, "/")
	for i, str := range componentTypes {
		for _, t := range strings.Split(str, ",") {
			if t = strings.TrimSpace(t); len(t) > 0 {
				switch i {
				case 0:
					inputTypes = append(inputTypes, t)
				case 1:
					processorTypes = append(processorTypes, t)
				case 2:
					outputTypes = append(outputTypes, t)
				default:
					return errors.New("more component separators than expected")
				}
			}
		}
	}

	if lInputs := len(inputTypes); lInputs == 1 {
		t := inputTypes[0]
		if _, exists := input.Constructors[t]; exists {
			conf.Input.Type = t
		} else {
			return fmt.Errorf("unrecognised input type '%v'", t)
		}
	} else if lInputs > 1 {
		conf.Input.Type = input.TypeBroker
		for _, t := range inputTypes {
			c := input.NewConfig()
			if _, exists := input.Constructors[t]; exists {
				c.Type = t
			} else {
				return fmt.Errorf("unrecognised input type '%v'", t)
			}
			conf.Input.Broker.Inputs = append(conf.Input.Broker.Inputs, c)
		}
	}

	for _, t := range processorTypes {
		c := processor.NewConfig()
		if _, exists := processor.Constructors[t]; exists {
			c.Type = t
		} else {
			return fmt.Errorf("unrecognised processor type '%v'", t)
		}
		conf.Pipeline.Processors = append(conf.Pipeline.Processors, c)
	}

	if lOutputs := len(outputTypes); lOutputs == 1 {
		t := outputTypes[0]
		if _, exists := output.Constructors[t]; exists {
			conf.Output.Type = t
		} else {
			return fmt.Errorf("unrecognised output type '%v'", t)
		}
	} else if lOutputs > 1 {
		conf.Output.Type = output.TypeBroker
		for _, t := range outputTypes {
			c := output.NewConfig()
			if _, exists := output.Constructors[t]; exists {
				c.Type = t
			} else {
				return fmt.Errorf("unrecognised output type '%v'", t)
			}
			conf.Output.Broker.Outputs = append(conf.Output.Broker.Outputs, c)
		}
	}
	return nil
}

//------------------------------------------------------------------------------

// RunWithOpts runs the Benthos service after first applying opt funcs, which
// are used for specify service customisations.
func RunWithOpts(opts ...func()) {
	for _, opt := range opts {
		opt()
	}
	Run()
}

// Run the Benthos service, if the pipeline is started successfully then this
// call blocks until either the pipeline shuts down or a termination signal is
// received.
func Run() {
	flags := []cli.Flag{
		&cli.BoolFlag{
			Name:    "version",
			Aliases: []string{"v"},
			Value:   false,
			Usage:   "display version info, then exit",
		},
		&cli.StringFlag{
			Name:    "config",
			Aliases: []string{"c"},
			Value:   "",
			Usage:   "a path to a configuration file",
		},
		&cli.BoolFlag{
			Name:  "chilled",
			Value: false,
			Usage: "continue to execute a config containing linter errors",
		},
	}
	if len(customFlags) > 0 {
		flags = append(flags, customFlags...)
	}

	app := &cli.App{
		Name:  "benthos",
		Usage: "A stream processor for mundane tasks - https://benthos.dev",
		Description: `
   Either run Benthos as a stream processor or choose a command:

   benthos list inputs
   benthos create kafka_balanced//file > ./config.yaml
   benthos -c ./config.yaml`[4:],
		Flags: flags,
		Action: func(c *cli.Context) error {
			if c.Bool("version") {
				cmdVersion(Version, DateBuilt)
			}
			if c.Args().Len() > 0 {
				fmt.Fprintf(os.Stderr, "Unrecognised command: %v\n", c.Args().First())
				cli.ShowAppHelp(c)
				os.Exit(1)
			}
			os.Exit(cmdService(c.String("config"), !c.Bool("chilled"), false, nil))
			return nil
		},
		Commands: []*cli.Command{
			{
				Name:  "echo",
				Usage: "Parse a config file and echo back a normalised version",
				Description: `
   This simple command is useful for sanity checking a config if it isn't
   behaving as expected, as it shows you a normalised version after environment
   variables have been resolved:

   benthos -c ./config.yaml echo | less`[4:],
				Action: func(c *cli.Context) error {
					readConfig(c.String("config"))
					outConf, err := conf.Sanitised()
					if err == nil {
						var configYAML []byte
						if configYAML, err = uconfig.MarshalYAML(outConf); err == nil {
							fmt.Println(string(configYAML))
						}
					}
					if err != nil {
						fmt.Fprintln(os.Stderr, fmt.Sprintf("Echo error: %v", err))
						os.Exit(1)
					}
					return nil
				},
			},
			{
				Name:  "lint",
				Usage: "Parse Benthos configs and report any linting errors",
				Description: `
   Exits with a status code 1 if any linting errors are detected:

   benthos -c target.yaml lint
   benthos lint ./configs/*.yaml
   benthos lint ./foo.yaml ./bar.yaml`[4:],
				Action: func(c *cli.Context) error {
					targets := c.Args().Slice()
					if conf := c.String("config"); len(conf) > 0 {
						targets = append(targets, conf)
					}
					var pathLints []string
					for _, target := range targets {
						if len(target) == 0 {
							continue
						}
						var conf = config.New()
						lints, err := config.Read(target, true, &conf)
						if err != nil {
							fmt.Fprintf(os.Stderr, "Configuration file read error: %v\n", err)
							os.Exit(1)
						}
						for _, l := range lints {
							pathLints = append(pathLints, target+": "+l)
						}
					}
					if len(pathLints) == 0 {
						os.Exit(0)
					}
					for _, lint := range pathLints {
						fmt.Fprintln(os.Stderr, lint)
					}
					os.Exit(1)
					return nil
				},
			},
			{
				Name:  "streams",
				Usage: "Run Benthos in streams mode",
				Description: `
   Run Benthos in streams mode, where multiple pipelines can be executed in a
   single process and can be created, updated and removed via REST HTTP
   endpoints.

   benthos streams ./path/to/stream/configs ./and/some/more
   benthos -c ./root_config.yaml streams ./path/to/stream/configs
   benthos -c ./root_config.yaml streams

   In streams mode the stream fields of a root target config (input, buffer,
   pipeline, output) will be ignored. Other fields will be shared across all
   loaded streams (resources, metrics, etc).

   For more information check out the docs at:
   https://benthos.dev/docs/guides/streams_mode/about`[4:],
				Action: func(c *cli.Context) error {
					os.Exit(cmdService(c.String("config"), !c.Bool("chilled"), true, c.Args().Slice()))
					return nil
				},
			},
			{
				Name:  "list",
				Usage: "List all Benthos component types",
				Description: `
   If any component types are explicitly listed then only types of those
   components will be shown.

   benthos list
   benthos list inputs output
   benthos list rate-limits buffers`[4:],
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:  "format",
						Value: "text",
						Usage: "Print the component list in a specific format. Options are text or json.",
					},
				},
				Action: func(c *cli.Context) error {
					listComponents(c)
					os.Exit(0)
					return nil
				},
			},
			{
				Name:  "create",
				Usage: "Create a new Benthos config",
				Description: `
   Prints a new Benthos config to stdout containing specified components
   according to an expression. The expression must take the form of three
   comma-separated lists of inputs, processors and outputs, divided by
   forward slashes:

   benthos create stdin/jmespath,awk/nats
   benthos create file,http_server/json/http_client

   If the expression is omitted a default config is created.`[4:],
				Action: func(c *cli.Context) error {
					if expression := c.Args().First(); len(expression) > 0 {
						if err := addExpression(&conf, expression); err != nil {
							fmt.Fprintln(os.Stderr, fmt.Sprintf("Generate error: %v", err))
							os.Exit(1)
						}
					}
					outConf, err := conf.Sanitised()
					if err == nil {
						var configYAML []byte
						if configYAML, err = uconfig.MarshalYAML(outConf); err == nil {
							fmt.Println(string(configYAML))
						}
					}
					if err != nil {
						fmt.Fprintln(os.Stderr, fmt.Sprintf("Generate error: %v", err))
						os.Exit(1)
					}
					return nil
				},
			},
			test.CliCommand(testSuffix),
			blobl.CliCommand(),
		},
	}

	app.OnUsageError = func(context *cli.Context, err error, isSubcommand bool) error {
		flags, notDeprecated := checkDeprecatedFlags(os.Args[1:])
		if !notDeprecated {
			fmt.Printf("Usage error: %v\n", err)
			cli.ShowAppHelp(context)
			return err
		}

		showVersion := flags.Bool(
			"version", false, "Display version info, then exit",
		)
		configPath := flags.String(
			"c", "", "Path to a configuration file",
		)

		flags.Usage = func() {
			cli.ShowAppHelp(context)
		}

		flags.Parse(os.Args[1:])
		if *showVersion {
			cmdVersion(Version, DateBuilt)
		}

		deprecatedExecute(*configPath, testSuffix)
		os.Exit(cmdService(*configPath, false, false, nil))
		return nil
	}

	app.Run(os.Args)
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>package libkbfs

// Version is the current version (should be MAJOR.MINOR.PATCH)
const Version = "1.0.0"

// Build is the current build number
const Build = "29"
<commit_msg>Bump build number<commit_after>package libkbfs

// Version is the current version (should be MAJOR.MINOR.PATCH)
const Version = "1.0.0"

// Build is the current build number
const Build = "30"
<|endoftext|>


<commit_before>package aranGO

import (
	"errors"
	"strings"
)

type Document struct {
	Id  string `json:"_id,omitempty"  `
	Rev string `json:"_rev,omitempty" `
	Key string `json:"_key,omitempty" `

	Error   bool   `json:"error,omitempty"`
	Message string `json:"errorMessage,omitempty"`
  /*
	Code    int    `json:"code,omitempty"`
	Num     int    `json:"errorNum,omitempty"`
  */
}

func NewDocument(id string) (*Document, error) {
	// some basic validation
	sid := strings.Split(id, "/")
	if len(sid) != 2 {
		return nil, errors.New("Invalid id")
	}
	if id == "" {
		return nil, errors.New("Invalid empty id")
	}
	var d Document
	d.Id = id
	d.Key = sid[1]
	return &d, nil
}

func (d *Document) SetKey(key string) error {
	//valitated key
	d.Key = key
	return nil
}

func (d *Document) SetRev(rev string) error {
	d.Rev = rev
	return nil
}

// Check if a document was updated
func (d *Document) Updated(db *Database) (bool,error){
	if db == nil {
		return false, errors.New("Invalid db")
	}
  // check document id and rev
  if d.Id == "" || d.Rev == "" {
    return false, errors.New("Document must exist or have valid _rev and _id")
  }
  // add revision id
  res, err := db.get("document",d.Id + "?rev="+d.Rev,"GET",nil,nil,nil)

  if err != nil{
    return false,err
  }

  switch res.Status(){
    case 404:
       return true,nil
    case 412:
       return true,nil
    default:
       return false,nil
  }
}

// Check if document exist
func (d *Document) Exist(db *Database) (bool, error) {

	if db == nil {
		return false, errors.New("Invalid db")
	}
  // check document id and rev
  if d.Id == "" {
    return false, errors.New("Document must exist or have valid _rev and _id")
  }
  // add revision id
  res, err := db.get("document",d.Id,"GET",nil,nil,nil)

  if err != nil{
    return false,err
  }

  switch res.Status(){
    case 404:
       return false,nil
    default:
       return true,nil
  }
}

<commit_msg>no error msg in json<commit_after>package aranGO

import (
	"errors"
	"strings"
)

type Document struct {
	Id  string `json:"_id,omitempty"  `
	Rev string `json:"_rev,omitempty" `
	Key string `json:"_key,omitempty" `

	Error   bool   `json:"-"`
	Message string `json:"-"`
  /*
	Code    int    `json:"code,omitempty"`
	Num     int    `json:"errorNum,omitempty"`
  */
}

func NewDocument(id string) (*Document, error) {
	// some basic validation
	sid := strings.Split(id, "/")
	if len(sid) != 2 {
		return nil, errors.New("Invalid id")
	}
	if id == "" {
		return nil, errors.New("Invalid empty id")
	}
	var d Document
	d.Id = id
	d.Key = sid[1]
	return &d, nil
}

func (d *Document) SetKey(key string) error {
	//valitated key
	d.Key = key
	return nil
}

func (d *Document) SetRev(rev string) error {
	d.Rev = rev
	return nil
}

// Check if a document was updated
func (d *Document) Updated(db *Database) (bool,error){
	if db == nil {
		return false, errors.New("Invalid db")
	}
  // check document id and rev
  if d.Id == "" || d.Rev == "" {
    return false, errors.New("Document must exist or have valid _rev and _id")
  }
  // add revision id
  res, err := db.get("document",d.Id + "?rev="+d.Rev,"GET",nil,nil,nil)

  if err != nil{
    return false,err
  }

  switch res.Status(){
    case 404:
       return true,nil
    case 412:
       return true,nil
    default:
       return false,nil
  }
}

// Check if document exist
func (d *Document) Exist(db *Database) (bool, error) {

	if db == nil {
		return false, errors.New("Invalid db")
	}
  // check document id and rev
  if d.Id == "" {
    return false, errors.New("Document must exist or have valid _rev and _id")
  }
  // add revision id
  res, err := db.get("document",d.Id,"GET",nil,nil,nil)

  if err != nil{
    return false,err
  }

  switch res.Status(){
    case 404:
       return false,nil
    default:
       return true,nil
  }
}

<|endoftext|>