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

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"

	"github.com/richardlehane/siegfried"
	"github.com/richardlehane/siegfried/config"
	"github.com/richardlehane/siegfried/pkg/core/siegreader"

	// Uncomment to build with profiler
	//"net/http"
	//_ "net/http/pprof"
)

const PROCS = -1

// flags
var (
	update  = flag.Bool("update", false, "update or install the default signature file")
	version = flag.Bool("version", false, "display version information")
	logf    = flag.String("log", "", "log errors, warnings, debug or slow output, knowns or unknowns to stderr or stdout e.g. -log error,warn,unknown,stdout")
	nr      = flag.Bool("nr", false, "prevent automatic directory recursion")
	csvo    = flag.Bool("csv", false, "CSV output format")
	jsono   = flag.Bool("json", false, "JSON output format")
	droido  = flag.Bool("droid", false, "DROID CSV output format")
	sig     = flag.String("sig", config.SignatureBase(), "set the signature file")
	home    = flag.String("home", config.Home(), "override the default home directory")
	serve   = flag.String("serve", "", "start siegfried server e.g. -serve localhost:5138")
	multi   = flag.Int("multi", 1, "set number of file ID processes")
	archive = flag.Bool("z", false, "scan archive formats (zip, tar, gzip, warc, arc)")
	hashf   = flag.String("hash", "", "calculate file checksum with hash algorithm; options "+hashChoices)
)

type res struct {
	path string
	sz   int64
	mod  string
	c    iterableID
	err  error
}

func printer(w writer, resc chan chan res, wg *sync.WaitGroup) {
	for rr := range resc {
		r := <-rr
		w.writeFile(r.path, r.sz, r.mod, nil, r.err, r.c)
		wg.Done()
	}
}

func multiIdentifyP(w writer, s *siegfried.Siegfried, r string, norecurse bool) {
	wg := &sync.WaitGroup{}
	runtime.GOMAXPROCS(PROCS)
	resc := make(chan chan res, *multi)
	go printer(w, resc, wg)
	wf := func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			if norecurse && path != r {
				return filepath.SkipDir
			}
			if *droido {
				wg.Add(1)
				rchan := make(chan res, 1)
				resc <- rchan
				go func() {
					rchan <- res{path, -1, info.ModTime().String(), nil, nil} // write directory with a -1 size for droid output only
				}()
			}
			return nil
		}
		wg.Add(1)
		rchan := make(chan res, 1)
		resc <- rchan
		go func() {
			f, err := os.Open(path)
			if err != nil {
				rchan <- res{path, 0, "", nil, err}
				return
			}
			c, err := s.Identify(f, path, "")
			if c == nil {
				f.Close()
				rchan <- res{path, 0, "", nil, err}
				return
			}
			ids := makeIdSlice(idChan(c))
			f.Close()
			rchan <- res{path, info.Size(), info.ModTime().Format(time.RFC3339), ids, err}
		}()
		return nil
	}
	filepath.Walk(r, wf)
	wg.Wait()
	close(resc)
}

func multiIdentifyS(w writer, s *siegfried.Siegfried, r string, norecurse bool) error {
	wf := func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			if norecurse && path != r {
				return filepath.SkipDir
			}
			if *droido {
				w.writeFile(path, -1, info.ModTime().Format(time.RFC3339), nil, nil, nil) // write directory with a -1 size for droid output only
			}
			return nil
		}
		identifyFile(w, s, path, info.Size(), info.ModTime().Format(time.RFC3339))
		return nil
	}
	return filepath.Walk(r, wf)
}

func identifyFile(w writer, s *siegfried.Siegfried, path string, sz int64, mod string) {
	f, err := os.Open(path)
	if err != nil {
		w.writeFile(path, sz, mod, nil, err, nil)
		// log the error too
		lg.set(path)
		lg.err(err)
		lg.reset()
		return
	}
	identifyRdr(w, s, f, sz, path, "", mod)
	f.Close()
}

func identifyRdr(w writer, s *siegfried.Siegfried, r io.Reader, sz int64, path, mime, mod string) {
	lg.set(path)
	c, err := s.Identify(r, path, mime)
	lg.err(err)
	if c == nil {
		w.writeFile(path, sz, mod, nil, err, nil)
		lg.reset()
		return
	}
	var b siegreader.Buffer
	var cs []byte
	if checksum != nil {
		b = s.Buffer()
		var i int64
		l := checksum.BlockSize()
		for ; ; i += int64(l) {
			buf, _ := b.Slice(i, l)
			if buf == nil {
				break
			}
			checksum.Write(buf)
		}
		cs = checksum.Sum(nil)
		checksum.Reset()
	}
	a := w.writeFile(path, sz, mod, cs, err, idChan(c))
	lg.reset()
	if !*archive || a == config.None {
		return
	}
	var d decompressor
	if b == nil {
		b = s.Buffer()
	}
	switch a {
	case config.Zip:
		d, err = newZip(siegreader.ReaderFrom(b), path, sz)
	case config.Gzip:
		d, err = newGzip(b, path)
	case config.Tar:
		d, err = newTar(siegreader.ReaderFrom(b), path)
	case config.ARC:
		d, err = newARC(siegreader.ReaderFrom(b), path)
	case config.WARC:
		d, err = newWARC(siegreader.ReaderFrom(b), path)
	}
	if err != nil {
		err = fmt.Errorf("failed to decompress %s, got: %v", path, err)
		w.writeFile(path, sz, mod, nil, err, nil)
		// log the error
		lg.set(path)
		lg.err(err)
		lg.reset()
		return
	}
	for err = d.next(); err == nil; err = d.next() {
		if *droido {
			for _, v := range d.dirs() {
				w.writeFile(v, -1, "", nil, nil, nil)
			}
		}
		identifyRdr(w, s, d.reader(), d.size(), d.path(), d.mime(), d.mod())
	}
}

func main() {

	flag.Parse()

	/*//UNCOMMENT TO RUN PROFILER
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()*/

	if *version {
		version := config.Version()
		fmt.Printf("siegfried %d.%d.%d\n", version[0], version[1], version[2])
		s, err := siegfried.Load(config.Signature())
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Print(s)
		return
	}

	if *home != config.Home() {
		config.SetHome(*home)
	}

	if *sig != config.SignatureBase() {
		config.SetSignature(*sig)
	}

	if *update {
		msg, err := updateSigs()
		if err != nil {
			log.Fatalf("Error: failed to update signature file, %v", err)
		}
		fmt.Println(msg)
		return
	}

	// during parallel scanning or in server mode, unsafe to access the last read buffer - so can't unzip or hash
	if *multi > 1 || *serve != "" {
		if *archive {
			log.Fatalln("Error: cannot scan archive formats when running in parallel mode")
		}
		if *hashf != "" {
			log.Fatalln("Error: cannot calculate file checksum when running in parallel mode")
		}
	}

	if *logf != "" {
		if *multi > 1 {
			log.Fatalln("Error: cannot log when running in parallel mode")
		}
		if err := newLogger(*logf); err != nil {
			log.Fatalln(err)
		}
	}

	if err := setHash(); err != nil {
		log.Fatal(err)
	}

	if *serve != "" || *fprflag {
		s, err := siegfried.Load(config.Signature())
		if err != nil {
			log.Fatalf("Error: error loading signature file, got: %v", err)
		}
		if *serve != "" {
			log.Printf("Starting server at %s. Use CTRL-C to quit.\n", *serve)
			listen(*serve, s)
			return
		}
		log.Printf("FPR server started at %s. Use CTRL-C to quit.\n", config.Fpr())
		serveFpr(config.Fpr(), s)
		return
	}

	if flag.NArg() != 1 {
		log.Fatalln("Error: expecting a single file or directory argument")
	}

	s, err := siegfried.Load(config.Signature())
	if err != nil {
		log.Fatalf("Error: error loading signature file, got: %v", err)
	}

	var w writer
	switch {
	case *csvo:
		w = newCSV(os.Stdout)
	case *jsono:
		w = newJSON(os.Stdout)
	case *droido:
		w = newDroid(os.Stdout)
	default:
		w = newYAML(os.Stdout)
	}

	if lg != nil && lg.w == os.Stdout {
		w = logWriter{}
	}

	// support reading list files from stdin
	if flag.Arg(0) == "-" {
		w.writeHead(s)
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			info, err := os.Stat(scanner.Text())
			if err != nil || info.IsDir() {
				err = fmt.Errorf("failed to identify %s (in scanning mode, inputs must all be files and not directories), got: %v", scanner.Text(), err)
				w.writeFile(scanner.Text(), 0, "", nil, err, nil)
				// log the error
				lg.set(scanner.Text())
				lg.err(err)
				lg.reset()
			} else {
				identifyFile(w, s, scanner.Text(), info.Size(), info.ModTime().Format(time.RFC3339))
			}
		}
		w.writeTail()
		os.Exit(0)
	}

	info, err := os.Stat(flag.Arg(0))
	if err != nil {
		log.Fatalf("Error: error getting info for %v, got: %v", flag.Arg(0), err)
	}

	if info.IsDir() {
		w.writeHead(s)
		if *multi > 16 {
			*multi = 16
		}
		if *multi > 1 {
			multiIdentifyP(w, s, flag.Arg(0), *nr)
		} else {
			multiIdentifyS(w, s, flag.Arg(0), *nr)
		}
		w.writeTail()
		os.Exit(0)
	}

	w.writeHead(s)
	identifyFile(w, s, flag.Arg(0), info.Size(), info.ModTime().Format(time.RFC3339))
	w.writeTail()
	os.Exit(0)
}
<commit_msg>longpath error<commit_after>// Copyright 2014 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/richardlehane/siegfried"
	"github.com/richardlehane/siegfried/config"
	"github.com/richardlehane/siegfried/pkg/core/siegreader"

	// Uncomment to build with profiler
	//"net/http"
	//_ "net/http/pprof"
)

const PROCS = -1

// flags
var (
	update  = flag.Bool("update", false, "update or install the default signature file")
	version = flag.Bool("version", false, "display version information")
	logf    = flag.String("log", "", "log errors, warnings, debug or slow output, knowns or unknowns to stderr or stdout e.g. -log error,warn,unknown,stdout")
	nr      = flag.Bool("nr", false, "prevent automatic directory recursion")
	csvo    = flag.Bool("csv", false, "CSV output format")
	jsono   = flag.Bool("json", false, "JSON output format")
	droido  = flag.Bool("droid", false, "DROID CSV output format")
	sig     = flag.String("sig", config.SignatureBase(), "set the signature file")
	home    = flag.String("home", config.Home(), "override the default home directory")
	serve   = flag.String("serve", "", "start siegfried server e.g. -serve localhost:5138")
	multi   = flag.Int("multi", 1, "set number of file ID processes")
	archive = flag.Bool("z", false, "scan archive formats (zip, tar, gzip, warc, arc)")
	hashf   = flag.String("hash", "", "calculate file checksum with hash algorithm; options "+hashChoices)
)

func writeError(w writer, path string, sz int64, mod string, err error) {
	w.writeFile(path, sz, mod, nil, err, nil)
	// log the error too
	lg.set(path)
	lg.err(err)
	lg.reset()
}

// longpath code from https://github.com/docker/docker/tree/master/pkg/longpath
// Prefix is the longpath prefix for Windows file paths.
const prefix = `\\?\`

func longpath(path string) string {
	if !strings.HasPrefix(path, prefix) {
		if strings.HasPrefix(path, `\\`) {
			// This is a UNC path, so we need to add 'UNC' to the path as well.
			path = prefix + `UNC` + path[1:]
		} else {
			path = prefix + path
		}
	}
	return path
}

type res struct {
	path string
	sz   int64
	mod  string
	c    iterableID
	err  error
}

func printer(w writer, resc chan chan res, wg *sync.WaitGroup) {
	for rr := range resc {
		r := <-rr
		w.writeFile(r.path, r.sz, r.mod, nil, r.err, r.c)
		wg.Done()
	}
}

func multiIdentifyP(w writer, s *siegfried.Siegfried, r string, norecurse bool) {
	wg := &sync.WaitGroup{}
	runtime.GOMAXPROCS(PROCS)
	resc := make(chan chan res, *multi)
	go printer(w, resc, wg)
	wf := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			// may be a long path error on win
			var lerr error
			info, lerr = os.Stat(longpath(path))
			if lerr != nil {
				wg.Add(1)
				rchan := make(chan res, 1)
				resc <- rchan
				go func() {
					rchan <- res{path, 0, "", nil, err}
				}()
				return nil
			}
		}
		if info.IsDir() {
			if norecurse && path != r {
				return filepath.SkipDir
			}
			if *droido {
				wg.Add(1)
				rchan := make(chan res, 1)
				resc <- rchan
				go func() {
					rchan <- res{path, -1, info.ModTime().String(), nil, nil} // write directory with a -1 size for droid output only
				}()
			}
			return nil
		}
		wg.Add(1)
		rchan := make(chan res, 1)
		resc <- rchan
		go func() {
			f, err := os.Open(path)
			if err != nil {
				var lerr error
				f, lerr = os.Open(longpath(path))
				if lerr != nil {
					rchan <- res{path, 0, "", nil, err}
					return
				}
			}
			c, err := s.Identify(f, path, "")
			if c == nil {
				f.Close()
				rchan <- res{path, 0, "", nil, err}
				return
			}
			ids := makeIdSlice(idChan(c))
			f.Close()
			rchan <- res{path, info.Size(), info.ModTime().Format(time.RFC3339), ids, err}
		}()
		return nil
	}
	filepath.Walk(r, wf)
	wg.Wait()
	close(resc)
}

func multiIdentifyS(w writer, s *siegfried.Siegfried, r string, norecurse bool) error {
	wf := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			// may be a long path error on win
			var lerr error
			info, lerr = os.Stat(longpath(path))
			if lerr != nil {
				writeError(w, path, 0, "", err)
				return nil
			}
		}
		if info.IsDir() {
			if norecurse && path != r {
				return filepath.SkipDir
			}
			if *droido {
				w.writeFile(path, -1, info.ModTime().Format(time.RFC3339), nil, nil, nil) // write directory with a -1 size for droid output only
			}
			return nil
		}
		identifyFile(w, s, path, info.Size(), info.ModTime().Format(time.RFC3339))
		return nil
	}
	return filepath.Walk(r, wf)
}

func identifyFile(w writer, s *siegfried.Siegfried, path string, sz int64, mod string) {
	f, err := os.Open(path)
	if err != nil {
		// may be a long path error on win
		var lerr error
		f, lerr = os.Open(longpath(path))
		if lerr != nil {
			writeError(w, path, sz, mod, err)
			return
		}
	}
	identifyRdr(w, s, f, sz, path, "", mod)
	f.Close()
}

func identifyRdr(w writer, s *siegfried.Siegfried, r io.Reader, sz int64, path, mime, mod string) {
	lg.set(path)
	c, err := s.Identify(r, path, mime)
	lg.err(err)
	if c == nil {
		w.writeFile(path, sz, mod, nil, err, nil)
		lg.reset()
		return
	}
	var b siegreader.Buffer
	var cs []byte
	if checksum != nil {
		b = s.Buffer()
		var i int64
		l := checksum.BlockSize()
		for ; ; i += int64(l) {
			buf, _ := b.Slice(i, l)
			if buf == nil {
				break
			}
			checksum.Write(buf)
		}
		cs = checksum.Sum(nil)
		checksum.Reset()
	}
	a := w.writeFile(path, sz, mod, cs, err, idChan(c))
	lg.reset()
	if !*archive || a == config.None {
		return
	}
	var d decompressor
	if b == nil {
		b = s.Buffer()
	}
	switch a {
	case config.Zip:
		d, err = newZip(siegreader.ReaderFrom(b), path, sz)
	case config.Gzip:
		d, err = newGzip(b, path)
	case config.Tar:
		d, err = newTar(siegreader.ReaderFrom(b), path)
	case config.ARC:
		d, err = newARC(siegreader.ReaderFrom(b), path)
	case config.WARC:
		d, err = newWARC(siegreader.ReaderFrom(b), path)
	}
	if err != nil {
		writeError(w, path, sz, mod, fmt.Errorf("failed to decompress %s, got: %v", path, err))
		return
	}
	for err = d.next(); err == nil; err = d.next() {
		if *droido {
			for _, v := range d.dirs() {
				w.writeFile(v, -1, "", nil, nil, nil)
			}
		}
		identifyRdr(w, s, d.reader(), d.size(), d.path(), d.mime(), d.mod())
	}
}

func main() {

	flag.Parse()

	/*//UNCOMMENT TO RUN PROFILER
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()*/

	if *version {
		version := config.Version()
		fmt.Printf("siegfried %d.%d.%d\n", version[0], version[1], version[2])
		s, err := siegfried.Load(config.Signature())
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Print(s)
		return
	}

	if *home != config.Home() {
		config.SetHome(*home)
	}

	if *sig != config.SignatureBase() {
		config.SetSignature(*sig)
	}

	if *update {
		msg, err := updateSigs()
		if err != nil {
			log.Fatalf("Error: failed to update signature file, %v", err)
		}
		fmt.Println(msg)
		return
	}

	// during parallel scanning or in server mode, unsafe to access the last read buffer - so can't unzip or hash
	if *multi > 1 || *serve != "" {
		if *archive {
			log.Fatalln("Error: cannot scan archive formats when running in parallel mode")
		}
		if *hashf != "" {
			log.Fatalln("Error: cannot calculate file checksum when running in parallel mode")
		}
	}

	if *logf != "" {
		if *multi > 1 {
			log.Fatalln("Error: cannot log when running in parallel mode")
		}
		if err := newLogger(*logf); err != nil {
			log.Fatalln(err)
		}
	}

	if err := setHash(); err != nil {
		log.Fatal(err)
	}

	if *serve != "" || *fprflag {
		s, err := siegfried.Load(config.Signature())
		if err != nil {
			log.Fatalf("Error: error loading signature file, got: %v", err)
		}
		if *serve != "" {
			log.Printf("Starting server at %s. Use CTRL-C to quit.\n", *serve)
			listen(*serve, s)
			return
		}
		log.Printf("FPR server started at %s. Use CTRL-C to quit.\n", config.Fpr())
		serveFpr(config.Fpr(), s)
		return
	}

	if flag.NArg() != 1 {
		log.Fatalln("Error: expecting a single file or directory argument")
	}

	s, err := siegfried.Load(config.Signature())
	if err != nil {
		log.Fatalf("Error: error loading signature file, got: %v", err)
	}

	var w writer
	switch {
	case *csvo:
		w = newCSV(os.Stdout)
	case *jsono:
		w = newJSON(os.Stdout)
	case *droido:
		w = newDroid(os.Stdout)
	default:
		w = newYAML(os.Stdout)
	}

	if lg != nil && lg.w == os.Stdout {
		w = logWriter{}
	}

	// support reading list files from stdin
	if flag.Arg(0) == "-" {
		w.writeHead(s)
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			info, err := os.Stat(scanner.Text())
			if err != nil {
				info, err = os.Stat(longpath(scanner.Text()))
			}
			if err != nil || info.IsDir() {
				writeError(w, scanner.Text(), 0, "", fmt.Errorf("failed to identify %s (in scanning mode, inputs must all be files and not directories), got: %v", scanner.Text(), err))
			} else {
				identifyFile(w, s, scanner.Text(), info.Size(), info.ModTime().Format(time.RFC3339))
			}
		}
		w.writeTail()
		os.Exit(0)
	}

	info, err := os.Stat(flag.Arg(0))
	if err != nil {
		var lerr error
		info, lerr = os.Stat(longpath(flag.Arg(0)))
		if lerr != nil {
			log.Fatalf("Error: error getting info for %v, got: %v", flag.Arg(0), err)
		}
	}

	if info.IsDir() {
		w.writeHead(s)
		if *multi > 16 {
			*multi = 16
		}
		if *multi > 1 {
			multiIdentifyP(w, s, flag.Arg(0), *nr)
		} else {
			multiIdentifyS(w, s, flag.Arg(0), *nr)
		}
		w.writeTail()
		os.Exit(0)
	}

	w.writeHead(s)
	identifyFile(w, s, flag.Arg(0), info.Size(), info.ModTime().Format(time.RFC3339))
	w.writeTail()
	os.Exit(0)
}
<|endoftext|>


<commit_before>package repo

import (
	"bytes"
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"io"

	"github.com/kopia/repo/storage"
)

const defaultFormatEncryption = "AES256_GCM"

const (
	maxChecksummedFormatBytesLength = 65000
	formatBlockChecksumSize         = sha256.Size
)

// FormatBlockID is the identifier of a storage block that describes repository format.
const FormatBlockID = "kopia.repository"

var (
	purposeAESKey   = []byte("AES")
	purposeAuthData = []byte("CHECKSUM")

	errFormatBlockNotFound = errors.New("format block not found")
)

type formatBlock struct {
	Tool         string `json:"tool"`
	BuildVersion string `json:"buildVersion"`
	BuildInfo    string `json:"buildInfo"`

	UniqueID               []byte `json:"uniqueID"`
	KeyDerivationAlgorithm string `json:"keyAlgo"`

	Version              string                  `json:"version"`
	EncryptionAlgorithm  string                  `json:"encryption"`
	EncryptedFormatBytes []byte                  `json:"encryptedBlockFormat,omitempty"`
	UnencryptedFormat    *repositoryObjectFormat `json:"blockFormat,omitempty"`
}

// encryptedRepositoryConfig contains the configuration of repository that's persisted in encrypted format.
type encryptedRepositoryConfig struct {
	Format repositoryObjectFormat `json:"format"`
}

func parseFormatBlock(b []byte) (*formatBlock, error) {
	f := &formatBlock{}

	if err := json.Unmarshal(b, &f); err != nil {
		return nil, fmt.Errorf("invalid format block: %v", err)
	}

	return f, nil
}

// RecoverFormatBlock attempts to recover format block replica from the specified file.
// The format block can be either the prefix or a suffix of the given file.
func RecoverFormatBlock(ctx context.Context, st storage.Storage, filename string) ([]byte, error) {
	var foundMetadata storage.BlockMetadata

	if err := st.ListBlocks(ctx, filename, func(bm storage.BlockMetadata) error {
		if foundMetadata.BlockID != "" {
			return fmt.Errorf("found multiple blocks with a given prefix: %v", filename)
		}
		foundMetadata = bm
		return nil
	}); err != nil {
		return nil, fmt.Errorf("error: %v", err)
	}

	if foundMetadata.BlockID == "" {
		return nil, storage.ErrBlockNotFound
	}

	return recoverFormatBlockWithLength(ctx, st, foundMetadata.BlockID, foundMetadata.Length)
}

func recoverFormatBlockWithLength(ctx context.Context, st storage.Storage, filename string, length int64) ([]byte, error) {
	chunkLength := int64(65536)
	if chunkLength > length {
		chunkLength = length
	}

	if chunkLength > 4 {

		// try prefix
		prefixChunk, err := st.GetBlock(ctx, filename, 0, chunkLength)
		if err != nil {
			return nil, err
		}
		if l := int(prefixChunk[0]) + int(prefixChunk[1])<<8; l <= maxChecksummedFormatBytesLength && l+2 < len(prefixChunk) {
			if b, ok := verifyFormatBlockChecksum(prefixChunk[2 : 2+l]); ok {
				return b, nil
			}
		}

		// try the suffix
		suffixChunk, err := st.GetBlock(ctx, filename, length-chunkLength, chunkLength)
		if err != nil {
			return nil, err
		}
		if l := int(suffixChunk[len(suffixChunk)-2]) + int(suffixChunk[len(suffixChunk)-1])<<8; l <= maxChecksummedFormatBytesLength && l+2 < len(suffixChunk) {
			if b, ok := verifyFormatBlockChecksum(suffixChunk[len(suffixChunk)-2-l : len(suffixChunk)-2]); ok {
				return b, nil
			}
		}
	}

	return nil, errFormatBlockNotFound
}

func verifyFormatBlockChecksum(b []byte) ([]byte, bool) {
	if len(b) < formatBlockChecksumSize {
		return nil, false
	}

	data, checksum := b[0:len(b)-formatBlockChecksumSize], b[len(b)-formatBlockChecksumSize:]
	h := sha256.New()
	h.Write(data) //nolint:errcheck
	actualChecksum := h.Sum(nil)
	if !hmac.Equal(actualChecksum, checksum) {
		return nil, false
	}

	return data, true
}

func writeFormatBlock(ctx context.Context, st storage.Storage, f *formatBlock) error {
	var buf bytes.Buffer
	e := json.NewEncoder(&buf)
	e.SetIndent("", "  ")
	if err := e.Encode(f); err != nil {
		return fmt.Errorf("unable to marshal format block: %v", err)
	}

	if err := st.PutBlock(ctx, FormatBlockID, buf.Bytes()); err != nil {
		return fmt.Errorf("unable to write format block: %v", err)
	}

	return nil
}

func (f *formatBlock) decryptFormatBytes(masterKey []byte) (*repositoryObjectFormat, error) {
	switch f.EncryptionAlgorithm {
	case "NONE": // do nothing
		return f.UnencryptedFormat, nil

	case "AES256_GCM":
		aead, authData, err := initCrypto(masterKey, f.UniqueID)
		if err != nil {
			return nil, fmt.Errorf("cannot initialize cipher: %v", err)
		}

		content := append([]byte(nil), f.EncryptedFormatBytes...)
		if len(content) < aead.NonceSize() {
			return nil, fmt.Errorf("invalid encrypted payload, too short")
		}
		nonce := content[0:aead.NonceSize()]
		payload := content[aead.NonceSize():]

		plainText, err := aead.Open(payload[:0], nonce, payload, authData)
		if err != nil {
			return nil, fmt.Errorf("unable to decrypt repository format, invalid credentials?")
		}

		var erc encryptedRepositoryConfig
		if err := json.Unmarshal(plainText, &erc); err != nil {
			return nil, fmt.Errorf("invalid repository format: %v", err)
		}

		return &erc.Format, nil

	default:
		return nil, fmt.Errorf("unknown encryption algorithm: '%v'", f.EncryptionAlgorithm)
	}
}

func initCrypto(masterKey, repositoryID []byte) (cipher.AEAD, []byte, error) {
	aesKey := deriveKeyFromMasterKey(masterKey, repositoryID, purposeAESKey, 32)
	authData := deriveKeyFromMasterKey(masterKey, repositoryID, purposeAuthData, 32)

	blk, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create cipher: %v", err)
	}
	aead, err := cipher.NewGCM(blk)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create cipher: %v", err)
	}

	return aead, authData, nil
}

func encryptFormatBytes(f *formatBlock, format *repositoryObjectFormat, masterKey, repositoryID []byte) error {
	switch f.EncryptionAlgorithm {
	case "NONE":
		f.UnencryptedFormat = format
		return nil

	case "AES256_GCM":
		content, err := json.Marshal(&encryptedRepositoryConfig{Format: *format})
		if err != nil {
			return fmt.Errorf("can't marshal format to JSON: %v", err)
		}
		aead, authData, err := initCrypto(masterKey, repositoryID)
		if err != nil {
			return fmt.Errorf("unable to initialize crypto: %v", err)
		}
		nonceLength := aead.NonceSize()
		noncePlusContentLength := nonceLength + len(content)
		cipherText := make([]byte, noncePlusContentLength+aead.Overhead())

		// Store nonce at the beginning of ciphertext.
		nonce := cipherText[0:nonceLength]
		if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
			return err
		}

		b := aead.Seal(cipherText[nonceLength:nonceLength], nonce, content, authData)
		content = nonce[0 : nonceLength+len(b)]
		f.EncryptedFormatBytes = content
		return nil

	default:
		return fmt.Errorf("unknown encryption algorithm: '%v'", f.EncryptionAlgorithm)
	}
}

func addFormatBlockChecksumAndLength(fb []byte) ([]byte, error) {
	h := sha256.New()
	h.Write(fb) //nolint:errcheck
	checksummedFormatBytes := h.Sum(fb)

	l := len(checksummedFormatBytes)
	if l > maxChecksummedFormatBytesLength {
		return nil, fmt.Errorf("format block too big: %v", l)
	}

	// return <length><checksummed-bytes><length>
	result := append([]byte(nil), byte(l), byte(l>>8))
	result = append(result, checksummedFormatBytes...)
	result = append(result, byte(l), byte(l>>8))
	return result, nil
}
<commit_msg>added hmac to format block embedded in pack indexes<commit_after>package repo

import (
	"bytes"
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"io"

	"github.com/kopia/repo/storage"
)

const defaultFormatEncryption = "AES256_GCM"

const (
	maxChecksummedFormatBytesLength = 65000
	formatBlockChecksumSize         = sha256.Size
)

// formatBlockChecksumSecret is a HMAC secret used for checksumming the format block.
// It's not really a secret, but will provide positive identification of blocks that
// are repository format blocks.
var formatBlockChecksumSecret = []byte("kopia-repository")

// FormatBlockID is the identifier of a storage block that describes repository format.
const FormatBlockID = "kopia.repository"

var (
	purposeAESKey   = []byte("AES")
	purposeAuthData = []byte("CHECKSUM")

	errFormatBlockNotFound = errors.New("format block not found")
)

type formatBlock struct {
	Tool         string `json:"tool"`
	BuildVersion string `json:"buildVersion"`
	BuildInfo    string `json:"buildInfo"`

	UniqueID               []byte `json:"uniqueID"`
	KeyDerivationAlgorithm string `json:"keyAlgo"`

	Version              string                  `json:"version"`
	EncryptionAlgorithm  string                  `json:"encryption"`
	EncryptedFormatBytes []byte                  `json:"encryptedBlockFormat,omitempty"`
	UnencryptedFormat    *repositoryObjectFormat `json:"blockFormat,omitempty"`
}

// encryptedRepositoryConfig contains the configuration of repository that's persisted in encrypted format.
type encryptedRepositoryConfig struct {
	Format repositoryObjectFormat `json:"format"`
}

func parseFormatBlock(b []byte) (*formatBlock, error) {
	f := &formatBlock{}

	if err := json.Unmarshal(b, &f); err != nil {
		return nil, fmt.Errorf("invalid format block: %v", err)
	}

	return f, nil
}

// RecoverFormatBlock attempts to recover format block replica from the specified file.
// The format block can be either the prefix or a suffix of the given file.
func RecoverFormatBlock(ctx context.Context, st storage.Storage, filename string) ([]byte, error) {
	var foundMetadata storage.BlockMetadata

	if err := st.ListBlocks(ctx, filename, func(bm storage.BlockMetadata) error {
		if foundMetadata.BlockID != "" {
			return fmt.Errorf("found multiple blocks with a given prefix: %v", filename)
		}
		foundMetadata = bm
		return nil
	}); err != nil {
		return nil, fmt.Errorf("error: %v", err)
	}

	if foundMetadata.BlockID == "" {
		return nil, storage.ErrBlockNotFound
	}

	return recoverFormatBlockWithLength(ctx, st, foundMetadata.BlockID, foundMetadata.Length)
}

func recoverFormatBlockWithLength(ctx context.Context, st storage.Storage, filename string, length int64) ([]byte, error) {
	chunkLength := int64(65536)
	if chunkLength > length {
		chunkLength = length
	}

	if chunkLength > 4 {

		// try prefix
		prefixChunk, err := st.GetBlock(ctx, filename, 0, chunkLength)
		if err != nil {
			return nil, err
		}
		if l := int(prefixChunk[0]) + int(prefixChunk[1])<<8; l <= maxChecksummedFormatBytesLength && l+2 < len(prefixChunk) {
			if b, ok := verifyFormatBlockChecksum(prefixChunk[2 : 2+l]); ok {
				return b, nil
			}
		}

		// try the suffix
		suffixChunk, err := st.GetBlock(ctx, filename, length-chunkLength, chunkLength)
		if err != nil {
			return nil, err
		}
		if l := int(suffixChunk[len(suffixChunk)-2]) + int(suffixChunk[len(suffixChunk)-1])<<8; l <= maxChecksummedFormatBytesLength && l+2 < len(suffixChunk) {
			if b, ok := verifyFormatBlockChecksum(suffixChunk[len(suffixChunk)-2-l : len(suffixChunk)-2]); ok {
				return b, nil
			}
		}
	}

	return nil, errFormatBlockNotFound
}

func verifyFormatBlockChecksum(b []byte) ([]byte, bool) {
	if len(b) < formatBlockChecksumSize {
		return nil, false
	}

	data, checksum := b[0:len(b)-formatBlockChecksumSize], b[len(b)-formatBlockChecksumSize:]
	h := hmac.New(sha256.New, formatBlockChecksumSecret)
	h.Write(data) //nolint:errcheck
	actualChecksum := h.Sum(nil)
	if !hmac.Equal(actualChecksum, checksum) {
		return nil, false
	}

	return data, true
}

func writeFormatBlock(ctx context.Context, st storage.Storage, f *formatBlock) error {
	var buf bytes.Buffer
	e := json.NewEncoder(&buf)
	e.SetIndent("", "  ")
	if err := e.Encode(f); err != nil {
		return fmt.Errorf("unable to marshal format block: %v", err)
	}

	if err := st.PutBlock(ctx, FormatBlockID, buf.Bytes()); err != nil {
		return fmt.Errorf("unable to write format block: %v", err)
	}

	return nil
}

func (f *formatBlock) decryptFormatBytes(masterKey []byte) (*repositoryObjectFormat, error) {
	switch f.EncryptionAlgorithm {
	case "NONE": // do nothing
		return f.UnencryptedFormat, nil

	case "AES256_GCM":
		aead, authData, err := initCrypto(masterKey, f.UniqueID)
		if err != nil {
			return nil, fmt.Errorf("cannot initialize cipher: %v", err)
		}

		content := append([]byte(nil), f.EncryptedFormatBytes...)
		if len(content) < aead.NonceSize() {
			return nil, fmt.Errorf("invalid encrypted payload, too short")
		}
		nonce := content[0:aead.NonceSize()]
		payload := content[aead.NonceSize():]

		plainText, err := aead.Open(payload[:0], nonce, payload, authData)
		if err != nil {
			return nil, fmt.Errorf("unable to decrypt repository format, invalid credentials?")
		}

		var erc encryptedRepositoryConfig
		if err := json.Unmarshal(plainText, &erc); err != nil {
			return nil, fmt.Errorf("invalid repository format: %v", err)
		}

		return &erc.Format, nil

	default:
		return nil, fmt.Errorf("unknown encryption algorithm: '%v'", f.EncryptionAlgorithm)
	}
}

func initCrypto(masterKey, repositoryID []byte) (cipher.AEAD, []byte, error) {
	aesKey := deriveKeyFromMasterKey(masterKey, repositoryID, purposeAESKey, 32)
	authData := deriveKeyFromMasterKey(masterKey, repositoryID, purposeAuthData, 32)

	blk, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create cipher: %v", err)
	}
	aead, err := cipher.NewGCM(blk)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create cipher: %v", err)
	}

	return aead, authData, nil
}

func encryptFormatBytes(f *formatBlock, format *repositoryObjectFormat, masterKey, repositoryID []byte) error {
	switch f.EncryptionAlgorithm {
	case "NONE":
		f.UnencryptedFormat = format
		return nil

	case "AES256_GCM":
		content, err := json.Marshal(&encryptedRepositoryConfig{Format: *format})
		if err != nil {
			return fmt.Errorf("can't marshal format to JSON: %v", err)
		}
		aead, authData, err := initCrypto(masterKey, repositoryID)
		if err != nil {
			return fmt.Errorf("unable to initialize crypto: %v", err)
		}
		nonceLength := aead.NonceSize()
		noncePlusContentLength := nonceLength + len(content)
		cipherText := make([]byte, noncePlusContentLength+aead.Overhead())

		// Store nonce at the beginning of ciphertext.
		nonce := cipherText[0:nonceLength]
		if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
			return err
		}

		b := aead.Seal(cipherText[nonceLength:nonceLength], nonce, content, authData)
		content = nonce[0 : nonceLength+len(b)]
		f.EncryptedFormatBytes = content
		return nil

	default:
		return fmt.Errorf("unknown encryption algorithm: '%v'", f.EncryptionAlgorithm)
	}
}

func addFormatBlockChecksumAndLength(fb []byte) ([]byte, error) {
	h := hmac.New(sha256.New, formatBlockChecksumSecret)
	h.Write(fb) //nolint:errcheck
	checksummedFormatBytes := h.Sum(fb)

	l := len(checksummedFormatBytes)
	if l > maxChecksummedFormatBytesLength {
		return nil, fmt.Errorf("format block too big: %v", l)
	}

	// return <length><checksummed-bytes><length>
	result := append([]byte(nil), byte(l), byte(l>>8))
	result = append(result, checksummedFormatBytes...)
	result = append(result, byte(l), byte(l>>8))
	return result, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"time"
)

// Event represents an event entry (over websocket)
//
// swagger:model
type Event struct {
	// Event type (one of operation, logging or lifecycle)
	// Example: lifecycle
	Type string `yaml:"type" json:"type"`

	// Time at which the event was sent
	// Example: 2021-02-24T19:00:45.452649098-05:00
	Timestamp time.Time `yaml:"timestamp" json:"timestamp"`

	// JSON encoded metadata (see EventLogging, EventLifecycle or Operation)
	// Example: {"action": "instance-started", "source": "/1.0/instances/c1", "context": {}}
	Metadata json.RawMessage `yaml:"metadata" json:"metadata"`

	// Originating cluster member
	// Example: lxd01
	//
	// API extension: event_location
	Location string `yaml:"location,omitempty" json:"location,omitempty"`
}

// ToLogging creates log record for the event
func (event *Event) ToLogging() (EventLogRecord, error) {
	if event.Type == "logging" {
		e := &EventLogging{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  e.Level,
			Msg:  e.Message,
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "lifecycle" {
		e := &EventLifecycle{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("Action: %s, Source: %s", e.Action, e.Source),
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "operation" {
		e := &Operation{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("ID: %s, Class: %s, Description: %s", e.ID, e.Class, e.Description),
			Ctx: []interface{}{
				"CreatedAt", e.CreatedAt,
				"UpdatedAt", e.UpdatedAt,
				"Status", e.Status,
				"StatusCode", e.StatusCode,
				"Resources", e.Resources,
				"Metadata", e.Metadata,
				"MayCancel", e.MayCancel,
				"Err", e.Err,
				"Location", e.Location,
			},
		}
		return record, nil
	}

	return EventLogRecord{}, fmt.Errorf("Not supported event type: %s", event.Type)
}

// EventLogRecord represents single log record
type EventLogRecord struct {
	Time time.Time
	Lvl  string
	Msg  string
	Ctx  []interface{}
}

// EventLogging represents a logging type event entry (admin only)
type EventLogging struct {
	Message string            `yaml:"message" json:"message"`
	Level   string            `yaml:"level" json:"level"`
	Context map[string]string `yaml:"context" json:"context"`
}

// EventLifecycle represets a lifecycle type event entry
//
// API extension: event_lifecycle
type EventLifecycle struct {
	Action  string                 `yaml:"action" json:"action"`
	Source  string                 `yaml:"source" json:"source"`
	Context map[string]interface{} `yaml:"context,omitempty" json:"context,omitempty"`

	// API extension: event_lifecycle_requestor
	Requestor *EventLifecycleRequestor `yaml:"requestor,omitempty" json:"requestor,omitempty"`
}

// EventLifecycleRequestor represents the initial requestor for an event
//
// API extension: event_lifecycle_requestor
type EventLifecycleRequestor struct {
	Username string `yaml:"username" json:"username"`
	Protocol string `yaml:"protocol" json:"protocol"`
}
<commit_msg>shared/api: Add Address field to EventLifecycleRequestor<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"time"
)

// Event represents an event entry (over websocket)
//
// swagger:model
type Event struct {
	// Event type (one of operation, logging or lifecycle)
	// Example: lifecycle
	Type string `yaml:"type" json:"type"`

	// Time at which the event was sent
	// Example: 2021-02-24T19:00:45.452649098-05:00
	Timestamp time.Time `yaml:"timestamp" json:"timestamp"`

	// JSON encoded metadata (see EventLogging, EventLifecycle or Operation)
	// Example: {"action": "instance-started", "source": "/1.0/instances/c1", "context": {}}
	Metadata json.RawMessage `yaml:"metadata" json:"metadata"`

	// Originating cluster member
	// Example: lxd01
	//
	// API extension: event_location
	Location string `yaml:"location,omitempty" json:"location,omitempty"`
}

// ToLogging creates log record for the event
func (event *Event) ToLogging() (EventLogRecord, error) {
	if event.Type == "logging" {
		e := &EventLogging{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  e.Level,
			Msg:  e.Message,
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "lifecycle" {
		e := &EventLifecycle{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("Action: %s, Source: %s", e.Action, e.Source),
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "operation" {
		e := &Operation{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("ID: %s, Class: %s, Description: %s", e.ID, e.Class, e.Description),
			Ctx: []interface{}{
				"CreatedAt", e.CreatedAt,
				"UpdatedAt", e.UpdatedAt,
				"Status", e.Status,
				"StatusCode", e.StatusCode,
				"Resources", e.Resources,
				"Metadata", e.Metadata,
				"MayCancel", e.MayCancel,
				"Err", e.Err,
				"Location", e.Location,
			},
		}
		return record, nil
	}

	return EventLogRecord{}, fmt.Errorf("Not supported event type: %s", event.Type)
}

// EventLogRecord represents single log record
type EventLogRecord struct {
	Time time.Time
	Lvl  string
	Msg  string
	Ctx  []interface{}
}

// EventLogging represents a logging type event entry (admin only)
type EventLogging struct {
	Message string            `yaml:"message" json:"message"`
	Level   string            `yaml:"level" json:"level"`
	Context map[string]string `yaml:"context" json:"context"`
}

// EventLifecycle represets a lifecycle type event entry
//
// API extension: event_lifecycle
type EventLifecycle struct {
	Action  string                 `yaml:"action" json:"action"`
	Source  string                 `yaml:"source" json:"source"`
	Context map[string]interface{} `yaml:"context,omitempty" json:"context,omitempty"`

	// API extension: event_lifecycle_requestor
	Requestor *EventLifecycleRequestor `yaml:"requestor,omitempty" json:"requestor,omitempty"`
}

// EventLifecycleRequestor represents the initial requestor for an event
//
// API extension: event_lifecycle_requestor
type EventLifecycleRequestor struct {
	Username string `yaml:"username" json:"username"`
	Protocol string `yaml:"protocol" json:"protocol"`

	// Requestor address
	// Example: 10.0.2.15
	//
	// API extension: event_lifecycle_requestor_address
	Address string `yaml:"address" json:"address"`
}
<|endoftext|>


<commit_before>package encryptor_rsa

import (
	"crypto/rsa"
	"crypto/rand"
	"crypto/sha256"
)

var (
	randReader = rand.Reader
	PRIVATE_KEY_FILE_EXTENSION = ".pem"
)

type RsaEncryptor struct {
	PrivateKey rsa.PrivateKey
	PublicKey rsa.PublicKey
}

func GenerateEncryptor(keyLength int) (RsaEncryptor, error) {
	var rsaEncryptor RsaEncryptor

	key, err := rsa.GenerateKey(randReader, keyLength)
	if err != nil {
		return rsaEncryptor, err
	}

	rsaEncryptor = RsaEncryptor{
		PrivateKey: *key,
		PublicKey: key.PublicKey,
	}

	return rsaEncryptor, nil
}

func GenerateVoidEncryptor() RsaEncryptor {
	return RsaEncryptor{}
}

func GetEncryptedDataLength() int {
	return 344
}

func (e *RsaEncryptor) EncryptBytes(source []byte) ([]byte, error) {
	cipherText, err := rsa.EncryptOAEP(sha256.New(), randReader, &e.PublicKey, source, []byte(""))
	if err != nil {
		return nil, err
	}
	return []byte(toBase64(cipherText)), nil
}

func (e *RsaEncryptor) Encrypt(plainData string) (string, error) {
	encrypted, err := e.EncryptBytes([]byte(plainData))
	if err != nil {
		return "", err
	}
	return string(encrypted), nil
}

func (e *RsaEncryptor) DecryptBytes(source []byte) ([]byte, error) {
	b64, err := fromBase64(string(source))
	if err != nil {
		return nil, err
	}
	sourceDecoded := []byte(b64)
	decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), randReader, &e.PrivateKey, sourceDecoded, []byte(""))
	if err != nil {
		return nil, err
	}
	return decryptedBytes, nil
}

func (e *RsaEncryptor) Decrypt(encryptedData string) (string, error) {
	decrypted, err := e.DecryptBytes( []byte(encryptedData) )
	if err != nil {
		return "", err
	}
	return string(decrypted), nil
}
<commit_msg>private key file extension start to be constant<commit_after>package encryptor_rsa

import (
	"crypto/rsa"
	"crypto/rand"
	"crypto/sha256"
)

var (
	randReader = rand.Reader
)

const (
	PrivateKeyFileExtension = ".pem"
	DefaultKeyLength        = 2048
)

type RsaEncryptor struct {
	PrivateKey rsa.PrivateKey
	PublicKey  rsa.PublicKey
}

func GenerateEncryptor(keyLength int) (e RsaEncryptor, err error) {

	key, err := rsa.GenerateKey(randReader, keyLength)
	if err != nil {
		return e, err
	}

	e = RsaEncryptor{
		PrivateKey: *key,
		PublicKey:  key.PublicKey,
	}

	return e, nil
}

func GenerateVoidEncryptor() RsaEncryptor {
	return RsaEncryptor{}
}

func GetEncryptedDataLength() int {
	return 344
}

func (e *RsaEncryptor) EncryptBytes(source []byte) ([]byte, error) {
	cipherText, err := rsa.EncryptOAEP(sha256.New(), randReader, &e.PublicKey, source, []byte(""))
	if err != nil {
		return nil, err
	}
	return []byte(toBase64(cipherText)), nil
}

func (e *RsaEncryptor) Encrypt(plainData string) (string, error) {
	encrypted, err := e.EncryptBytes([]byte(plainData))
	if err != nil {
		return "", err
	}
	return string(encrypted), nil
}

func (e *RsaEncryptor) DecryptBytes(source []byte) ([]byte, error) {
	b64, err := fromBase64(string(source))
	if err != nil {
		return nil, err
	}
	sourceDecoded := []byte(b64)
	decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), randReader, &e.PrivateKey, sourceDecoded, []byte(""))
	if err != nil {
		return nil, err
	}
	return decryptedBytes, nil
}

func (e *RsaEncryptor) Decrypt(encryptedData string) (string, error) {
	decrypted, err := e.DecryptBytes( []byte(encryptedData) )
	if err != nil {
		return "", err
	}
	return string(decrypted), nil
}
<|endoftext|>


<commit_before>package aat

import (
	"context"
	"testing"
	"time"

	"github.com/fortytw2/leaktest"
	"github.com/gammazero/nexus/client"
	"github.com/gammazero/nexus/wamp"
)

func TestRPCRegisterAndCall(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect callee session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test registering a valid procedure.
	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
		var sum int64
		for i := range args {
			n, ok := wamp.AsInt64(args[i])
			if ok {
				sum += n
			}
		}
		return &client.InvokeResult{Args: wamp.List{sum}}
	}

	// Register procedure "sum"
	procName := "sum"
	if err = callee.Register(procName, handler, nil); err != nil {
		t.Fatal("Failed to register procedure:", err)
	}

	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test calling the procedure.
	callArgs := wamp.List{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	ctx := context.Background()
	result, err := caller.Call(ctx, procName, nil, callArgs, nil, "")
	if err != nil {
		t.Fatal("Failed to call procedure:", err)
	}
	sum, ok := wamp.AsInt64(result.Arguments[0])
	if !ok {
		t.Fatal("Could not convert result to int64")
	}
	if sum != 55 {
		t.Fatal("Wrong result:", sum)
	}

	// Test unregister.
	if err = callee.Unregister(procName); err != nil {
		t.Fatal("Failed to unregister procedure:", err)
	}

	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}

	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCCallUnregistered(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test calling unregistered procedure.
	callArgs := wamp.List{555}
	ctx := context.Background()
	result, err := caller.Call(ctx, "NotRegistered", nil, callArgs, nil, "")
	if err == nil {
		t.Fatal("expected error calling unregistered procedure")
	}
	if result != nil {
		t.Fatal("result should be nil on error")
	}

	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCUnregisterUnregistered(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect caller session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test unregister unregistered procedure.
	if err = callee.Unregister("NotHere"); err == nil {
		t.Fatal("expected error unregistering unregistered procedure")
	}

	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCCancelCall(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect callee session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	invkCanceled := make(chan struct{}, 1)
	// Register procedure that waits.
	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
		<-ctx.Done() // handler will block forever until canceled.
		invkCanceled <- struct{}{}
		return &client.InvokeResult{Err: wamp.ErrCanceled}
	}
	procName := "myproc"
	if err = callee.Register(procName, handler, nil); err != nil {
		t.Fatal("failed to register procedure:", err)
	}

	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	errChan := make(chan error)
	ctx, cancel := context.WithCancel(context.Background())
	// Calling the procedure, should block.
	go func() {
		callArgs := wamp.List{73}
		_, e := caller.Call(ctx, procName, nil, callArgs, nil, "killnowait")
		errChan <- e
	}()

	// Make sure the call is blocked.
	select {
	case err = <-errChan:
		t.Fatal("call should have been blocked")
	case <-time.After(200 * time.Millisecond):
	}

	cancel()

	// Make sure the call is canceled on caller side.
	select {
	case err = <-errChan:
		if err == nil {
			t.Fatal("Expected error from canceling call")
		}
		if err.(client.RPCError).Err.Error != wamp.ErrCanceled {
			t.Fatal("Wrong error for canceled call, got",
				err.(client.RPCError).Err.Error, "want", wamp.ErrCanceled)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("call should have been canceled")
	}

	// Make sure the invocation is canceled on callee side.
	select {
	case <-invkCanceled:
	case <-time.After(time.Second):
		t.Fatal("invocation should have been canceled")
	}

	rpcError, ok := err.(client.RPCError)
	if !ok {
		t.Fatal("expected RPCError type of error")
	}
	if rpcError.Err.Error != wamp.ErrCanceled {
		t.Fatal("expected canceled error, got:", err)
	}
	if err = callee.Unregister(procName); err != nil {
		t.Fatal("failed to unregister procedure:", err)
	}
	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}
<commit_msg>aat for call timeout<commit_after>package aat

import (
	"context"
	"testing"
	"time"

	"github.com/fortytw2/leaktest"
	"github.com/gammazero/nexus/client"
	"github.com/gammazero/nexus/wamp"
)

func TestRPCRegisterAndCall(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect callee session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test registering a valid procedure.
	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
		var sum int64
		for i := range args {
			n, ok := wamp.AsInt64(args[i])
			if ok {
				sum += n
			}
		}
		return &client.InvokeResult{Args: wamp.List{sum}}
	}

	// Register procedure "sum"
	procName := "sum"
	if err = callee.Register(procName, handler, nil); err != nil {
		t.Fatal("Failed to register procedure:", err)
	}

	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test calling the procedure.
	callArgs := wamp.List{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	ctx := context.Background()
	result, err := caller.Call(ctx, procName, nil, callArgs, nil, "")
	if err != nil {
		t.Fatal("Failed to call procedure:", err)
	}
	sum, ok := wamp.AsInt64(result.Arguments[0])
	if !ok {
		t.Fatal("Could not convert result to int64")
	}
	if sum != 55 {
		t.Fatal("Wrong result:", sum)
	}

	// Test unregister.
	if err = callee.Unregister(procName); err != nil {
		t.Fatal("Failed to unregister procedure:", err)
	}

	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}

	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCCallUnregistered(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test calling unregistered procedure.
	callArgs := wamp.List{555}
	ctx := context.Background()
	result, err := caller.Call(ctx, "NotRegistered", nil, callArgs, nil, "")
	if err == nil {
		t.Fatal("expected error calling unregistered procedure")
	}
	if result != nil {
		t.Fatal("result should be nil on error")
	}

	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCUnregisterUnregistered(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect caller session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	// Test unregister unregistered procedure.
	if err = callee.Unregister("NotHere"); err == nil {
		t.Fatal("expected error unregistering unregistered procedure")
	}

	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCCancelCall(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect callee session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	invkCanceled := make(chan struct{}, 1)
	// Register procedure that waits.
	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
		<-ctx.Done() // handler will block forever until canceled.
		invkCanceled <- struct{}{}
		return &client.InvokeResult{Err: wamp.ErrCanceled}
	}
	procName := "myproc"
	if err = callee.Register(procName, handler, nil); err != nil {
		t.Fatal("failed to register procedure:", err)
	}

	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	errChan := make(chan error)
	ctx, cancel := context.WithCancel(context.Background())
	// Calling the procedure, should block.
	go func() {
		callArgs := wamp.List{73}
		_, e := caller.Call(ctx, procName, nil, callArgs, nil, "killnowait")
		errChan <- e
	}()

	// Make sure the call is blocked.
	select {
	case err = <-errChan:
		t.Fatal("call should have been blocked")
	case <-time.After(200 * time.Millisecond):
	}

	cancel()

	// Make sure the call is canceled on caller side.
	select {
	case err = <-errChan:
		if err == nil {
			t.Fatal("Expected error from canceling call")
		}
		if err.(client.RPCError).Err.Error != wamp.ErrCanceled {
			t.Fatal("Wrong error for canceled call, got",
				err.(client.RPCError).Err.Error, "want", wamp.ErrCanceled)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("call should have been canceled")
	}

	// Make sure the invocation is canceled on callee side.
	select {
	case <-invkCanceled:
	case <-time.After(time.Second):
		t.Fatal("invocation should have been canceled")
	}

	rpcError, ok := err.(client.RPCError)
	if !ok {
		t.Fatal("expected RPCError type of error")
	}
	if rpcError.Err.Error != wamp.ErrCanceled {
		t.Fatal("expected canceled error, got:", err)
	}
	if err = callee.Unregister(procName); err != nil {
		t.Fatal("failed to unregister procedure:", err)
	}
	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}

func TestRPCTimeoutCall(t *testing.T) {
	defer leaktest.Check(t)()
	// Connect callee session.
	callee, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	invkCanceled := make(chan struct{}, 1)
	// Register procedure that waits.
	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
		<-ctx.Done() // handler will block forever until canceled.
		invkCanceled <- struct{}{}
		return &client.InvokeResult{Err: wamp.ErrCanceled}
	}
	procName := "myproc"
	if err = callee.Register(procName, handler, nil); err != nil {
		t.Fatal("failed to register procedure:", err)
	}

	// Connect caller session.
	caller, err := connectClient()
	if err != nil {
		t.Fatal("Failed to connect client:", err)
	}

	errChan := make(chan error)
	ctx := context.Background()
	opts := wamp.Dict{"timeout": 1000}
	// Calling the procedure, should block.
	go func() {
		callArgs := wamp.List{73}
		_, e := caller.Call(ctx, procName, opts, callArgs, nil, "killnowait")
		errChan <- e
	}()

	// Make sure the call is blocked.
	select {
	case err = <-errChan:
		t.Fatal("call should have been blocked")
	case <-time.After(200 * time.Millisecond):
	}

	// Make sure the call is canceled on caller side.
	select {
	case err = <-errChan:
		if err == nil {
			t.Fatal("Expected error from canceling call")
		}
		if err.(client.RPCError).Err.Error != wamp.ErrCanceled {
			t.Fatal("Wrong error for canceled call, got",
				err.(client.RPCError).Err.Error, "want", wamp.ErrCanceled)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("call should have been canceled")
	}

	// Make sure the invocation is canceled on callee side.
	select {
	case <-invkCanceled:
	case <-time.After(time.Second):
		t.Fatal("invocation should have been canceled")
	}

	rpcError, ok := err.(client.RPCError)
	if !ok {
		t.Fatal("expected RPCError type of error")
	}
	if rpcError.Err.Error != wamp.ErrCanceled {
		t.Fatal("expected canceled error, got:", err)
	}
	if err = callee.Unregister(procName); err != nil {
		t.Fatal("failed to unregister procedure:", err)
	}
	err = callee.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
	err = caller.Close()
	if err != nil {
		t.Fatal("Failed to disconnect client:", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package main

import (
	"fmt"

	"github.com/go-vgo/robotgo"
	// "go-vgo/robotgo"
)

func typeStr() {
	// importing "Hello World"
	robotgo.TypeString("Hello World")

	robotgo.TypeString("留给真爱你的人")
	robotgo.MicroSleep(1)

	robotgo.TypeStr("山达尔星新星军团, galaxy. こんにちは世界.")
	robotgo.Sleep(1)
	robotgo.TypeStr("所以, 你好, 再见")
	robotgo.Sleep(1)

	ustr := uint32(robotgo.CharCodeAt("所以, 你好, 再见", 0))
	robotgo.UnicodeType(ustr)

	robotgo.PasteStr(" 粘贴字符串, paste")
}

func keyTap() {
	// press "enter"
	robotgo.KeyTap("enter")
	robotgo.KeyTap("a", "control")
	// hide window
	robotgo.KeyTap("h", "command")
	robotgo.KeyTap("h", "command", 12)

	// press "i", "alt", "command" Key combination
	robotgo.KeyTap("i", "alt", "command")
	robotgo.KeyTap("i", "alt", "command", 11)
	arr := []string{"alt", "command"}
	robotgo.KeyTap("i", arr)
	robotgo.KeyTap("i", arr, 12)

	// close window
	robotgo.KeyTap("w", "command")
	// minimize window
	robotgo.KeyTap("m", "command")
	robotgo.KeyTap("f1", "control")
	robotgo.KeyTap("a", "control")
}

func keyToggle() {
	robotgo.KeyToggle("a", "down")
	robotgo.KeyToggle("a", "down", "alt")
	robotgo.KeyToggle("a", "down", "alt", "command")
	robotgo.KeyToggle("enter", "down")
}

func cilp() {
	robotgo.TypeString("en")

	// write string to clipboard
	robotgo.WriteAll("测试")
	// read string from clipboard
	text, err := robotgo.ReadAll()
	if err == nil {
		fmt.Println(text)
	}
}

func key() {
	////////////////////////////////////////////////////////////////////////////////
	// Control the keyboard
	////////////////////////////////////////////////////////////////////////////////

	typeStr()

	keyTap()
	keyToggle()

	cilp()
}

func main() {
	key()
}
<commit_msg>update key example code<commit_after>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package main

import (
	"fmt"

	"github.com/go-vgo/robotgo"
	// "go-vgo/robotgo"
)

func typeStr() {
	// importing "Hello World"
	robotgo.TypeString("Hello World")

	robotgo.TypeString("留给真爱你的人")
	robotgo.MicroSleep(1)

	robotgo.TypeStr("山达尔星新星军团, galaxy. こんにちは世界.")
	robotgo.Sleep(1)
	robotgo.TypeStr("所以, 你好, 再见")
	robotgo.Sleep(1)

	ustr := uint32(robotgo.CharCodeAt("所以, 你好, 再见", 0))
	robotgo.UnicodeType(ustr)

	robotgo.PasteStr(" 粘贴字符串, paste")
}

func keyTap() {
	// press "enter"
	robotgo.KeyTap("enter")
	robotgo.KeyTap("a", "control")

	// hide window
	robotgo.KeyTap("h", "command")
	robotgo.KeyTap("h", "command", 12)

	// press "i", "alt", "command" Key combination
	robotgo.KeyTap("i", "alt", "command")
	robotgo.KeyTap("i", "alt", "command", 11)

	arr := []string{"alt", "command"}
	robotgo.KeyTap("i", arr)
	robotgo.KeyTap("i", arr, 12)

	// close window
	robotgo.KeyTap("w", "command")

	// minimize window
	robotgo.KeyTap("m", "command")
	robotgo.KeyTap("f1", "control")
	robotgo.KeyTap("a", "control")
}

func keyToggle() {
	robotgo.KeyToggle("a", "down")
	robotgo.KeyToggle("a", "down", "alt")
	robotgo.KeyToggle("a", "down", "alt", "command")
	robotgo.KeyToggle("enter", "down")
}

func cilp() {
	robotgo.TypeString("en")

	// write string to clipboard
	robotgo.WriteAll("测试")
	// read string from clipboard
	text, err := robotgo.ReadAll()
	if err == nil {
		fmt.Println(text)
	}
}

func key() {
	////////////////////////////////////////////////////////////////////////////////
	// Control the keyboard
	////////////////////////////////////////////////////////////////////////////////

	typeStr()

	keyTap()
	keyToggle()

	cilp()
}

func main() {
	key()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/keybase/go-libkb"
)

type CmdProve struct {
	me                *libkb.User
	force             bool
	service, username string
	output            string
}

func (v *CmdProve) ParseArgv(ctx *cli.Context) error {
	nargs := len(ctx.Args())
	var err error
	v.force = ctx.Bool("force")
	v.output = ctx.String("output")

	if nargs > 2 || nargs == 0 {
		err = fmt.Errorf("prove takes 1 or args: <service> [<username>]")
	} else {
		v.service = ctx.Args()[0]
		if nargs == 2 {
			v.username = ctx.Args()[1]
		}
	}
	return err
}

func (v *CmdProve) Login() (err error) {
	return
}
func (v *CmdProve) LoadMe() (err error) {
	return
}
func (v *CmdProve) CheckExists1() (err error) {
	return
}
func (v *CmdProve) PromptRemoteName() (err error) {
	return
}
func (v *CmdProve) NormalizeRemoteName() (err error) {
	return
}
func (v *CmdProve) CheckExists2() (err error) {
	return
}
func (v *CmdProve) DoPrechecks() (err error) {
	return
}
func (v *CmdProve) DoWarnings() (err error) {
	return
}
func (v *CmdProve) GenerateProof() (err error) {
	return
}
func (v *CmdProve) PostProofToServer() (err error) {
	return
}
func (v *CmdProve) PromptPostedLoop() (err error) {
	return
}

func (v *CmdProve) Run() (err error) {

	if err = v.Login(); err != nil {
		return
	}
	if err = v.LoadMe(); err != nil {
		return
	}
	if err = v.CheckExists1(); err != nil {
		return
	}
	if err = v.PromptRemoteName(); err != nil {
		return
	}
	if err = v.NormalizeRemoteName(); err != nil {
		return
	}
	if err = v.CheckExists2(); err != nil {
		return
	}
	if err = v.DoPrechecks(); err != nil {
		return
	}
	if err = v.DoWarnings(); err != nil {
		return
	}
	if err = v.GenerateProof(); err != nil {
		return
	}
	if err = v.PostProofToServer(); err != nil {
		return
	}
	if err = v.PromptPostedLoop(); err != nil {
		return
	}
	return nil
}

func NewCmdProve(cl *CommandLine) cli.Command {
	return cli.Command{
		Name:        "prove",
		Usage:       "keybase prove <service> [<username>]",
		Description: "generate a new proof",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "output, o",
				Usage: "output proof text to a file (rather than standard out)",
			},
			cli.BoolFlag{
				Name:  "force, f",
				Usage: "don't stop for any prompts",
			},
		},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdTrack{}, "track", c)
		},
	}
}

func (v *CmdProve) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		Terminal:  true,
		KbKeyring: true,
	}
}
<commit_msg>boilerplate prove<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/keybase/go-libkb"
)

type CmdProve struct {
	me                *libkb.User
	force             bool
	service, username string
	output            string
}

func (v *CmdProve) ParseArgv(ctx *cli.Context) error {
	nargs := len(ctx.Args())
	var err error
	v.force = ctx.Bool("force")
	v.output = ctx.String("output")

	if nargs > 2 || nargs == 0 {
		err = fmt.Errorf("prove takes 1 or args: <service> [<username>]")
	} else {
		v.service = ctx.Args()[0]
		if nargs == 2 {
			v.username = ctx.Args()[1]
		}
	}
	return err
}

func (v *CmdProve) Login() (err error) {
	return G.LoginState.Login(libkb.LoginArg{})
}
func (v *CmdProve) LoadMe() (err error) {
	return
}
func (v *CmdProve) CheckExists1() (err error) {
	return
}
func (v *CmdProve) PromptRemoteName() (err error) {
	return
}
func (v *CmdProve) NormalizeRemoteName() (err error) {
	return
}
func (v *CmdProve) CheckExists2() (err error) {
	return
}
func (v *CmdProve) DoPrechecks() (err error) {
	return
}
func (v *CmdProve) DoWarnings() (err error) {
	return
}
func (v *CmdProve) GenerateProof() (err error) {
	return
}
func (v *CmdProve) PostProofToServer() (err error) {
	return
}
func (v *CmdProve) PromptPostedLoop() (err error) {
	return
}

func (v *CmdProve) Run() (err error) {

	if err = v.Login(); err != nil {
		return
	}
	if err = v.LoadMe(); err != nil {
		return
	}
	if err = v.CheckExists1(); err != nil {
		return
	}
	if err = v.PromptRemoteName(); err != nil {
		return
	}
	if err = v.NormalizeRemoteName(); err != nil {
		return
	}
	if err = v.CheckExists2(); err != nil {
		return
	}
	if err = v.DoPrechecks(); err != nil {
		return
	}
	if err = v.DoWarnings(); err != nil {
		return
	}
	if err = v.GenerateProof(); err != nil {
		return
	}
	if err = v.PostProofToServer(); err != nil {
		return
	}
	if err = v.PromptPostedLoop(); err != nil {
		return
	}
	return nil
}

func NewCmdProve(cl *CommandLine) cli.Command {
	return cli.Command{
		Name:        "prove",
		Usage:       "keybase prove <service> [<username>]",
		Description: "generate a new proof",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "output, o",
				Usage: "output proof text to a file (rather than standard out)",
			},
			cli.BoolFlag{
				Name:  "force, f",
				Usage: "don't stop for any prompts",
			},
		},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdProve{}, "track", c)
		},
	}
}

func (v *CmdProve) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		Terminal:  true,
		KbKeyring: true,
	}
}
<|endoftext|>


<commit_before>package action

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/justwatchcom/gopass/fsutil"
	"github.com/urfave/cli"
)

// Clone will fetch and mount a new password store from a git repo
func (s *Action) Clone(c *cli.Context) error {
	if len(c.Args()) < 1 {
		return fmt.Errorf("Usage: gopass clone repo [mount]")
	}

	repo := c.Args()[0]
	mount := ""
	if len(c.Args()) > 1 {
		mount = c.Args()[1]
	}

	path := c.String("path")
	if path == "" {
		path = pwStoreDir(mount)
	}

	if mount == "" && s.Store.Initialized() {
		return fmt.Errorf("Can not clone %s to the root store, as this store is already initialized. Please try cloning to a submount: `gopass clone %s sub`", repo, repo)
	}

	// clone repo
	if err := gitClone(repo, path); err != nil {
		return err
	}

	// add mount
	if mount != "" {
		if !s.Store.Initialized() {
			return fmt.Errorf("Root-Store is not initialized. Clone or init root store first")
		}
		if err := s.Store.AddMount(mount, path); err != nil {
			return fmt.Errorf("Failed to add mount: %s", err)
		}
		fmt.Printf("Mounted password store %s at mount point `%s` ...\n", path, mount)
	}

	// save new mount in config file
	if err := writeConfig(s.Store); err != nil {
		return fmt.Errorf("Failed to update config: %s", err)
	}

	fmt.Printf("Your password store is ready to use! Has a look around: `gopass %s`\n", mount)

	return nil
}

func gitClone(repo, path string) error {
	if fsutil.IsDir(path) {
		return fmt.Errorf("%s is a directory", path)
	}

	fmt.Printf("Cloning repository %s to %s ...\n", repo, path)

	cmd := exec.Command("git", "clone", repo, path)
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr

	return cmd.Run()
}
<commit_msg>Improve error message in case the directory to clone to already exists (#121)<commit_after>package action

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/justwatchcom/gopass/fsutil"
	"github.com/urfave/cli"
)

// Clone will fetch and mount a new password store from a git repo
func (s *Action) Clone(c *cli.Context) error {
	if len(c.Args()) < 1 {
		return fmt.Errorf("Usage: gopass clone repo [mount]")
	}

	repo := c.Args()[0]
	mount := ""
	if len(c.Args()) > 1 {
		mount = c.Args()[1]
	}

	path := c.String("path")
	if path == "" {
		path = pwStoreDir(mount)
	}

	if mount == "" && s.Store.Initialized() {
		return fmt.Errorf("Can not clone %s to the root store, as this store is already initialized. Please try cloning to a submount: `gopass clone %s sub`", repo, repo)
	}

	// clone repo
	if err := gitClone(repo, path); err != nil {
		return err
	}

	// add mount
	if mount != "" {
		if !s.Store.Initialized() {
			return fmt.Errorf("Root-Store is not initialized. Clone or init root store first")
		}
		if err := s.Store.AddMount(mount, path); err != nil {
			return fmt.Errorf("Failed to add mount: %s", err)
		}
		fmt.Printf("Mounted password store %s at mount point `%s` ...\n", path, mount)
	}

	// save new mount in config file
	if err := writeConfig(s.Store); err != nil {
		return fmt.Errorf("Failed to update config: %s", err)
	}

	fmt.Printf("Your password store is ready to use! Has a look around: `gopass %s`\n", mount)

	return nil
}

func gitClone(repo, path string) error {
	if fsutil.IsDir(path) {
		return fmt.Errorf("%s is a directory that already exists", path)
	}

	fmt.Printf("Cloning repository %s to %s ...\n", repo, path)

	cmd := exec.Command("git", "clone", repo, path)
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr

	return cmd.Run()
}
<|endoftext|>


<commit_before>package abcicli_test

import (
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/tendermint/tendermint/abci/client"
	"github.com/tendermint/tendermint/abci/server"
	"github.com/tendermint/tendermint/abci/types"
	cmn "github.com/tendermint/tendermint/libs/common"
)

func TestSocketClientStopForErrorDeadlock(t *testing.T) {
	c := abcicli.NewSocketClient(":80", false)
	err := errors.New("foo-tendermint")

	// See Issue https://github.com/tendermint/abci/issues/114
	doneChan := make(chan bool)
	go func() {
		defer close(doneChan)
		c.StopForError(err)
		c.StopForError(err)
	}()

	select {
	case <-doneChan:
	case <-time.After(time.Second * 4):
		t.Fatalf("Test took too long, potential deadlock still exists")
	}
}

func TestProperSyncCalls(t *testing.T) {
	app := slowApp{}

	s, c := setupClientServer(t, app)
	defer s.Stop()
	defer c.Stop()

	resp := make(chan error, 1)
	go func() {
		// This is BeginBlockSync unrolled....
		reqres := c.BeginBlockAsync(types.RequestBeginBlock{})
		c.FlushSync()
		res := reqres.Response.GetBeginBlock()
		require.NotNil(t, res)
		resp <- c.Error()
	}()

	select {
	case <-time.After(time.Second):
		require.Fail(t, "No response arrived")
	case err, ok := <-resp:
		require.True(t, ok, "Must not close channel")
		assert.NoError(t, err, "This should return success")
	}
}

func TestHangingSyncCalls(t *testing.T) {
	app := slowApp{}

	s, c := setupClientServer(t, app)
	defer s.Stop()
	defer c.Stop()

	resp := make(chan error, 1)
	go func() {
		// Start BeginBlock and flush it
		reqres := c.BeginBlockAsync(types.RequestBeginBlock{})
		flush := c.FlushAsync()
		// wait 20 ms for all events to travel socket, but
		// no response yet from server
		time.Sleep(20 * time.Millisecond)
		// kill the server, so the connections break
		s.Stop()

		// wait for the response from BeginBlock
		fmt.Println("waiting for begin block")
		reqres.Wait()
		fmt.Println("waiting for flush")
		flush.Wait()
		fmt.Println("got all responses")
		// res := reqres.Response.GetBeginBlock()
		// require.NotNil(t, res)
		resp <- c.Error()
	}()

	select {
	case <-time.After(time.Second):
		require.Fail(t, "No response arrived")
	case err, ok := <-resp:
		require.True(t, ok, "Must not close channel")
		assert.Error(t, err, "We should get EOF error")
	}
}

func setupClientServer(t *testing.T, app types.Application) (
	cmn.Service, abcicli.Client) {
	// some port between 20k and 30k
	port := 20000 + cmn.RandInt32()%10000
	addr := fmt.Sprintf("localhost:%d", port)

	s, err := server.NewServer(addr, "socket", app)
	require.NoError(t, err)
	err = s.Start()
	require.NoError(t, err)

	c := abcicli.NewSocketClient(addr, true)
	err = c.Start()
	require.NoError(t, err)

	return s, c
}

type slowApp struct {
	types.BaseApplication
}

func (slowApp) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
	time.Sleep(200 * time.Millisecond)
	return types.ResponseBeginBlock{}
}
<commit_msg>remove print statements<commit_after>package abcicli_test

import (
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	abcicli "github.com/tendermint/tendermint/abci/client"
	"github.com/tendermint/tendermint/abci/server"
	"github.com/tendermint/tendermint/abci/types"
	cmn "github.com/tendermint/tendermint/libs/common"
)

func TestSocketClientStopForErrorDeadlock(t *testing.T) {
	c := abcicli.NewSocketClient(":80", false)
	err := errors.New("foo-tendermint")

	// See Issue https://github.com/tendermint/abci/issues/114
	doneChan := make(chan bool)
	go func() {
		defer close(doneChan)
		c.StopForError(err)
		c.StopForError(err)
	}()

	select {
	case <-doneChan:
	case <-time.After(time.Second * 4):
		t.Fatalf("Test took too long, potential deadlock still exists")
	}
}

func TestProperSyncCalls(t *testing.T) {
	app := slowApp{}

	s, c := setupClientServer(t, app)
	defer s.Stop()
	defer c.Stop()

	resp := make(chan error, 1)
	go func() {
		// This is BeginBlockSync unrolled....
		reqres := c.BeginBlockAsync(types.RequestBeginBlock{})
		c.FlushSync()
		res := reqres.Response.GetBeginBlock()
		require.NotNil(t, res)
		resp <- c.Error()
	}()

	select {
	case <-time.After(time.Second):
		require.Fail(t, "No response arrived")
	case err, ok := <-resp:
		require.True(t, ok, "Must not close channel")
		assert.NoError(t, err, "This should return success")
	}
}

func TestHangingSyncCalls(t *testing.T) {
	app := slowApp{}

	s, c := setupClientServer(t, app)
	defer s.Stop()
	defer c.Stop()

	resp := make(chan error, 1)
	go func() {
		// Start BeginBlock and flush it
		reqres := c.BeginBlockAsync(types.RequestBeginBlock{})
		flush := c.FlushAsync()
		// wait 20 ms for all events to travel socket, but
		// no response yet from server
		time.Sleep(20 * time.Millisecond)
		// kill the server, so the connections break
		s.Stop()

		// wait for the response from BeginBlock
		reqres.Wait()
		flush.Wait()
		resp <- c.Error()
	}()

	select {
	case <-time.After(time.Second):
		require.Fail(t, "No response arrived")
	case err, ok := <-resp:
		require.True(t, ok, "Must not close channel")
		assert.Error(t, err, "We should get EOF error")
	}
}

func setupClientServer(t *testing.T, app types.Application) (
	cmn.Service, abcicli.Client) {
	// some port between 20k and 30k
	port := 20000 + cmn.RandInt32()%10000
	addr := fmt.Sprintf("localhost:%d", port)

	s, err := server.NewServer(addr, "socket", app)
	require.NoError(t, err)
	err = s.Start()
	require.NoError(t, err)

	c := abcicli.NewSocketClient(addr, true)
	err = c.Start()
	require.NoError(t, err)

	return s, c
}

type slowApp struct {
	types.BaseApplication
}

func (slowApp) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
	time.Sleep(200 * time.Millisecond)
	return types.ResponseBeginBlock{}
}
<|endoftext|>


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

package localcluster

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"time"

	"github.com/cockroachdb/cockroach/util/log"
	"github.com/samalba/dockerclient"
)

func getTLSConfig() *tls.Config {
	certPath := os.Getenv("DOCKER_CERT_PATH")

	clientCert, err := tls.LoadX509KeyPair(
		filepath.Join(certPath, "cert.pem"),
		filepath.Join(certPath, "key.pem"),
	)
	if err != nil {
		panic(err)
	}

	rootCAs := x509.NewCertPool()
	caCert, err := ioutil.ReadFile(filepath.Join(certPath, "ca.pem"))
	if err != nil {
		panic(err)
	}
	rootCAs.AppendCertsFromPEM(caCert)

	return &tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCAs,
	}
}

// newDockerClient constructs a new docker client using the best available
// method. On darwin (Mac OS X), first look for the DOCKER_HOST env
// variable. If not found, run "boot2docker shellinit" and add the exported
// variables to the environment. If DOCKER_HOST is set, initialize the client
// using DOCKER_TLS_VERIFY and DOCKER_CERT_PATH. If DOCKER_HOST is not set,
// look for the unix domain socket in /run/docker.sock and
// /var/run/docker.sock.
func newDockerClient() dockerclient.Client {
	if runtime.GOOS == "darwin" && os.Getenv("DOCKER_HOST") == "" {
		output, err := exec.Command("boot2docker", "shellinit").Output()
		if err != nil {
			log.Fatal(err)
		}
		exportRE := regexp.MustCompile(`export (\w+)=([^\n]+)`)
		for s := bufio.NewScanner(bytes.NewReader(output)); s.Scan(); {
			export := exportRE.FindStringSubmatch(s.Text())
			if len(export) != 3 {
				continue
			}
			if err := os.Setenv(export[1], export[2]); err != nil {
				log.Fatal(err)
			}
		}
	}

	host := os.Getenv("DOCKER_HOST")
	if host != "" {
		if os.Getenv("DOCKER_TLS_VERIFY") == "" {
			c, err := dockerclient.NewDockerClient(host, nil)
			if err != nil {
				log.Fatal(err)
			}
			return c
		}
		c, err := dockerclient.NewDockerClient(host, getTLSConfig())
		if err != nil {
			log.Fatal(err)
		}
		return c
	}

	for _, l := range []string{"/run/docker.sock", "/var/run/docker.sock"} {
		if _, err := os.Stat(l); err != nil {
			continue
		}
		c, err := dockerclient.NewDockerClient("unix://"+l, nil)
		if err != nil {
			return nil
		}
		return c
	}
	log.Fatal("docker not configured")
	return nil
}

// Retrieve the IP address of docker itself.
func dockerIP() net.IP {
	host := os.Getenv("DOCKER_HOST")
	if host != "" {
		u, err := url.Parse(host)
		if err != nil {
			panic(err)
		}
		h, _, err := net.SplitHostPort(u.Host)
		if err != nil {
			panic(err)
		}
		return net.ParseIP(h)
	}
	if runtime.GOOS == "linux" {
		return net.IPv4zero
	}
	panic(fmt.Errorf("unable to determine docker ip address"))
}

// Container provides the programmatic interface for a single docker
// container.
type Container struct {
	dockerclient.ContainerInfo
	client dockerclient.Client
}

// createContainer creates a new container using the specified options. Per the
// docker API, the created container is not running and must be started
// explicitly.
func createContainer(client dockerclient.Client, config dockerclient.ContainerConfig) (*Container, error) {
	id, err := client.CreateContainer(&config, "")
	if err != nil {
		return nil, err
	}
	return &Container{
		ContainerInfo: dockerclient.ContainerInfo{Id: id},
		client:        client}, nil
}

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}

// Remove removes the container from docker. It is an error to remove a running
// container.
func (c *Container) Remove() error {
	return c.client.RemoveContainer(c.Id, false, true)
}

func (c *Container) mustRemove() {
	maybePanic(c.Remove())
}

// Kill stops a running container and removes it.
func (c *Container) Kill() error {
	// Paused containers cannot be killed. Attempt to unpause it first
	// (which might fail) before killing.
	_ = c.Unpause()
	err := c.client.KillContainer(c.Id, "9")
	if err != nil {
		return err
	}
	return c.Remove()
}

func (c *Container) mustKill() {
	maybePanic(c.Kill())
}

// Start starts a non-running container.
//
// TODO(pmattis): Generalize the setting of parameters here.
func (c *Container) Start(binds []string, dns, vols *Container) error {
	config := &dockerclient.HostConfig{
		Binds:           binds,
		PublishAllPorts: true,
	}
	if dns != nil {
		config.Dns = append(config.Dns, dns.NetworkSettings.IPAddress)
	}
	if vols != nil {
		config.VolumesFrom = append(config.VolumesFrom, vols.Id)
	}
	return c.client.StartContainer(c.Id, config)
}

// Pause pauses a running container.
func (c *Container) Pause() error {
	return c.client.PauseContainer(c.Id)
}

// Unpause resumes a paused container.
func (c *Container) Unpause() error {
	return c.client.UnpauseContainer(c.Id)
}

// Restart restarts a running container.
// Container will be killed after 'timeout' seconds if it fails to stop.
func (c *Container) Restart(timeoutSeconds int) error {
	if err := c.client.RestartContainer(c.Id, timeoutSeconds); err != nil {
		return err
	}
	// We need to refresh the container metadata. Ports change on restart.
	return c.Inspect()
}

// Wait waits for a running container to exit.
func (c *Container) Wait() error {
	// TODO(pmattis): dockerclient does not support the "wait" method
	// (yet), so perform the http call ourselves. Remove once "wait"
	// support is added to dockerclient.
	dc := c.client.(*dockerclient.DockerClient)
	resp, err := dc.HTTPClient.Post(
		fmt.Sprintf("%s/containers/%s/wait", dc.URL, c.Id), "application/json", nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusNotFound {
		return fmt.Errorf("no such container: %s", c.Id)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var r struct{ StatusCode int }
	err = json.Unmarshal(body, &r)
	if err != nil {
		return err
	}
	if r.StatusCode != 0 {
		_ = c.Logs()
		return fmt.Errorf("non-zero exit code: %d", r.StatusCode)
	}
	return nil
}

// Logs outputs the containers logs to stdout/stderr.
func (c *Container) Logs() error {
	r, err := c.client.ContainerLogs(c.Id, &dockerclient.LogOptions{
		Stdout: true,
		Stderr: true,
	})
	if err != nil {
		return err
	}
	defer r.Close()
	_, err = io.Copy(os.Stdout, r)
	return err
}

// Inspect retrieves detailed info about a container.
func (c *Container) Inspect() error {
	out, err := c.client.InspectContainer(c.Id)
	if err != nil {
		return err
	}
	c.ContainerInfo = *out
	return nil
}

// Addr returns the address to connect to the specified port.
func (c *Container) Addr(name string) *net.TCPAddr {
	if name == "" {
		// No port specified, pick a random one (random because iteration
		// over maps is randomized).
		for port := range c.NetworkSettings.Ports {
			name = port
			break
		}
	}
	bindings, ok := c.NetworkSettings.Ports[name]
	if !ok || len(bindings) == 0 {
		return nil
	}
	port, _ := strconv.Atoi(bindings[0].HostPort)
	return &net.TCPAddr{
		IP:   dockerIP(),
		Port: port,
	}
}

// GetJSON retrieves the URL specified by https://Addr(<port>)<path>
// and unmarshals the result as JSON.
func (c *Container) GetJSON(port, path string, v interface{}) error {
	client := &http.Client{
		Timeout: time.Second,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		}}
	resp, err := client.Get(fmt.Sprintf("https://%s%s", c.Addr(port), path))
	if err != nil {
		if log.V(1) {
			log.Info(err)
		}
		return err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		if log.V(1) {
			log.Info(err)
		}
		return err
	}
	if err := json.Unmarshal(b, v); err != nil {
		if log.V(1) {
			log.Info(err)
		}
	}
	return nil
}
<commit_msg>Don't sleep too long<commit_after>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter.mattis@gmail.com)

package localcluster

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"time"

	"github.com/cockroachdb/cockroach/util/log"
	"github.com/samalba/dockerclient"
)

func getTLSConfig() *tls.Config {
	certPath := os.Getenv("DOCKER_CERT_PATH")

	clientCert, err := tls.LoadX509KeyPair(
		filepath.Join(certPath, "cert.pem"),
		filepath.Join(certPath, "key.pem"),
	)
	if err != nil {
		panic(err)
	}

	rootCAs := x509.NewCertPool()
	caCert, err := ioutil.ReadFile(filepath.Join(certPath, "ca.pem"))
	if err != nil {
		panic(err)
	}
	rootCAs.AppendCertsFromPEM(caCert)

	return &tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCAs,
	}
}

// newDockerClient constructs a new docker client using the best available
// method. On darwin (Mac OS X), first look for the DOCKER_HOST env
// variable. If not found, run "boot2docker shellinit" and add the exported
// variables to the environment. If DOCKER_HOST is set, initialize the client
// using DOCKER_TLS_VERIFY and DOCKER_CERT_PATH. If DOCKER_HOST is not set,
// look for the unix domain socket in /run/docker.sock and
// /var/run/docker.sock.
func newDockerClient() dockerclient.Client {
	if runtime.GOOS == "darwin" && os.Getenv("DOCKER_HOST") == "" {
		output, err := exec.Command("boot2docker", "shellinit").Output()
		if err != nil {
			log.Fatal(err)
		}
		exportRE := regexp.MustCompile(`export (\w+)=([^\n]+)`)
		for s := bufio.NewScanner(bytes.NewReader(output)); s.Scan(); {
			export := exportRE.FindStringSubmatch(s.Text())
			if len(export) != 3 {
				continue
			}
			if err := os.Setenv(export[1], export[2]); err != nil {
				log.Fatal(err)
			}
		}
	}

	host := os.Getenv("DOCKER_HOST")
	if host != "" {
		if os.Getenv("DOCKER_TLS_VERIFY") == "" {
			c, err := dockerclient.NewDockerClient(host, nil)
			if err != nil {
				log.Fatal(err)
			}
			return c
		}
		c, err := dockerclient.NewDockerClient(host, getTLSConfig())
		if err != nil {
			log.Fatal(err)
		}
		return c
	}

	for _, l := range []string{"/run/docker.sock", "/var/run/docker.sock"} {
		if _, err := os.Stat(l); err != nil {
			continue
		}
		c, err := dockerclient.NewDockerClient("unix://"+l, nil)
		if err != nil {
			return nil
		}
		return c
	}
	log.Fatal("docker not configured")
	return nil
}

// Retrieve the IP address of docker itself.
func dockerIP() net.IP {
	host := os.Getenv("DOCKER_HOST")
	if host != "" {
		u, err := url.Parse(host)
		if err != nil {
			panic(err)
		}
		h, _, err := net.SplitHostPort(u.Host)
		if err != nil {
			panic(err)
		}
		return net.ParseIP(h)
	}
	if runtime.GOOS == "linux" {
		return net.IPv4zero
	}
	panic(fmt.Errorf("unable to determine docker ip address"))
}

// Container provides the programmatic interface for a single docker
// container.
type Container struct {
	dockerclient.ContainerInfo
	client dockerclient.Client
}

// createContainer creates a new container using the specified options. Per the
// docker API, the created container is not running and must be started
// explicitly.
func createContainer(client dockerclient.Client, config dockerclient.ContainerConfig) (*Container, error) {
	id, err := client.CreateContainer(&config, "")
	if err != nil {
		return nil, err
	}
	return &Container{
		ContainerInfo: dockerclient.ContainerInfo{Id: id},
		client:        client}, nil
}

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}

// Remove removes the container from docker. It is an error to remove a running
// container.
func (c *Container) Remove() error {
	return c.client.RemoveContainer(c.Id, false, true)
}

func (c *Container) mustRemove() {
	maybePanic(c.Remove())
}

// Kill stops a running container and removes it.
func (c *Container) Kill() error {
	// Paused containers cannot be killed. Attempt to unpause it first
	// (which might fail) before killing.
	_ = c.Unpause()
	err := c.client.KillContainer(c.Id, "9")
	if err != nil {
		return err
	}
	return c.Remove()
}

func (c *Container) mustKill() {
	maybePanic(c.Kill())
}

// Start starts a non-running container.
//
// TODO(pmattis): Generalize the setting of parameters here.
func (c *Container) Start(binds []string, dns, vols *Container) error {
	config := &dockerclient.HostConfig{
		Binds:           binds,
		PublishAllPorts: true,
	}
	if dns != nil {
		config.Dns = append(config.Dns, dns.NetworkSettings.IPAddress)
	}
	if vols != nil {
		config.VolumesFrom = append(config.VolumesFrom, vols.Id)
	}
	return c.client.StartContainer(c.Id, config)
}

// Pause pauses a running container.
func (c *Container) Pause() error {
	return c.client.PauseContainer(c.Id)
}

// Unpause resumes a paused container.
func (c *Container) Unpause() error {
	return c.client.UnpauseContainer(c.Id)
}

// Restart restarts a running container.
// Container will be killed after 'timeout' seconds if it fails to stop.
func (c *Container) Restart(timeoutSeconds int) error {
	if err := c.client.RestartContainer(c.Id, timeoutSeconds); err != nil {
		return err
	}
	// We need to refresh the container metadata. Ports change on restart.
	return c.Inspect()
}

// Wait waits for a running container to exit.
func (c *Container) Wait() error {
	// TODO(pmattis): dockerclient does not support the "wait" method
	// (yet), so perform the http call ourselves. Remove once "wait"
	// support is added to dockerclient.
	dc := c.client.(*dockerclient.DockerClient)
	resp, err := dc.HTTPClient.Post(
		fmt.Sprintf("%s/containers/%s/wait", dc.URL, c.Id), "application/json", nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusNotFound {
		return fmt.Errorf("no such container: %s", c.Id)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var r struct{ StatusCode int }
	err = json.Unmarshal(body, &r)
	if err != nil {
		return err
	}
	if r.StatusCode != 0 {
		_ = c.Logs()
		return fmt.Errorf("non-zero exit code: %d", r.StatusCode)
	}
	return nil
}

// Logs outputs the containers logs to stdout/stderr.
func (c *Container) Logs() error {
	r, err := c.client.ContainerLogs(c.Id, &dockerclient.LogOptions{
		Stdout: true,
		Stderr: true,
	})
	if err != nil {
		return err
	}
	defer r.Close()
	_, err = io.Copy(os.Stdout, r)
	return err
}

// Inspect retrieves detailed info about a container.
func (c *Container) Inspect() error {
	out, err := c.client.InspectContainer(c.Id)
	if err != nil {
		return err
	}
	c.ContainerInfo = *out
	return nil
}

// Addr returns the address to connect to the specified port.
func (c *Container) Addr(name string) *net.TCPAddr {
	if name == "" {
		// No port specified, pick a random one (random because iteration
		// over maps is randomized).
		for port := range c.NetworkSettings.Ports {
			name = port
			break
		}
	}
	bindings, ok := c.NetworkSettings.Ports[name]
	if !ok || len(bindings) == 0 {
		return nil
	}
	port, _ := strconv.Atoi(bindings[0].HostPort)
	return &net.TCPAddr{
		IP:   dockerIP(),
		Port: port,
	}
}

// GetJSON retrieves the URL specified by https://Addr(<port>)<path>
// and unmarshals the result as JSON.
func (c *Container) GetJSON(port, path string, v interface{}) error {
	client := &http.Client{
		Timeout: 200 * time.Millisecond,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		}}
	resp, err := client.Get(fmt.Sprintf("https://%s%s", c.Addr(port), path))
	if err != nil {
		if log.V(1) {
			log.Info(err)
		}
		return err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		if log.V(1) {
			log.Info(err)
		}
		return err
	}
	if err := json.Unmarshal(b, v); err != nil {
		if log.V(1) {
			log.Info(err)
		}
	}
	return nil
}
<|endoftext|>