<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"github.com/akerl/timber/v2/log"
	"github.com/mholt/archiver/v3"

	"github.com/dock0/ducktape/cmd"
)

var logger = log.NewLogger("ducktape")

func usage() {
	exampleURL := "https://example.org/download.tar.bz2"
	fmt.Printf("Usage: %s %s\n", os.Args[0], exampleURL)
	fmt.Printf("Alternate usage: DUCKTAPE_URL=%s %s\n", exampleURL, os.Args[0])
}

func getDirPath() (string, error) {
	binPath, err := os.Executable()
	if err != nil {
		return "", err
	}
	return filepath.Dir(binPath), nil
}

func getFilePath(name string) (string, error) {
	dirPath, err := getDirPath()
	if err != nil {
		return "", err
	}
	return filepath.Join(dirPath, name), nil
}

func getTmpFile(extension string) (string, error) {
	dirPath, err := getDirPath()
	if err != nil {
		return "", err
	}
	pattern = fmt.Sprintf("ducktape-*%s", extension)
	file, err := ioutil.TempFile(dirPath, pattern)
	if err != nil {
		return "", err
	}
	file.Close()
	return file.Name(), nil
}

func getTLSConfig() (tls.Config, error) {
	certFile, err := getFilePath("cert")
	if err != nil {
		return tls.Config{}, err
	}
	cert, err := ioutil.ReadFile(certFile)
	if err != nil {
		return tls.Config{}, err
	}
	pool := x509.NewCertPool()
	pool.AppendCertsFromPEM(cert)
	return tls.Config{RootCAs: pool}, nil
}

func getTLSClient() (http.Client, error) {
	tlsConfig, err := getTLSConfig()
	if err != nil {
		return http.Client{}, err
	}
	transport := http.Transport{TLSClientConfig: &tlsConfig}
	return http.Client{Transport: &transport}, nil
}

func download(path, url string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	client, err := getTLSClient()
	if err != nil {
		return err
	}
	logger.InfoMsgf("Downloading from %s", url)
	response, err := client.Get(url)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return fmt.Errorf("failed http response: %s", response.Status)
	}

	_, err = io.Copy(file, response.Body)
	return err
}

func execute(url string) error {
	path, err := getTmpFile(".tar.bz2")
	if err != nil {
		return err
	}
	logger.InfoMsgf("Created tmp file %s", path)
	defer os.Remove(path)

	err = download(path, url)
	if err != nil {
		return err
	}

	logger.InfoMsgf("Beginning unarchive")
	return archiver.Unarchive(path, "/")
}

func main() {
	url := os.Getenv("DUCKTAPE_URL")
	if len(os.Args) > 1 {
		if os.Args[1] == "-v" {
			fmt.Println(cmd.Version)
			return
		}
		url = os.Args[1]
	}
	if len(url) == 0 {
		usage()
		os.Exit(1)
	}

	err := execute(url)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<commit_msg>fix var declaration<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"github.com/akerl/timber/v2/log"
	"github.com/mholt/archiver/v3"

	"github.com/dock0/ducktape/cmd"
)

var logger = log.NewLogger("ducktape")

func usage() {
	exampleURL := "https://example.org/download.tar.bz2"
	fmt.Printf("Usage: %s %s\n", os.Args[0], exampleURL)
	fmt.Printf("Alternate usage: DUCKTAPE_URL=%s %s\n", exampleURL, os.Args[0])
}

func getDirPath() (string, error) {
	binPath, err := os.Executable()
	if err != nil {
		return "", err
	}
	return filepath.Dir(binPath), nil
}

func getFilePath(name string) (string, error) {
	dirPath, err := getDirPath()
	if err != nil {
		return "", err
	}
	return filepath.Join(dirPath, name), nil
}

func getTmpFile(extension string) (string, error) {
	dirPath, err := getDirPath()
	if err != nil {
		return "", err
	}
	pattern := fmt.Sprintf("ducktape-*%s", extension)
	file, err := ioutil.TempFile(dirPath, pattern)
	if err != nil {
		return "", err
	}
	file.Close()
	return file.Name(), nil
}

func getTLSConfig() (tls.Config, error) {
	certFile, err := getFilePath("cert")
	if err != nil {
		return tls.Config{}, err
	}
	cert, err := ioutil.ReadFile(certFile)
	if err != nil {
		return tls.Config{}, err
	}
	pool := x509.NewCertPool()
	pool.AppendCertsFromPEM(cert)
	return tls.Config{RootCAs: pool}, nil
}

func getTLSClient() (http.Client, error) {
	tlsConfig, err := getTLSConfig()
	if err != nil {
		return http.Client{}, err
	}
	transport := http.Transport{TLSClientConfig: &tlsConfig}
	return http.Client{Transport: &transport}, nil
}

func download(path, url string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	client, err := getTLSClient()
	if err != nil {
		return err
	}
	logger.InfoMsgf("Downloading from %s", url)
	response, err := client.Get(url)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return fmt.Errorf("failed http response: %s", response.Status)
	}

	_, err = io.Copy(file, response.Body)
	return err
}

func execute(url string) error {
	path, err := getTmpFile(".tar.bz2")
	if err != nil {
		return err
	}
	logger.InfoMsgf("Created tmp file %s", path)
	defer os.Remove(path)

	err = download(path, url)
	if err != nil {
		return err
	}

	logger.InfoMsgf("Beginning unarchive")
	return archiver.Unarchive(path, "/")
}

func main() {
	url := os.Getenv("DUCKTAPE_URL")
	if len(os.Args) > 1 {
		if os.Args[1] == "-v" {
			fmt.Println(cmd.Version)
			return
		}
		url = os.Args[1]
	}
	if len(url) == 0 {
		usage()
		os.Exit(1)
	}

	err := execute(url)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"compress/flate"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/reportportal/commons-go/v5/commons"
	"github.com/reportportal/commons-go/v5/conf"
	"github.com/reportportal/commons-go/v5/server"
	"github.com/unrolled/secure"
)

func main() {
	currDir, e := os.Getwd()
	if nil != e {
		log.Fatalf("Cannot get workdir: %s", e.Error())
	}
	cfg := conf.EmptyConfig()
	rpConf := struct {
		Cfg         *conf.ServerConfig
		StaticsPath string `env:"RP_STATICS_PATH"`
	}{
		Cfg:         cfg,
		StaticsPath: currDir,
	}
	err := conf.LoadConfig(&rpConf)
	if nil != err {
		log.Fatalf("Cannot log app config")
	}
	info := commons.GetBuildInfo()
	info.Name = "Service UI"
	srv := server.New(rpConf.Cfg, info)
	configureRouter(srv, rpConf)
	srv.StartServer()
}

func configureRouter(srv *server.RpServer, rpConf struct {
	Cfg         *conf.ServerConfig
	StaticsPath string `env:"RP_STATICS_PATH"`
}) {
	srv.WithRouter(func(router *chi.Mux) {
		// apply compression

		compressor := middleware.NewCompressor(flate.DefaultCompression)
		router.Use(compressor.Handler)
		router.Use(middleware.Logger)
		// content security policy
		csp := map[string][]string{
			"default-src": {"'self'", "data:", "'unsafe-inline'", "*.uservoice.com"},
			"script-src": {
				"'self'",
				"'unsafe-inline'",
				"'unsafe-eval'",
				"status.reportportal.io",
				"www.google-analytics.com",
				"stats.g.doubleclick.net",
				"*.saucelabs.com",
				"*.epam.com",
				"*.uservoice.com",
				"*.rawgit.com",
			},
			"worker-src":     {"'self'", "blob:"},
			"font-src":       {"'self'", "data:", "fonts.googleapis.com", "fonts.gstatic.com", "*.rawgit.com"},
			"style-src-elem": {"'self'", "data:", "'unsafe-inline'", "*.googleapis.com", "*.rawgit.com"},
			"media-src":      {"'self'", "*.saucelabs.com", "blob:"},
			"img-src":        {"*", "'self'", "data:", "blob:"},
			"object-src":     {"'self'"},
		}
		// apply content security policies
		var STSSeconds int64 = 315360000
		router.Use(func(next http.Handler) http.Handler {
			return secure.New(secure.Options{
				ContentTypeNosniff:    true,
				BrowserXssFilter:      true,
				ContentSecurityPolicy: buildCSP(csp),
				STSSeconds:            STSSeconds,
				STSIncludeSubdomains:  true,
				STSPreload:            true,
			}).Handler(next)
		})
		err := os.Chdir(rpConf.StaticsPath)
		if nil != err {
			log.Fatalf("Dir %s not found", rpConf.StaticsPath)
		}
		router.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// trim query params
			ext := filepath.Ext(trimQuery(r.URL.String(), "?"))
			// never cache html
			if "/" == r.URL.String() || ".html" == ext {
				w.Header().Add("Cache-Control", "no-cache")
			}
			http.FileServer(http.Dir(rpConf.StaticsPath)).ServeHTTP(&redirectingRW{ResponseWriter: w, Request: r}, r)
		}))
	})
}

func trimQuery(s string, sep string) string {
	sepIndex := strings.Index(s, sep)
	if -1 != sepIndex {
		return s[:sepIndex]
	}

	return s
}

func buildCSP(csp map[string][]string) string {
	var instr []string
	for k, v := range csp {
		instr = append(instr, k+" "+strings.Join(v, " "))
	}

	return strings.Join(instr, "; ")
}

type redirectingRW struct {
	*http.Request
	http.ResponseWriter
	ignore bool
}

func (hrw *redirectingRW) Header() http.Header {
	return hrw.ResponseWriter.Header()
}

func (hrw *redirectingRW) WriteHeader(status int) {
	if status == http.StatusNotFound {
		hrw.ignore = true
		http.Redirect(hrw.ResponseWriter, hrw.Request, "/ui/#notfound", http.StatusTemporaryRedirect)
	} else {
		hrw.ResponseWriter.WriteHeader(status)
	}
}

func (hrw *redirectingRW) Write(p []byte) (int, error) {
	if hrw.ignore {
		return len(p), nil
	}

	return hrw.ResponseWriter.Write(p)
}
<commit_msg>Update CSP for google analytics<commit_after>package main

import (
	"compress/flate"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/reportportal/commons-go/v5/commons"
	"github.com/reportportal/commons-go/v5/conf"
	"github.com/reportportal/commons-go/v5/server"
	"github.com/unrolled/secure"
)

func main() {
	currDir, e := os.Getwd()
	if nil != e {
		log.Fatalf("Cannot get workdir: %s", e.Error())
	}
	cfg := conf.EmptyConfig()
	rpConf := struct {
		Cfg         *conf.ServerConfig
		StaticsPath string `env:"RP_STATICS_PATH"`
	}{
		Cfg:         cfg,
		StaticsPath: currDir,
	}
	err := conf.LoadConfig(&rpConf)
	if nil != err {
		log.Fatalf("Cannot log app config")
	}
	info := commons.GetBuildInfo()
	info.Name = "Service UI"
	srv := server.New(rpConf.Cfg, info)
	configureRouter(srv, rpConf)
	srv.StartServer()
}

func configureRouter(srv *server.RpServer, rpConf struct {
	Cfg         *conf.ServerConfig
	StaticsPath string `env:"RP_STATICS_PATH"`
}) {
	srv.WithRouter(func(router *chi.Mux) {
		// apply compression

		compressor := middleware.NewCompressor(flate.DefaultCompression)
		router.Use(compressor.Handler)
		router.Use(middleware.Logger)
		// content security policy
		csp := map[string][]string{
			"default-src": {"'self'", "data:", "'unsafe-inline'", "*.uservoice.com"},
			"script-src": {
				"'self'",
				"'unsafe-inline'",
				"'unsafe-eval'",
				"status.reportportal.io",
				"www.google-analytics.com",
				"stats.g.doubleclick.net",
				"*.saucelabs.com",
				"*.epam.com",
				"*.uservoice.com",
				"*.rawgit.com",
			},
			"worker-src":     {"'self'", "blob:"},
			"font-src":       {"'self'", "data:", "fonts.googleapis.com", "fonts.gstatic.com", "*.rawgit.com"},
			"style-src-elem": {"'self'", "data:", "'unsafe-inline'", "*.googleapis.com", "*.rawgit.com"},
			"media-src":      {"'self'", "*.saucelabs.com", "blob:"},
			"img-src":        {"*", "'self'", "data:", "blob:"},
			"object-src":     {"'self'"},
			"connect-src":    {"www.google-analytics.com"},
		}
		// apply content security policies
		var STSSeconds int64 = 315360000
		router.Use(func(next http.Handler) http.Handler {
			return secure.New(secure.Options{
				ContentTypeNosniff:    true,
				BrowserXssFilter:      true,
				ContentSecurityPolicy: buildCSP(csp),
				STSSeconds:            STSSeconds,
				STSIncludeSubdomains:  true,
				STSPreload:            true,
			}).Handler(next)
		})
		err := os.Chdir(rpConf.StaticsPath)
		if nil != err {
			log.Fatalf("Dir %s not found", rpConf.StaticsPath)
		}
		router.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// trim query params
			ext := filepath.Ext(trimQuery(r.URL.String(), "?"))
			// never cache html
			if "/" == r.URL.String() || ".html" == ext {
				w.Header().Add("Cache-Control", "no-cache")
			}
			http.FileServer(http.Dir(rpConf.StaticsPath)).ServeHTTP(&redirectingRW{ResponseWriter: w, Request: r}, r)
		}))
	})
}

func trimQuery(s string, sep string) string {
	sepIndex := strings.Index(s, sep)
	if -1 != sepIndex {
		return s[:sepIndex]
	}

	return s
}

func buildCSP(csp map[string][]string) string {
	var instr []string
	for k, v := range csp {
		instr = append(instr, k+" "+strings.Join(v, " "))
	}

	return strings.Join(instr, "; ")
}

type redirectingRW struct {
	*http.Request
	http.ResponseWriter
	ignore bool
}

func (hrw *redirectingRW) Header() http.Header {
	return hrw.ResponseWriter.Header()
}

func (hrw *redirectingRW) WriteHeader(status int) {
	if status == http.StatusNotFound {
		hrw.ignore = true
		http.Redirect(hrw.ResponseWriter, hrw.Request, "/ui/#notfound", http.StatusTemporaryRedirect)
	} else {
		hrw.ResponseWriter.WriteHeader(status)
	}
}

func (hrw *redirectingRW) Write(p []byte) (int, error) {
	if hrw.ignore {
		return len(p), nil
	}

	return hrw.ResponseWriter.Write(p)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"

	dockerClient "github.com/fsouza/go-dockerclient"
)

var (
	SYSFS       string        = "/sys/fs/cgroup"
	PROCS       string        = "cgroup.procs"
	CGROUP_PROC string        = "/proc/%d/cgroup"
	INTERVAL    time.Duration = 1000
)

type Context struct {
	Args         []string
	Cgroups      []string
	AllCgroups   bool
	Logs         bool
	Notify       bool
	Name         string
	Env          bool
	Rm           bool
	Id           string
	NotifySocket string
	Cmd          *exec.Cmd
	Pid          int
	PidFile      string
	Client       *dockerClient.Client
}

func setupEnvironment(c *Context) {
	newArgs := []string{}
	if c.Notify && len(c.NotifySocket) > 0 {
		newArgs = append(newArgs, "-e", fmt.Sprintf("NOTIFY_SOCKET=%s", c.NotifySocket))
		newArgs = append(newArgs, "-v", fmt.Sprintf("%s:%s", c.NotifySocket, c.NotifySocket))
	} else {
		c.Notify = false
	}

	if c.Env {
		for _, val := range os.Environ() {
			if !strings.HasPrefix(val, "HOME=") && !strings.HasPrefix(val, "PATH=") {
				newArgs = append(newArgs, "-e", val)
			}
		}
	}

	if len(newArgs) > 0 {
		c.Args = append(newArgs, c.Args...)
	}
}

func parseContext(args []string) (*Context, error) {
	c := &Context{
		Logs:       true,
		AllCgroups: false,
	}

	flags := flag.NewFlagSet("systemd-docker", flag.ContinueOnError)

	var flCgroups opts.ListOpts

	flags.StringVar(&c.PidFile, []string{"p", "-pid-file"}, "", "pipe file")
	flags.BoolVar(&c.Logs, []string{"l", "-logs"}, true, "pipe logs")
	flags.BoolVar(&c.Notify, []string{"n", "-notify"}, false, "setup systemd notify for container")
	flags.BoolVar(&c.Env, []string{"e", "-env"}, false, "inherit environment variable")
	flags.Var(&flCgroups, []string{"c", "-cgroups"}, "cgroups to take ownership of or 'all' for all cgroups available")

	err := flags.Parse(args)
	if err != nil {
		return nil, err
	}

	foundD := false
	var name string

	runArgs := flags.Args()
	if len(runArgs) == 0 || runArgs[0] != "run" {
		log.Println("Args:", runArgs)
		return nil, errors.New("run not found in arguments")
	}

	runArgs = runArgs[1:]
	newArgs := make([]string, 0, len(runArgs))

	for i, arg := range runArgs {
		/* This is tedious, but flag can't ignore unknown flags and I don't want to define them all */
		add := true

		switch {
		case arg == "-rm" || arg == "--rm":
			c.Rm = true
			add = false
		case arg == "-d" || arg == "-detach" || arg == "--detach":
			foundD = true
		case strings.HasPrefix(arg, "-name") || strings.HasPrefix(arg, "--name"):
			if strings.Contains(arg, "=") {
				name = strings.SplitN(arg, "=", 2)[1]
			} else if len(runArgs) > i+1 {
				name = runArgs[i+1]
			}
		}

		if add {
			newArgs = append(newArgs, arg)
		}
	}

	if !foundD {
		newArgs = append([]string{"-d"}, newArgs...)
	}

	c.Name = name
	c.NotifySocket = os.Getenv("NOTIFY_SOCKET")
	c.Args = newArgs
	c.Cgroups = flCgroups.GetAll()

	for _, val := range c.Cgroups {
		if val == "all" {
			c.Cgroups = nil
			c.AllCgroups = true
			break
		}
	}

	setupEnvironment(c)

	return c, nil
}

func lookupNamedContainer(c *Context) error {
	client, err := getClient(c)
	if err != nil {
		return err
	}

	container, err := client.InspectContainer(c.Name)
	if _, ok := err.(*dockerClient.NoSuchContainer); ok {
		return nil
	}
	if err != nil || container == nil {
		return err
	}

	if container.State.Running {
		c.Id = container.ID
		c.Pid = container.State.Pid
		return nil
	} else if c.Rm {
		return client.RemoveContainer(dockerClient.RemoveContainerOptions{
			ID:    container.ID,
			Force: true,
		})
	} else {
		client, err := getClient(c)
		err = client.StartContainer(container.ID, nil)
		if err != nil {
			return err
		}

		container, err = client.InspectContainer(c.Name)
		if err != nil {
			return err
		}

		c.Id = container.ID
		c.Pid = container.State.Pid

		return nil
	}
}

func launchContainer(c *Context) error {
	args := append([]string{"run"}, c.Args...)
	c.Cmd = exec.Command("docker", args...)

	errorPipe, err := c.Cmd.StderrPipe()
	if err != nil {
		return err
	}

	outputPipe, err := c.Cmd.StdoutPipe()
	if err != nil {
		return err
	}

	err = c.Cmd.Start()
	if err != nil {
		return err
	}

	go io.Copy(os.Stderr, errorPipe)

	bytes, err := ioutil.ReadAll(outputPipe)
	if err != nil {
		return err
	}

	c.Id = strings.TrimSpace(string(bytes))

	err = c.Cmd.Wait()
	if err != nil {
		return err
	}

	if !c.Cmd.ProcessState.Success() {
		return err
	}

	c.Pid, err = getContainerPid(c)

	return err
}

func runContainer(c *Context) error {
	if len(c.Name) > 0 {
		err := lookupNamedContainer(c)
		if err != nil {
			return err
		}

	}

	if len(c.Id) == 0 {
		err := launchContainer(c)
		if err != nil {
			return err
		}
	}

	if c.Pid == 0 {
		return errors.New("Failed to launch container, pid is 0")
	}

	return nil
}

func getClient(c *Context) (*dockerClient.Client, error) {
	if c.Client != nil {
		return c.Client, nil
	}

	endpoint := os.Getenv("DOCKER_HOST")
	if len(endpoint) == 0 {
		endpoint = "unix:///var/run/docker.sock"
	}

	return dockerClient.NewVersionedClient(endpoint, "1.11")
}

func getContainerPid(c *Context) (int, error) {
	client, err := getClient(c)
	if err != nil {
		return 0, err
	}

	container, err := client.InspectContainer(c.Id)
	if err != nil {
		return 0, err
	}

	if container == nil {
		return 0, errors.New(fmt.Sprintf("Failed to find container %s", c.Id))
	}

	if container.State.Pid <= 0 {
		return 0, errors.New(fmt.Sprintf("Pid is %d for container %s", container.State.Pid, c.Id))
	}

	return container.State.Pid, nil
}

func getCgroupsForPid(pid int) (map[string]string, error) {
	file, err := os.Open(fmt.Sprintf(CGROUP_PROC, pid))
	if err != nil {
		return nil, err
	}

	ret := map[string]string{}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.SplitN(scanner.Text(), ":", 3)
		if len(line) != 3 {
			continue
		}

		ret[line[1]] = line[2]
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return ret, nil
}

func constructCgroupPath(cgroupName string, cgroupPath string) string {
	return path.Join(SYSFS, strings.TrimPrefix(cgroupName, "name="), cgroupPath, PROCS)
}

func getCgroupPids(cgroupName string, cgroupPath string) ([]string, error) {
	ret := []string{}

	file, err := os.Open(constructCgroupPath(cgroupName, cgroupPath))
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		ret = append(ret, strings.TrimSpace(scanner.Text()))
	}

	if err = scanner.Err(); err != nil {
		return nil, err
	}

	return ret, nil
}

func writePid(pid string, path string) error {
	return ioutil.WriteFile(path, []byte(pid), 0644)
}

func moveCgroups(c *Context) (bool, error) {
	moved := false
	currentCgroups, err := getCgroupsForPid(os.Getpid())
	if err != nil {
		return false, err
	}

	containerCgroups, err := getCgroupsForPid(c.Pid)
	if err != nil {
		return false, err
	}

	var ns []string

	if c.AllCgroups || c.Cgroups == nil || len(c.Cgroups) == 0 {
		ns = make([]string, 0, len(containerCgroups))
		for value, _ := range containerCgroups {
			ns = append(ns, value)
		}
	} else {
		ns = c.Cgroups
	}

	for _, nsName := range ns {
		currentPath, ok := currentCgroups[nsName]
		if !ok {
			continue
		}

		containerPath, ok := containerCgroups[nsName]
		if !ok {
			continue
		}

		if currentPath == containerPath || containerPath == "/" {
			continue
		}

		pids, err := getCgroupPids(nsName, containerPath)
		if err != nil {
			return false, err
		}

		for _, pid := range pids {
			pidInt, err := strconv.Atoi(pid)
			if err != nil {
				continue
			}

			if pidDied(pidInt) {
				continue
			}

			currentFullPath := constructCgroupPath(nsName, currentPath)
			log.Printf("Moving pid %s to %s\n", pid, currentFullPath)
			err = writePid(pid, currentFullPath)
			if err != nil {
				return false, err
			}

			moved = true
		}
	}

	return moved, nil
}

func pidDied(pid int) bool {
	_, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
	return os.IsNotExist(err)
}

func notify(c *Context) error {
	if pidDied(c.Pid) {
		return errors.New("Container exited before we could notify systemd")
	}

	if len(c.NotifySocket) == 0 {
		return nil
	}

	conn, err := net.Dial("unixgram", c.NotifySocket)
	if err != nil {
		return err
	}

	defer conn.Close()

	_, err = conn.Write([]byte(fmt.Sprintf("MAINPID=%d", c.Pid)))
	if err != nil {
		return err
	}

	if pidDied(c.Pid) {
		conn.Write([]byte(fmt.Sprintf("MAINPID=%d", os.Getpid())))
		return errors.New("Container exited before we could notify systemd")
	}

	if !c.Notify {
		_, err = conn.Write([]byte("READY=1"))
		if err != nil {
			return err
		}
	}

	return nil
}

func pidFile(c *Context) error {
	if len(c.PidFile) == 0 || c.Pid <= 0 {
		return nil
	}

	err := ioutil.WriteFile(c.PidFile, []byte(strconv.Itoa(c.Pid)), 0644)
	if err != nil {
		return err
	}

	return nil
}

func pipeLogs(c *Context) error {
	if !c.Logs {
		return nil
	}

	client, err := getClient(c)
	if err != nil {
		return err
	}

	err = client.Logs(dockerClient.LogsOptions{
		Container:    c.Id,
		Follow:       true,
		Stdout:       true,
		Stderr:       true,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stderr,
	})

	return err
}

func keepAlive(c *Context) error {
	if c.Logs || c.Rm {
		client, err := getClient(c)
		if err != nil {
			return err
		}

		/* Good old polling... */
		for true {
			container, err := client.InspectContainer(c.Id)
			if err != nil {
				return err
			}

			if container.State.Running {
				time.Sleep(INTERVAL * time.Millisecond)
			} else {
				return nil
			}
		}
	}

	return nil
}

func rmContainer(c *Context) error {
	if !c.Rm {
		return nil
	}

	client, err := getClient(c)
	if err != nil {
		return err
	}

	return client.RemoveContainer(dockerClient.RemoveContainerOptions{
		ID:    c.Id,
		Force: true,
	})
}

func mainWithArgs(args []string) (*Context, error) {
	c, err := parseContext(args)
	if err != nil {
		return c, err
	}

	err = runContainer(c)
	if err != nil {
		return c, err
	}

	_, err = moveCgroups(c)
	if err != nil {
		return c, err
	}

	err = notify(c)
	if err != nil {
		return c, err
	}

	err = pidFile(c)
	if err != nil {
		return c, err
	}

	go pipeLogs(c)

	err = keepAlive(c)
	if err != nil {
		return c, err
	}

	err = rmContainer(c)
	if err != nil {
		return c, err
	}

	return c, nil
}

func main() {
	_, err := mainWithArgs(os.Args[1:])
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Instantiate ListOpts<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"

	dockerClient "github.com/fsouza/go-dockerclient"
)

var (
	SYSFS       string        = "/sys/fs/cgroup"
	PROCS       string        = "cgroup.procs"
	CGROUP_PROC string        = "/proc/%d/cgroup"
	INTERVAL    time.Duration = 1000
)

type Context struct {
	Args         []string
	Cgroups      []string
	AllCgroups   bool
	Logs         bool
	Notify       bool
	Name         string
	Env          bool
	Rm           bool
	Id           string
	NotifySocket string
	Cmd          *exec.Cmd
	Pid          int
	PidFile      string
	Client       *dockerClient.Client
}

func setupEnvironment(c *Context) {
	newArgs := []string{}
	if c.Notify && len(c.NotifySocket) > 0 {
		newArgs = append(newArgs, "-e", fmt.Sprintf("NOTIFY_SOCKET=%s", c.NotifySocket))
		newArgs = append(newArgs, "-v", fmt.Sprintf("%s:%s", c.NotifySocket, c.NotifySocket))
	} else {
		c.Notify = false
	}

	if c.Env {
		for _, val := range os.Environ() {
			if !strings.HasPrefix(val, "HOME=") && !strings.HasPrefix(val, "PATH=") {
				newArgs = append(newArgs, "-e", val)
			}
		}
	}

	if len(newArgs) > 0 {
		c.Args = append(newArgs, c.Args...)
	}
}

func parseContext(args []string) (*Context, error) {
	c := &Context{
		Logs:       true,
		AllCgroups: false,
	}

	flags := flag.NewFlagSet("systemd-docker", flag.ContinueOnError)

	flCgroups := opts.NewListOpts(nil)

	flags.StringVar(&c.PidFile, []string{"p", "-pid-file"}, "", "pipe file")
	flags.BoolVar(&c.Logs, []string{"l", "-logs"}, true, "pipe logs")
	flags.BoolVar(&c.Notify, []string{"n", "-notify"}, false, "setup systemd notify for container")
	flags.BoolVar(&c.Env, []string{"e", "-env"}, false, "inherit environment variable")
	flags.Var(&flCgroups, []string{"c", "-cgroups"}, "cgroups to take ownership of or 'all' for all cgroups available")

	err := flags.Parse(args)
	if err != nil {
		return nil, err
	}

	foundD := false
	var name string

	runArgs := flags.Args()
	if len(runArgs) == 0 || runArgs[0] != "run" {
		log.Println("Args:", runArgs)
		return nil, errors.New("run not found in arguments")
	}

	runArgs = runArgs[1:]
	newArgs := make([]string, 0, len(runArgs))

	for i, arg := range runArgs {
		/* This is tedious, but flag can't ignore unknown flags and I don't want to define them all */
		add := true

		switch {
		case arg == "-rm" || arg == "--rm":
			c.Rm = true
			add = false
		case arg == "-d" || arg == "-detach" || arg == "--detach":
			foundD = true
		case strings.HasPrefix(arg, "-name") || strings.HasPrefix(arg, "--name"):
			if strings.Contains(arg, "=") {
				name = strings.SplitN(arg, "=", 2)[1]
			} else if len(runArgs) > i+1 {
				name = runArgs[i+1]
			}
		}

		if add {
			newArgs = append(newArgs, arg)
		}
	}

	if !foundD {
		newArgs = append([]string{"-d"}, newArgs...)
	}

	c.Name = name
	c.NotifySocket = os.Getenv("NOTIFY_SOCKET")
	c.Args = newArgs
	c.Cgroups = flCgroups.GetAll()

	for _, val := range c.Cgroups {
		if val == "all" {
			c.Cgroups = nil
			c.AllCgroups = true
			break
		}
	}

	setupEnvironment(c)

	return c, nil
}

func lookupNamedContainer(c *Context) error {
	client, err := getClient(c)
	if err != nil {
		return err
	}

	container, err := client.InspectContainer(c.Name)
	if _, ok := err.(*dockerClient.NoSuchContainer); ok {
		return nil
	}
	if err != nil || container == nil {
		return err
	}

	if container.State.Running {
		c.Id = container.ID
		c.Pid = container.State.Pid
		return nil
	} else if c.Rm {
		return client.RemoveContainer(dockerClient.RemoveContainerOptions{
			ID:    container.ID,
			Force: true,
		})
	} else {
		client, err := getClient(c)
		err = client.StartContainer(container.ID, nil)
		if err != nil {
			return err
		}

		container, err = client.InspectContainer(c.Name)
		if err != nil {
			return err
		}

		c.Id = container.ID
		c.Pid = container.State.Pid

		return nil
	}
}

func launchContainer(c *Context) error {
	args := append([]string{"run"}, c.Args...)
	c.Cmd = exec.Command("docker", args...)

	errorPipe, err := c.Cmd.StderrPipe()
	if err != nil {
		return err
	}

	outputPipe, err := c.Cmd.StdoutPipe()
	if err != nil {
		return err
	}

	err = c.Cmd.Start()
	if err != nil {
		return err
	}

	go io.Copy(os.Stderr, errorPipe)

	bytes, err := ioutil.ReadAll(outputPipe)
	if err != nil {
		return err
	}

	c.Id = strings.TrimSpace(string(bytes))

	err = c.Cmd.Wait()
	if err != nil {
		return err
	}

	if !c.Cmd.ProcessState.Success() {
		return err
	}

	c.Pid, err = getContainerPid(c)

	return err
}

func runContainer(c *Context) error {
	if len(c.Name) > 0 {
		err := lookupNamedContainer(c)
		if err != nil {
			return err
		}

	}

	if len(c.Id) == 0 {
		err := launchContainer(c)
		if err != nil {
			return err
		}
	}

	if c.Pid == 0 {
		return errors.New("Failed to launch container, pid is 0")
	}

	return nil
}

func getClient(c *Context) (*dockerClient.Client, error) {
	if c.Client != nil {
		return c.Client, nil
	}

	endpoint := os.Getenv("DOCKER_HOST")
	if len(endpoint) == 0 {
		endpoint = "unix:///var/run/docker.sock"
	}

	return dockerClient.NewVersionedClient(endpoint, "1.11")
}

func getContainerPid(c *Context) (int, error) {
	client, err := getClient(c)
	if err != nil {
		return 0, err
	}

	container, err := client.InspectContainer(c.Id)
	if err != nil {
		return 0, err
	}

	if container == nil {
		return 0, errors.New(fmt.Sprintf("Failed to find container %s", c.Id))
	}

	if container.State.Pid <= 0 {
		return 0, errors.New(fmt.Sprintf("Pid is %d for container %s", container.State.Pid, c.Id))
	}

	return container.State.Pid, nil
}

func getCgroupsForPid(pid int) (map[string]string, error) {
	file, err := os.Open(fmt.Sprintf(CGROUP_PROC, pid))
	if err != nil {
		return nil, err
	}

	ret := map[string]string{}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.SplitN(scanner.Text(), ":", 3)
		if len(line) != 3 {
			continue
		}

		ret[line[1]] = line[2]
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return ret, nil
}

func constructCgroupPath(cgroupName string, cgroupPath string) string {
	return path.Join(SYSFS, strings.TrimPrefix(cgroupName, "name="), cgroupPath, PROCS)
}

func getCgroupPids(cgroupName string, cgroupPath string) ([]string, error) {
	ret := []string{}

	file, err := os.Open(constructCgroupPath(cgroupName, cgroupPath))
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		ret = append(ret, strings.TrimSpace(scanner.Text()))
	}

	if err = scanner.Err(); err != nil {
		return nil, err
	}

	return ret, nil
}

func writePid(pid string, path string) error {
	return ioutil.WriteFile(path, []byte(pid), 0644)
}

func moveCgroups(c *Context) (bool, error) {
	moved := false
	currentCgroups, err := getCgroupsForPid(os.Getpid())
	if err != nil {
		return false, err
	}

	containerCgroups, err := getCgroupsForPid(c.Pid)
	if err != nil {
		return false, err
	}

	var ns []string

	if c.AllCgroups || c.Cgroups == nil || len(c.Cgroups) == 0 {
		ns = make([]string, 0, len(containerCgroups))
		for value, _ := range containerCgroups {
			ns = append(ns, value)
		}
	} else {
		ns = c.Cgroups
	}

	for _, nsName := range ns {
		currentPath, ok := currentCgroups[nsName]
		if !ok {
			continue
		}

		containerPath, ok := containerCgroups[nsName]
		if !ok {
			continue
		}

		if currentPath == containerPath || containerPath == "/" {
			continue
		}

		pids, err := getCgroupPids(nsName, containerPath)
		if err != nil {
			return false, err
		}

		for _, pid := range pids {
			pidInt, err := strconv.Atoi(pid)
			if err != nil {
				continue
			}

			if pidDied(pidInt) {
				continue
			}

			currentFullPath := constructCgroupPath(nsName, currentPath)
			log.Printf("Moving pid %s to %s\n", pid, currentFullPath)
			err = writePid(pid, currentFullPath)
			if err != nil {
				return false, err
			}

			moved = true
		}
	}

	return moved, nil
}

func pidDied(pid int) bool {
	_, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
	return os.IsNotExist(err)
}

func notify(c *Context) error {
	if pidDied(c.Pid) {
		return errors.New("Container exited before we could notify systemd")
	}

	if len(c.NotifySocket) == 0 {
		return nil
	}

	conn, err := net.Dial("unixgram", c.NotifySocket)
	if err != nil {
		return err
	}

	defer conn.Close()

	_, err = conn.Write([]byte(fmt.Sprintf("MAINPID=%d", c.Pid)))
	if err != nil {
		return err
	}

	if pidDied(c.Pid) {
		conn.Write([]byte(fmt.Sprintf("MAINPID=%d", os.Getpid())))
		return errors.New("Container exited before we could notify systemd")
	}

	if !c.Notify {
		_, err = conn.Write([]byte("READY=1"))
		if err != nil {
			return err
		}
	}

	return nil
}

func pidFile(c *Context) error {
	if len(c.PidFile) == 0 || c.Pid <= 0 {
		return nil
	}

	err := ioutil.WriteFile(c.PidFile, []byte(strconv.Itoa(c.Pid)), 0644)
	if err != nil {
		return err
	}

	return nil
}

func pipeLogs(c *Context) error {
	if !c.Logs {
		return nil
	}

	client, err := getClient(c)
	if err != nil {
		return err
	}

	err = client.Logs(dockerClient.LogsOptions{
		Container:    c.Id,
		Follow:       true,
		Stdout:       true,
		Stderr:       true,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stderr,
	})

	return err
}

func keepAlive(c *Context) error {
	if c.Logs || c.Rm {
		client, err := getClient(c)
		if err != nil {
			return err
		}

		/* Good old polling... */
		for true {
			container, err := client.InspectContainer(c.Id)
			if err != nil {
				return err
			}

			if container.State.Running {
				time.Sleep(INTERVAL * time.Millisecond)
			} else {
				return nil
			}
		}
	}

	return nil
}

func rmContainer(c *Context) error {
	if !c.Rm {
		return nil
	}

	client, err := getClient(c)
	if err != nil {
		return err
	}

	return client.RemoveContainer(dockerClient.RemoveContainerOptions{
		ID:    c.Id,
		Force: true,
	})
}

func mainWithArgs(args []string) (*Context, error) {
	c, err := parseContext(args)
	if err != nil {
		return c, err
	}

	err = runContainer(c)
	if err != nil {
		return c, err
	}

	_, err = moveCgroups(c)
	if err != nil {
		return c, err
	}

	err = notify(c)
	if err != nil {
		return c, err
	}

	err = pidFile(c)
	if err != nil {
		return c, err
	}

	go pipeLogs(c)

	err = keepAlive(c)
	if err != nil {
		return c, err
	}

	err = rmContainer(c)
	if err != nil {
		return c, err
	}

	return c, nil
}

func main() {
	_, err := mainWithArgs(os.Args[1:])
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/fatih/color"
	"github.com/justwatchcom/gopass/action"
	"github.com/mattn/go-colorable"
	"github.com/urfave/cli"
)

const (
	name = "gopass"
)

var (
	// Version is the released version of gopass
	Version string
	// BuildTime is the time the binary was built
	BuildTime string
	// Commit is the git hash the binary was built from
	Commit string
)

type errorWriter struct {
	out io.Writer
}

func (e errorWriter) Write(p []byte) (int, error) {
	return e.out.Write([]byte("\n" + color.RedString("Error: "+string(p))))
}

func main() {
	cli.ErrWriter = errorWriter{
		out: colorable.NewColorableStderr(),
	}

	cli.VersionPrinter = func(c *cli.Context) {
		buildtime := ""
		if bt, err := time.Parse("2006-01-02T15:04:05-0700", BuildTime); err == nil {
			buildtime = bt.Format("2006-01-02 15:04:05")
		}
		if Version == "" {
			Version = "HEAD"
		}
		if Commit == "" {
			Commit = "n/a"
		}
		fmt.Printf("%s %s (%s %s) %s\n",
			name,
			Version,
			Commit,
			buildtime,
			runtime.Version(),
		)
	}

	action := action.New(Version)
	app := cli.NewApp()

	app.Name = name
	app.Version = Version
	app.Usage = "The standard unix password manager - rewritten in Go"
	app.EnableBashCompletion = true
	app.BashComplete = func(c *cli.Context) {
		cli.DefaultAppComplete(c)
		action.Complete(c)
	}

	app.Action = func(c *cli.Context) error {
		if err := action.Initialized(c); err != nil {
			return err
		}

		if c.Args().Present() {
			return action.Show(c)
		}
		return action.List(c)
	}

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "clip, c",
			Usage: "Copy the secret into the clipboard",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:        "clone",
			Usage:       "Clone a new store",
			Description: "To clone a remote repo",
			Action:      action.Clone,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "path",
					Usage: "Path to clone the repo to",
				},
			},
		},
		{
			Name:  "completion",
			Usage: "Source the output with bash or zsh to get auto completion",
			Subcommands: []cli.Command{{
				Name:   "bash",
				Usage:  "Source for auto completion in bash",
				Action: action.CompletionBash,
			}, {
				Name:   "zsh",
				Usage:  "Source for auto completion in zsh",
				Action: action.CompletionZSH,
			}, {
				Name:   "dmenu",
				Usage:  "Completion output for dmenu",
				Action: action.CompletionDMenu,
				Flags: []cli.Flag{
					cli.BoolFlag{
						Name:  "type",
						Usage: "Type the password with xdotool",
					},
					cli.StringFlag{
						Name:  "args",
						Usage: "Arguments passed to dmenu itself",
					},
				},
			}},
		},
		{
			Name:        "config",
			Usage:       "Edit configuration",
			Description: "To manipulate the gopass configuration",
			Action:      action.Config,
		},
		{
			Name:         "copy",
			Aliases:      []string{"cp"},
			Usage:        "Copies old-path to new-path, optionally forcefully, selectively reencrypting.",
			Before:       action.Initialized,
			Action:       action.Copy,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to copy the secret and overwrite existing one",
				},
			},
		},
		{
			Name:         "delete",
			Usage:        "Remove existing secret or directory, optionally forcefully.",
			Aliases:      []string{"remove", "rm"},
			Before:       action.Initialized,
			Action:       action.Delete,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "recursive, r",
					Usage: "f",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to delete the secret",
				},
			},
		},
		{
			Name:         "edit",
			Usage:        "Insert a new secret or edit an existing secret using $EDITOR.",
			Before:       action.Initialized,
			Action:       action.Edit,
			BashComplete: action.Complete,
		},
		{
			Name:         "find",
			Usage:        "List secrets that match the search term.",
			Before:       action.Initialized,
			Action:       action.Find,
			Aliases:      []string{"search"},
			BashComplete: action.Complete,
		},
		{
			Name:        "fsck",
			Usage:       "Check store integrity",
			Description: "Check integrity of all stores",
			Before:      action.Initialized,
			Action:      action.Fsck,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "check, c",
					Usage: "Only report",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Auto-correct any errors, do not ask",
				},
			},
		},
		{
			Name:  "generate",
			Usage: "Generate a new password of the specified length with optionally no symbols.",
			Description: "" +
				"Generate a new password of the specified length with optionally no symbols. " +
				"Optionally put it on the clipboard and clear board after 45 seconds. " +
				"Prompt before overwriting existing password unless forced. " +
				"Optionally replace only the first line of an existing file with a new password.",
			Before:       action.Initialized,
			Action:       action.Generate,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "clip, c",
					Usage: "Copy the password into the clipboard",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to overwrite existing password",
				},
				cli.BoolFlag{
					Name:  "no-symbols, n",
					Usage: "Don't use symbols in the password",
				},
			},
		},
		{
			Name:        "git",
			Usage:       "Do git things",
			Description: "If the password store is a git repository, execute a git command specified by git-command-args.",
			Before:      action.Initialized,
			Action:      action.Git,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "store",
					Usage: "Store to operate on",
				},
			},
			Subcommands: []cli.Command{
				{
					Name:        "init",
					Usage:       "Init git repo",
					Description: "Create and initialize a new git repo in the store",
					Before:      action.Initialized,
					Action:      action.GitInit,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "store",
							Usage: "Store to operate on",
						},
						cli.StringFlag{
							Name:  "sign-key",
							Usage: "GPG Key to sign commits",
						},
					},
				},
			},
		},
		{
			Name:   "grep",
			Usage:  "Search for secrets files containing search-string when decrypted.",
			Before: action.Initialized,
			Action: action.Grep,
		},
		{
			Name:  "init",
			Usage: "Initialize new password storage and use gpg-id for encryption.",
			Description: "" +
				"Initialize new password storage and use gpg-id for encryption.",
			Action: action.Init,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "store, s",
					Usage: "Set the sub store to operate on",
				},
				cli.BoolFlag{
					Name:  "nogit",
					Usage: "Do not init git repo",
				},
			},
		},
		{
			Name:  "insert",
			Usage: "Insert new secret",
			Description: "" +
				"Insert new secret. Optionally, echo the secret back to the console during entry. " +
				"Or, optionally, the entry may be multiline. " +
				"Prompt before overwriting existing secret unless forced.",
			Before:       action.Initialized,
			Action:       action.Insert,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "echo, e",
					Usage: "Display secret while typing",
				},
				cli.BoolFlag{
					Name:  "multiline, m",
					Usage: "Insert using $EDITOR",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Overwrite any existing secret and do not prompt to confirm recipients",
				},
			},
		},
		{
			Name:         "list",
			Usage:        "List secrets.",
			Aliases:      []string{"ls"},
			Before:       action.Initialized,
			Action:       action.List,
			BashComplete: action.Complete,
		},
		{
			Name:         "move",
			Aliases:      []string{"mv"},
			Usage:        "Renames or moves old-path to new-path, optionally forcefully, selectively reencrypting.",
			Before:       action.Initialized,
			Action:       action.Move,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to move the secret and overwrite existing one",
				},
			},
		},
		{
			Name:        "mounts",
			Usage:       "Edit mounts",
			Description: "To manipulate gopass mounts",
			Before:      action.Initialized,
			Action:      action.MountsPrint,
			Subcommands: []cli.Command{
				{
					Name:        "add",
					Usage:       "Add mount",
					Description: "To add a new mounted sub store",
					Before:      action.Initialized,
					Action:      action.MountAdd,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "init, i",
							Usage: "Init the store with the given recpient key",
						},
					},
				},
				{
					Name:         "remove",
					Usage:        "Remove mount",
					Description:  "To remove a mounted sub store",
					Before:       action.Initialized,
					Action:       action.MountRemove,
					BashComplete: action.MountsComplete,
				},
			},
		},
		{
			Name:        "recipients",
			Usage:       "List Recipients",
			Description: "To show all recipients",
			Before:      action.Initialized,
			Action:      action.RecipientsPrint,
			Subcommands: []cli.Command{
				{
					Name:        "add",
					Usage:       "Add any number of Recipients",
					Description: "To add any number of recipients to a store",
					Before:      action.Initialized,
					Action:      action.RecipientsAdd,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "store",
							Usage: "Store to operate on",
						},
					},
				},
				{
					Name:         "remove",
					Usage:        "Remove any number of Recipients",
					Description:  "To remove any number of recipients from a store",
					Before:       action.Initialized,
					Action:       action.RecipientsRemove,
					BashComplete: action.RecipientsComplete,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "store",
							Usage: "Store to operate on",
						},
					},
				},
			},
		},
		{
			Name:  "show",
			Usage: "Show existing secret and optionally put it on the clipboard.",
			Description: "" +
				"Show existing secret and optionally put it on the clipboard. " +
				"If put on the clipboard, it will be cleared in 45 seconds.",
			Before:       action.Initialized,
			Action:       action.Show,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "clip, c",
					Usage: "Copy the secret into the clipboard",
				},
				cli.BoolFlag{
					Name:  "qr",
					Usage: "Print secret as QR Code",
				},
			},
		},
		{
			Name:        "unclip",
			Usage:       "Internal command to clear clipboard",
			Description: "Clear the clipboard if the content matches the checksum",
			Action:      action.Unclip,
			Hidden:      true,
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "timeout",
					Usage: "Time to wait",
				},
			},
		},
		{
			Name:        "version",
			Usage:       "Print gopass version",
			Description: "Display version and build time information",
			Action:      action.Version,
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Document behavior of `-c`<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/fatih/color"
	"github.com/justwatchcom/gopass/action"
	"github.com/mattn/go-colorable"
	"github.com/urfave/cli"
)

const (
	name = "gopass"
)

var (
	// Version is the released version of gopass
	Version string
	// BuildTime is the time the binary was built
	BuildTime string
	// Commit is the git hash the binary was built from
	Commit string
)

type errorWriter struct {
	out io.Writer
}

func (e errorWriter) Write(p []byte) (int, error) {
	return e.out.Write([]byte("\n" + color.RedString("Error: "+string(p))))
}

func main() {
	cli.ErrWriter = errorWriter{
		out: colorable.NewColorableStderr(),
	}

	cli.VersionPrinter = func(c *cli.Context) {
		buildtime := ""
		if bt, err := time.Parse("2006-01-02T15:04:05-0700", BuildTime); err == nil {
			buildtime = bt.Format("2006-01-02 15:04:05")
		}
		if Version == "" {
			Version = "HEAD"
		}
		if Commit == "" {
			Commit = "n/a"
		}
		fmt.Printf("%s %s (%s %s) %s\n",
			name,
			Version,
			Commit,
			buildtime,
			runtime.Version(),
		)
	}

	action := action.New(Version)
	app := cli.NewApp()

	app.Name = name
	app.Version = Version
	app.Usage = "The standard unix password manager - rewritten in Go"
	app.EnableBashCompletion = true
	app.BashComplete = func(c *cli.Context) {
		cli.DefaultAppComplete(c)
		action.Complete(c)
	}

	app.Action = func(c *cli.Context) error {
		if err := action.Initialized(c); err != nil {
			return err
		}

		if c.Args().Present() {
			return action.Show(c)
		}
		return action.List(c)
	}

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "clip, c",
			Usage: "Copy the first line of the secret into the clipboard",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:        "clone",
			Usage:       "Clone a new store",
			Description: "To clone a remote repo",
			Action:      action.Clone,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "path",
					Usage: "Path to clone the repo to",
				},
			},
		},
		{
			Name:  "completion",
			Usage: "Source the output with bash or zsh to get auto completion",
			Subcommands: []cli.Command{{
				Name:   "bash",
				Usage:  "Source for auto completion in bash",
				Action: action.CompletionBash,
			}, {
				Name:   "zsh",
				Usage:  "Source for auto completion in zsh",
				Action: action.CompletionZSH,
			}, {
				Name:   "dmenu",
				Usage:  "Completion output for dmenu",
				Action: action.CompletionDMenu,
				Flags: []cli.Flag{
					cli.BoolFlag{
						Name:  "type",
						Usage: "Type the password with xdotool",
					},
					cli.StringFlag{
						Name:  "args",
						Usage: "Arguments passed to dmenu itself",
					},
				},
			}},
		},
		{
			Name:        "config",
			Usage:       "Edit configuration",
			Description: "To manipulate the gopass configuration",
			Action:      action.Config,
		},
		{
			Name:         "copy",
			Aliases:      []string{"cp"},
			Usage:        "Copies old-path to new-path, optionally forcefully, selectively reencrypting.",
			Before:       action.Initialized,
			Action:       action.Copy,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to copy the secret and overwrite existing one",
				},
			},
		},
		{
			Name:         "delete",
			Usage:        "Remove existing secret or directory, optionally forcefully.",
			Aliases:      []string{"remove", "rm"},
			Before:       action.Initialized,
			Action:       action.Delete,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "recursive, r",
					Usage: "f",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to delete the secret",
				},
			},
		},
		{
			Name:         "edit",
			Usage:        "Insert a new secret or edit an existing secret using $EDITOR.",
			Before:       action.Initialized,
			Action:       action.Edit,
			BashComplete: action.Complete,
		},
		{
			Name:         "find",
			Usage:        "List secrets that match the search term.",
			Before:       action.Initialized,
			Action:       action.Find,
			Aliases:      []string{"search"},
			BashComplete: action.Complete,
		},
		{
			Name:        "fsck",
			Usage:       "Check store integrity",
			Description: "Check integrity of all stores",
			Before:      action.Initialized,
			Action:      action.Fsck,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "check, c",
					Usage: "Only report",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Auto-correct any errors, do not ask",
				},
			},
		},
		{
			Name:  "generate",
			Usage: "Generate a new password of the specified length with optionally no symbols.",
			Description: "" +
				"Generate a new password of the specified length with optionally no symbols. " +
				"Optionally put it on the clipboard and clear board after 45 seconds. " +
				"Prompt before overwriting existing password unless forced. " +
				"Optionally replace only the first line of an existing file with a new password.",
			Before:       action.Initialized,
			Action:       action.Generate,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "clip, c",
					Usage: "Copy the password into the clipboard",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to overwrite existing password",
				},
				cli.BoolFlag{
					Name:  "no-symbols, n",
					Usage: "Don't use symbols in the password",
				},
			},
		},
		{
			Name:        "git",
			Usage:       "Do git things",
			Description: "If the password store is a git repository, execute a git command specified by git-command-args.",
			Before:      action.Initialized,
			Action:      action.Git,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "store",
					Usage: "Store to operate on",
				},
			},
			Subcommands: []cli.Command{
				{
					Name:        "init",
					Usage:       "Init git repo",
					Description: "Create and initialize a new git repo in the store",
					Before:      action.Initialized,
					Action:      action.GitInit,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "store",
							Usage: "Store to operate on",
						},
						cli.StringFlag{
							Name:  "sign-key",
							Usage: "GPG Key to sign commits",
						},
					},
				},
			},
		},
		{
			Name:   "grep",
			Usage:  "Search for secrets files containing search-string when decrypted.",
			Before: action.Initialized,
			Action: action.Grep,
		},
		{
			Name:  "init",
			Usage: "Initialize new password storage and use gpg-id for encryption.",
			Description: "" +
				"Initialize new password storage and use gpg-id for encryption.",
			Action: action.Init,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "store, s",
					Usage: "Set the sub store to operate on",
				},
				cli.BoolFlag{
					Name:  "nogit",
					Usage: "Do not init git repo",
				},
			},
		},
		{
			Name:  "insert",
			Usage: "Insert new secret",
			Description: "" +
				"Insert new secret. Optionally, echo the secret back to the console during entry. " +
				"Or, optionally, the entry may be multiline. " +
				"Prompt before overwriting existing secret unless forced.",
			Before:       action.Initialized,
			Action:       action.Insert,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "echo, e",
					Usage: "Display secret while typing",
				},
				cli.BoolFlag{
					Name:  "multiline, m",
					Usage: "Insert using $EDITOR",
				},
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Overwrite any existing secret and do not prompt to confirm recipients",
				},
			},
		},
		{
			Name:         "list",
			Usage:        "List secrets.",
			Aliases:      []string{"ls"},
			Before:       action.Initialized,
			Action:       action.List,
			BashComplete: action.Complete,
		},
		{
			Name:         "move",
			Aliases:      []string{"mv"},
			Usage:        "Renames or moves old-path to new-path, optionally forcefully, selectively reencrypting.",
			Before:       action.Initialized,
			Action:       action.Move,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "force, f",
					Usage: "Force to move the secret and overwrite existing one",
				},
			},
		},
		{
			Name:        "mounts",
			Usage:       "Edit mounts",
			Description: "To manipulate gopass mounts",
			Before:      action.Initialized,
			Action:      action.MountsPrint,
			Subcommands: []cli.Command{
				{
					Name:        "add",
					Usage:       "Add mount",
					Description: "To add a new mounted sub store",
					Before:      action.Initialized,
					Action:      action.MountAdd,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "init, i",
							Usage: "Init the store with the given recpient key",
						},
					},
				},
				{
					Name:         "remove",
					Usage:        "Remove mount",
					Description:  "To remove a mounted sub store",
					Before:       action.Initialized,
					Action:       action.MountRemove,
					BashComplete: action.MountsComplete,
				},
			},
		},
		{
			Name:        "recipients",
			Usage:       "List Recipients",
			Description: "To show all recipients",
			Before:      action.Initialized,
			Action:      action.RecipientsPrint,
			Subcommands: []cli.Command{
				{
					Name:        "add",
					Usage:       "Add any number of Recipients",
					Description: "To add any number of recipients to a store",
					Before:      action.Initialized,
					Action:      action.RecipientsAdd,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "store",
							Usage: "Store to operate on",
						},
					},
				},
				{
					Name:         "remove",
					Usage:        "Remove any number of Recipients",
					Description:  "To remove any number of recipients from a store",
					Before:       action.Initialized,
					Action:       action.RecipientsRemove,
					BashComplete: action.RecipientsComplete,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "store",
							Usage: "Store to operate on",
						},
					},
				},
			},
		},
		{
			Name:  "show",
			Usage: "Show existing secret and optionally put its first line on the clipboard.",
			Description: "" +
				"Show existing secret and optionally put its first line on the clipboard. " +
				"If put on the clipboard, it will be cleared in 45 seconds.",
			Before:       action.Initialized,
			Action:       action.Show,
			BashComplete: action.Complete,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "clip, c",
					Usage: "Copy the first line of the secret into the clipboard",
				},
				cli.BoolFlag{
					Name:  "qr",
					Usage: "Print the first line of the secret as QR Code",
				},
			},
		},
		{
			Name:        "unclip",
			Usage:       "Internal command to clear clipboard",
			Description: "Clear the clipboard if the content matches the checksum",
			Action:      action.Unclip,
			Hidden:      true,
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "timeout",
					Usage: "Time to wait",
				},
			},
		},
		{
			Name:        "version",
			Usage:       "Print gopass version",
			Description: "Display version and build time information",
			Action:      action.Version,
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"io"
	"log"
	"net/mail"
	"net/smtp"
	"os"
	"strconv"
	"strings"

	message "github.com/emersion/go-message"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	// Перенаправление логов в файл
	// создать файл лога, установить права доступа
	// l, err := os.OpenFile("./klshmail.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	// l, err := os.OpenFile("/var/log/klshmail.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	// logFatal(err)
	// defer l.Close()

	// log.SetOutput(l)

	// Заголовки исходного сообщения,
	// которые нужно оставить
	hh := []string{
		"MIME-Version",
		"Message-Id",
		"Subject",
		"Content-Type",
		"Content-Transfer-Encoding",
		"In-Reply-To",
		"References",
	}

	// r, err := os.Open("./processor/testmsg.eml")
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// m, err := message.Read(r)

	// Читаем сообщение из стандартного ввода
	m, err := message.Read(os.Stdin)
	logFatal(err)
	log.Println("New message accepted...")

	// Разбор сообщения
	to, err := mail.ParseAddress(m.Header.Get("to"))
	logFatal(err)
	log.Printf("To: %s <%s>\n", to.Name, to.Address)

	// Разбор заголовков сообщения
	from, err := mail.ParseAddress(m.Header.Get("from"))
	logFatal(err)
	log.Printf("From: %s <%s>\n", from.Name, from.Address)

	if to.Address == "" || from.Address == "" {
		log.Fatalln("Empty address. Reject message.")
	}

	// Сокдинение с сервером БД
	db, err := sql.Open("mysql", "klshmail:euXoe8uSha1xu4sh@/klshmail?charset=utf8")
	logFatal(err)

	// Проверка соединения с сервером БД
	err = db.Ping()
	logFatal(err)

	// Запрос данных о списке рассылки
	var lid uint64
	var lprefix string

	err = db.QueryRow(`
		SELECT list.id, list.prefix
		FROM list
		WHERE LCASE(list.email)=TRIM(LCASE(?))
		AND list.active
		`, to.Address).Scan(&lid, &lprefix)
	if err != nil {
		log.Println("No list with address:", to.Address)
		os.Exit(0)
	}

	// Запрос на проверку прав пользователя на отправку сообщений в список
	var uid uint64
	err = db.QueryRow(`
		SELECT user.id
		FROM user
		INNER JOIN user_list
		ON (user_list.lid=?
			AND user.id=user_list.uid
			AND user_list.canwrite
		)
		WHERE LCASE(user.email)=TRIM(LCASE(?))
		AND user.active
		`, lid, from.Address).Scan(&uid)
	if err != nil {
		log.Println("User", from.Address, "can't send messages to", to.Address)
		os.Exit(0)
	}

	// Формирование заголовков нового сообщения
	// var newmessage string
	var newHeader message.Header
	newHeader = make(message.Header)

	for _, k := range hh {
		if h := m.Header.Get(k); h != "" {
			newHeader.Set(k, h)
		}
	}

	sender := new(mail.Address)
	sender.Name = from.Name
	sender.Address = from.Address

	from.Name = fmt.Sprintf("%s", lprefix)
	from.Address = to.Address
	newHeader.Set("From", from.String())
	newHeader.Set("Reply-To", to.Address)
	newHeader.Set("X-KLSH-Sender", strconv.FormatUint(uid, 10))

	var b bytes.Buffer
	w, err := message.CreateWriter(&b, newHeader)
	if err != nil {
		log.Fatal(err)
	}

	if err := transform(w, m, sender); err != nil {
		log.Fatal(err)
	}
	w.Close()

	// log.Println(b.String())

	// Подключение к SMTP серверу
	c, err := smtp.Dial("127.0.0.1:25")
	if err != nil {
		log.Println("SMTP connection error")
		log.Fatal(err)
	}
	defer c.Close()

	// Отправка сообщения
	c.Mail(to.Address)
	c.Rcpt(fmt.Sprintf("%v@klshmail", lid))

	wc, err := c.Data()
	logFatal(err)
	defer wc.Close()

	// Отправка заголовков сообщения
	// if _, err = wc.Write([]byte(newmessage)); err != nil {
	// 	log.Println("SMTP send headers error")
	// 	log.Fatalln(err)
	// }

	// Отправка тела сообщения
	br := bytes.NewReader(b.Bytes())
	if _, err = io.Copy(wc, br); err != nil {
		log.Println("SMTP send body error")
		log.Fatalln(err)
	}

	// Завершение работы
	log.Println("Message processing done.")

}

func logFatal(e error) {
	if e != nil {
		log.Fatalln(e)
	}
}

const senderHtml string = `<p><b>Сообщение от:</b> %s <<a href="mailto:%s">%s</a>><p>`
const senderPlain string = "Сообщение от:  %s <%s>\n— — — — — —\n\n"

func transform(w *message.Writer, e *message.Entity, sender *mail.Address) error {
	if mr := e.MultipartReader(); mr != nil {
		// This is a multipart entity, transform each of its parts
		for {
			p, err := mr.NextPart()
			if err == io.EOF {
				break
			} else if err != nil {
				return err
			}

			pw, err := w.CreatePart(p.Header)
			if err != nil {
				return err
			}

			if err := transform(pw, p, sender); err != nil {
				return err
			}

			pw.Close()
		}
		return nil
	} else {
		body := e.Body
		var newLine string
		if strings.HasPrefix(e.Header.Get("Content-Type"), "text/plain") {
			newLine = fmt.Sprintf(senderPlain, sender.Name, sender.Address)
		}
		if strings.HasPrefix(e.Header.Get("Content-Type"), "text/html") {
			newLine = fmt.Sprintf(senderHtml, sender.Name, sender.Address, sender.Address)
		}
		body = io.MultiReader(strings.NewReader(newLine), body)
		_, err := io.Copy(w, body)
		return err
	}
}
<commit_msg>Update main.go<commit_after>package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"io"
	"log"
	"mime"
	"net/mail"
	"net/smtp"
	"os"
	"strconv"
	"strings"

	message "github.com/emersion/go-message"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	// Перенаправление логов в файл
	// создать файл лога, установить права доступа
	// l, err := os.OpenFile("./klshmail.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	// l, err := os.OpenFile("/var/log/klshmail.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	// logFatal(err)
	// defer l.Close()

	// log.SetOutput(l)

	// Заголовки исходного сообщения,
	// которые нужно оставить
	hh := []string{
		"MIME-Version",
		"Message-Id",
		"Subject",
		"Content-Type",
		"Content-Transfer-Encoding",
		"In-Reply-To",
		"References",
	}

	// r, err := os.Open("./processor/testmsg.eml")
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// m, err := message.Read(r)

	// Читаем сообщение из стандартного ввода
	m, err := message.Read(os.Stdin)
	logFatal(err)
	log.Println("New message accepted...")

	// Разбор сообщения
	to, err := mail.ParseAddress(m.Header.Get("to"))
	logFatal(err)
	log.Printf("To: %s <%s>\n", to.Name, to.Address)

	// Разбор заголовков сообщения
	from, err := mail.ParseAddress(m.Header.Get("from"))
	logFatal(err)
	log.Printf("From: %s <%s>\n", from.Name, from.Address)

	if to.Address == "" || from.Address == "" {
		log.Fatalln("Empty address. Reject message.")
	}

	// Сокдинение с сервером БД
	db, err := sql.Open("mysql", "klshmail:euXoe8uSha1xu4sh@/klshmail?charset=utf8")
	logFatal(err)

	// Проверка соединения с сервером БД
	err = db.Ping()
	logFatal(err)

	// Запрос данных о списке рассылки
	var lid uint64
	var lprefix string

	err = db.QueryRow(`
		SELECT list.id, list.prefix
		FROM list
		WHERE LCASE(list.email)=TRIM(LCASE(?))
		AND list.active
		`, to.Address).Scan(&lid, &lprefix)
	if err != nil {
		log.Println("No list with address:", to.Address)
		os.Exit(0)
	}

	// Запрос на проверку прав пользователя на отправку сообщений в список
	var uid uint64
	err = db.QueryRow(`
		SELECT user.id
		FROM user
		INNER JOIN user_list
		ON (user_list.lid=?
			AND user.id=user_list.uid
			AND user_list.canwrite
		)
		WHERE LCASE(user.email)=TRIM(LCASE(?))
		AND user.active
		`, lid, from.Address).Scan(&uid)
	if err != nil {
		log.Println("User", from.Address, "can't send messages to", to.Address)
		os.Exit(0)
	}

	// Формирование заголовков нового сообщения
	// var newmessage string
	var newHeader message.Header
	newHeader = make(message.Header)

	dec := new(mime.WordDecoder)

	for _, k := range hh {
		if h := m.Header.Get(k); h != "" {
			// newHeader[k] := h
			dh, _ := dec.Decode(h)
			newHeader.Set(k, dh)
		}
	}

	sender := new(mail.Address)
	sender.Name = from.Name
	sender.Address = from.Address

	from.Name = fmt.Sprintf("%s", lprefix)
	from.Address = to.Address
	newHeader.Set("From", from.String())
	newHeader.Set("Reply-To", to.Address)
	newHeader.Set("X-KLSH-Sender", strconv.FormatUint(uid, 10))
	// newHeader["From"] := from.String()
	// newHeader["Reply-To"] := to.Address
	// newHeader["X-KLSH-Sender"] := strconv.FormatUint(uid, 10)

	var b bytes.Buffer
	w, err := message.CreateWriter(&b, newHeader)
	if err != nil {
		log.Fatal(err)
	}

	if err := transform(w, m, sender); err != nil {
		log.Fatal(err)
	}
	w.Close()

	// log.Println(b.String())

	// Подключение к SMTP серверу
	c, err := smtp.Dial("127.0.0.1:25")
	if err != nil {
		log.Println("SMTP connection error")
		log.Fatal(err)
	}
	defer c.Close()

	// Отправка сообщения
	c.Mail(to.Address)
	c.Rcpt(fmt.Sprintf("%v@klshmail", lid))

	wc, err := c.Data()
	logFatal(err)
	defer wc.Close()

	// Отправка заголовков сообщения
	// if _, err = wc.Write([]byte(newmessage)); err != nil {
	// 	log.Println("SMTP send headers error")
	// 	log.Fatalln(err)
	// }

	// Отправка тела сообщения
	br := bytes.NewReader(b.Bytes())
	if _, err = io.Copy(wc, br); err != nil {
		log.Println("SMTP send body error")
		log.Fatalln(err)
	}

	// Завершение работы
	log.Println("Message processing done.")

}

func logFatal(e error) {
	if e != nil {
		log.Fatalln(e)
	}
}

const senderHtml string = `<p><b>Сообщение от:</b> %s <<a href="mailto:%s">%s</a>><p>`
const senderPlain string = "| Сообщение от:  %s <%s>\n——\n\n"

func transform(w *message.Writer, e *message.Entity, sender *mail.Address) error {
	if mr := e.MultipartReader(); mr != nil {
		// This is a multipart entity, transform each of its parts
		for {
			p, err := mr.NextPart()
			if err == io.EOF {
				break
			} else if err != nil {
				return err
			}

			pw, err := w.CreatePart(p.Header)
			if err != nil {
				return err
			}

			if err := transform(pw, p, sender); err != nil {
				return err
			}

			pw.Close()
		}
		return nil
	} else {
		body := e.Body
		var newLine string
		if strings.HasPrefix(e.Header.Get("Content-Type"), "text/plain") {
			newLine = fmt.Sprintf(senderPlain, sender.Name, sender.Address)
		}
		if strings.HasPrefix(e.Header.Get("Content-Type"), "text/html") {
			newLine = fmt.Sprintf(senderHtml, sender.Name, sender.Address, sender.Address)
		}
		body = io.MultiReader(strings.NewReader(newLine), body)
		_, err := io.Copy(w, body)
		return err
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"time"
)

type ProgressCallbackType int

const (
	// Process is figuring out what to do
	ProgressCalculate ProgressCallbackType = iota
	// Process is transferring data
	ProgressTransferBytes ProgressCallbackType = iota
	// Process is skipping data because it's already up to date
	ProgressSkip ProgressCallbackType = iota
	// Process did not find the requested data, moving on
	ProgressNotFound ProgressCallbackType = iota
	// Non-fatal error
	ProgressError ProgressCallbackType = iota
)

// Collected callback data for a progress operation
type ProgressCallbackData struct {
	// What stage of the process this is for, preparing, transferring or skipping something
	Type ProgressCallbackType
	// Either a general message or an item name (e.g. file name in download stage)
	Desc string
	// If applicable, how many bytes transferred for this item
	ItemBytesDone int64
	// If applicable, how many bytes comprise this item
	ItemBytes int64
	// The number of bytes transferred for all items
	TotalBytesDone int64
	// The number of bytes needed to transfer all of this process
	TotalBytes int64
}

// Callback when progress is made during process
// return true to abort the (entire) process
type ProgressCallback func(data *ProgressCallbackData) (abort bool)

// Function to periodically (based on freq) report progress of a transfer process to the console
// callbackChan must be a channel of updates which is being populated with ProgressCallbackData
// from a goroutine at an unknown frequency. This function will then print updates every freq seconds
// of the updates received so far, collapsing duplicates (in the case of very frequent transfer updates)
// and filling in the blanks with an updated transfer rate in the case of no updates in the time.
func ReportProgressToConsole(callbackChan <-chan *ProgressCallbackData, op string, freq time.Duration) {
	// Update the console once every half second regardless of how many callbacks
	// (or zero callbacks, so we can reduce xfer rate)
	tickChan := time.Tick(freq)
	// samples of data transferred over the last 4 ticks (2s average)
	transferRate := NewTransferRateCalculator(4)

	var lastTotalBytesDone int64
	var lastTime = time.Now()
	var lastProgress *ProgressCallbackData
	complete := false
	lastConsoleLineLen := 0
	for _ = range tickChan {
		// We run this every 0.5s
		var finalDownloadProgress *ProgressCallbackData
		for stop := false; !stop && !complete; {
			select {
			case data := <-callbackChan:
				if data == nil {
					// channel was closed, we've finished
					stop = true
					complete = true
					break
				}

				// Some progress data is available
				// May get many of these and we only want to display the last one
				// unless it's general infoo or we're in verbose mode
				switch data.Type {
				case ProgressCalculate:
					finalDownloadProgress = nil
					LogConsole(data.Desc)
				case ProgressSkip:
					finalDownloadProgress = nil
					// Only print if verbose
					LogConsoleDebugf("Skipped: %v (Up to date)\n", data.Desc)
				case ProgressNotFound:
					finalDownloadProgress = nil
					LogConsolef("Not found: %v (Continuing)\n", data.Desc)
				case ProgressTransferBytes:
					// Print completion in verbose mode
					if data.ItemBytesDone == data.ItemBytes && GlobalOptions.Verbose {
						msg := fmt.Sprintf("%ved: %v 100%%", op, data.Desc)
						LogConsoleOverwrite(msg, lastConsoleLineLen)
						lastConsoleLineLen = len(msg)
						// Clear line on completion in verbose mode
						// Don't do this as \n in string above since we need to clear spaces after
						LogConsole("")
					} else {
						// Otherwise we only really want to display the last one
						finalDownloadProgress = data
					}
				}
			default:
				// No (more) progress data
				stop = true
			}
		}
		// Write progress data for this 0.5s if relevant
		// If either we have new progress data, or unfinished progress data from previous
		if finalDownloadProgress != nil || lastProgress != nil {
			var bytesPerSecond int64
			if finalDownloadProgress != nil && finalDownloadProgress.ItemBytes != 0 && finalDownloadProgress.TotalBytes != 0 {
				lastProgress = finalDownloadProgress
				bytesDoneThisTick := finalDownloadProgress.TotalBytesDone - lastTotalBytesDone
				lastTotalBytesDone = finalDownloadProgress.TotalBytesDone
				seconds := float32(time.Since(lastTime).Seconds())
				if seconds > 0 {
					bytesPerSecond = int64(float32(bytesDoneThisTick) / seconds)
				}
			} else {
				// Actually the default but lets be specific
				bytesPerSecond = 0
			}
			// Calculate transfer rate
			transferRate.AddSample(bytesPerSecond)
			avgRate := transferRate.Average()

			if lastProgress.ItemBytes != 0 || lastProgress.TotalBytes != 0 {
				buf := bytes.NewBufferString(fmt.Sprintf("%ving: ", op))
				if lastProgress.ItemBytes > 0 && GlobalOptions.Verbose {
					itemPercent := int((100 * lastProgress.ItemBytesDone) / lastProgress.ItemBytes)
					buf.WriteString(fmt.Sprintf("%v %d%%", lastProgress.Desc, itemPercent))
					if lastProgress.TotalBytes != 0 {
						buf.WriteString(" Overall: ")
					}
				}
				if lastProgress.TotalBytes > 0 {
					overallPercent := int((100 * lastProgress.TotalBytesDone) / lastProgress.TotalBytes)
					bytesRemaining := lastProgress.TotalBytes - lastProgress.TotalBytesDone
					secondsRemaining := bytesRemaining / avgRate
					timeRemaining := time.Duration(secondsRemaining) * time.Second

					buf.WriteString(fmt.Sprintf("%d%% (%v ETA %v)", overallPercent, FormatTransferRate(avgRate), timeRemaining))
				}
				msg := buf.String()
				LogConsoleOverwrite(msg, lastConsoleLineLen)
				lastConsoleLineLen = len(msg)
			}
		}

		if complete {
			break
		}

	}

}
<commit_msg>Fix trailing partial progress line in verbose mode<commit_after>package main

import (
	"bytes"
	"fmt"
	"time"
)

type ProgressCallbackType int

const (
	// Process is figuring out what to do
	ProgressCalculate ProgressCallbackType = iota
	// Process is transferring data
	ProgressTransferBytes ProgressCallbackType = iota
	// Process is skipping data because it's already up to date
	ProgressSkip ProgressCallbackType = iota
	// Process did not find the requested data, moving on
	ProgressNotFound ProgressCallbackType = iota
	// Non-fatal error
	ProgressError ProgressCallbackType = iota
)

// Collected callback data for a progress operation
type ProgressCallbackData struct {
	// What stage of the process this is for, preparing, transferring or skipping something
	Type ProgressCallbackType
	// Either a general message or an item name (e.g. file name in download stage)
	Desc string
	// If applicable, how many bytes transferred for this item
	ItemBytesDone int64
	// If applicable, how many bytes comprise this item
	ItemBytes int64
	// The number of bytes transferred for all items
	TotalBytesDone int64
	// The number of bytes needed to transfer all of this process
	TotalBytes int64
}

// Callback when progress is made during process
// return true to abort the (entire) process
type ProgressCallback func(data *ProgressCallbackData) (abort bool)

// Function to periodically (based on freq) report progress of a transfer process to the console
// callbackChan must be a channel of updates which is being populated with ProgressCallbackData
// from a goroutine at an unknown frequency. This function will then print updates every freq seconds
// of the updates received so far, collapsing duplicates (in the case of very frequent transfer updates)
// and filling in the blanks with an updated transfer rate in the case of no updates in the time.
func ReportProgressToConsole(callbackChan <-chan *ProgressCallbackData, op string, freq time.Duration) {
	// Update the console once every half second regardless of how many callbacks
	// (or zero callbacks, so we can reduce xfer rate)
	tickChan := time.Tick(freq)
	// samples of data transferred over the last 4 ticks (2s average)
	transferRate := NewTransferRateCalculator(4)

	var lastTotalBytesDone int64
	var lastTime = time.Now()
	var lastProgress *ProgressCallbackData
	complete := false
	lastConsoleLineLen := 0
	for _ = range tickChan {
		// We run this every 0.5s
		var finalDownloadProgress *ProgressCallbackData
		for stop := false; !stop && !complete; {
			select {
			case data := <-callbackChan:
				if data == nil {
					// channel was closed, we've finished
					stop = true
					complete = true
					break
				}

				// Some progress data is available
				// May get many of these and we only want to display the last one
				// unless it's general infoo or we're in verbose mode
				switch data.Type {
				case ProgressCalculate:
					finalDownloadProgress = nil
					LogConsole(data.Desc)
				case ProgressSkip:
					finalDownloadProgress = nil
					// Only print if verbose
					LogConsoleDebugf("Skipped: %v (Up to date)\n", data.Desc)
				case ProgressNotFound:
					finalDownloadProgress = nil
					LogConsolef("Not found: %v (Continuing)\n", data.Desc)
				case ProgressTransferBytes:
					// Print completion in verbose mode
					if data.ItemBytesDone == data.ItemBytes && GlobalOptions.Verbose {
						msg := fmt.Sprintf("%ved: %v 100%%", op, data.Desc)
						LogConsoleOverwrite(msg, lastConsoleLineLen)
						lastConsoleLineLen = len(msg)
						// Clear line on completion in verbose mode
						// Don't do this as \n in string above since we need to clear spaces after
						LogConsole("")
						finalDownloadProgress = nil
						lastProgress = nil
					} else {
						// Otherwise we only really want to display the last one
						finalDownloadProgress = data
					}
				}
			default:
				// No (more) progress data
				stop = true
			}
		}
		// Write progress data for this 0.5s if relevant
		// If either we have new progress data, or unfinished progress data from previous
		if finalDownloadProgress != nil || lastProgress != nil {
			var bytesPerSecond int64
			if finalDownloadProgress != nil && finalDownloadProgress.ItemBytes != 0 && finalDownloadProgress.TotalBytes != 0 {
				lastProgress = finalDownloadProgress
				bytesDoneThisTick := finalDownloadProgress.TotalBytesDone - lastTotalBytesDone
				lastTotalBytesDone = finalDownloadProgress.TotalBytesDone
				seconds := float32(time.Since(lastTime).Seconds())
				if seconds > 0 {
					bytesPerSecond = int64(float32(bytesDoneThisTick) / seconds)
				}
			} else {
				// Actually the default but lets be specific
				bytesPerSecond = 0
			}
			// Calculate transfer rate
			transferRate.AddSample(bytesPerSecond)
			avgRate := transferRate.Average()

			if lastProgress.ItemBytes != 0 || lastProgress.TotalBytes != 0 {
				buf := bytes.NewBufferString(fmt.Sprintf("%ving: ", op))
				if lastProgress.ItemBytes > 0 && GlobalOptions.Verbose {
					itemPercent := int((100 * lastProgress.ItemBytesDone) / lastProgress.ItemBytes)
					buf.WriteString(fmt.Sprintf("%v %d%%", lastProgress.Desc, itemPercent))
					if lastProgress.TotalBytes != 0 {
						buf.WriteString(" Overall: ")
					}
				}
				if lastProgress.TotalBytes > 0 {
					overallPercent := int((100 * lastProgress.TotalBytesDone) / lastProgress.TotalBytes)
					bytesRemaining := lastProgress.TotalBytes - lastProgress.TotalBytesDone
					secondsRemaining := bytesRemaining / avgRate
					timeRemaining := time.Duration(secondsRemaining) * time.Second

					buf.WriteString(fmt.Sprintf("%d%% (%v ETA %v)", overallPercent, FormatTransferRate(avgRate), timeRemaining))
				}
				msg := buf.String()
				LogConsoleOverwrite(msg, lastConsoleLineLen)
				lastConsoleLineLen = len(msg)
			}
		}

		if complete {
			break
		}

	}

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type config struct {
	AppName string
	Author  string
	Year    int
}

func main() {
	checkDependencies()
	conf := setup()

	if conf.AppName == "" {
		exitIf(fmt.Errorf("App name cannot be blank."))
	}

	checkForExistingDir(conf)

	exitIf(os.Mkdir(conf.AppName, 0777))
	exitIf(os.Chdir(conf.AppName))

	exitIf(os.Mkdir("vendor", 0777))

	initGit()

	writeStatic(".git/hooks/pre_commit", 0766)
	writeStatic("Makefile", 0666)
	writeStatic("dev.env", 0666)
	writeStatic(".gitignore", 0666)
	writeTemplate("LICENSE", conf, 0666)
	writeTemplate("tmux", conf, 0766)

	fmt.Println(conf.AppName + "'s workspace is complete!")
}

func exitIf(err error) {
	if err != nil {
		fmt.Println(err, "\nExiting...")
		os.Exit(1)
	}
}

func checkForExistingDir(c config) {
	if _, err := os.Stat(c.AppName); os.IsExist(err) {
		exitIf(fmt.Errorf("Directory \"%s\" already exists"))
	}
}

func checkDependencies() {
	out, _ := exec.Command("which", "git").Output()
	if len(out) == 0 {
		exitIf(fmt.Errorf("Git not found, is it installed?"))
	}
}

func setup() config {
	conf := config{}

	in := bufio.NewReader(os.Stdin)

	fmt.Println("Enter your app's name: ")
	line, err := in.ReadString('\n')
	exitIf(err)
	conf.AppName = strings.TrimSpace(line)

	in.Reset(os.Stdin)

	fmt.Println("Enter author's name for license: ")
	line, err = in.ReadString('\n')
	exitIf(err)
	conf.Author = strings.TrimSpace(line)

	conf.Year = time.Now().Year()

	return conf
}

func memoTemplate(name string) []byte {
	templ, err := Asset("templates/" + name)
	exitIf(err)
	return templ
}

func writeStatic(name string, mode os.FileMode) {
	exitIf(ioutil.WriteFile(name, memoTemplate(filepath.Base(name)), mode))
}

func writeTemplate(name string, c config, mode os.FileMode) {
	f, e := os.Create(name)
	exitIf(e)
	exitIf(f.Chmod(mode))
	defer f.Close()
	tmpl, err := template.New(name).Parse(string(memoTemplate(name)))
	exitIf(err)
	exitIf(tmpl.Execute(f, c))
}

func initGit() {
	_, err := exec.Command("git", "init").Output()
	exitIf(err)
}
<commit_msg>fix linter suggestions<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type config struct {
	AppName string
	Author  string
	Year    int
}

func main() {
	checkDependencies()
	conf := setup()

	if conf.AppName == "" {
		exitIf(errors.New("App name cannot be blank."))
	}

	checkForExistingDir(conf)

	exitIf(os.Mkdir(conf.AppName, 0700))
	exitIf(os.Chdir(conf.AppName))

	exitIf(os.Mkdir("vendor", 0700))

	initGit()

	writeStatic(".git/hooks/pre_commit", 0766)
	writeStatic("Makefile", 0666)
	writeStatic("dev.env", 0666)
	writeStatic(".gitignore", 0666)
	writeTemplate("LICENSE", conf, 0666)
	writeTemplate("tmux", conf, 0766)
	writeTemplate("modd.conf", conf, 0666)

	fmt.Println(conf.AppName + "'s workspace is complete!")
}

func exitIf(err error) {
	if err != nil {
		fmt.Println(err, "\nExiting...")
		os.Exit(1)
	}
}

func checkForExistingDir(c config) {
	if _, err := os.Stat(c.AppName); os.IsExist(err) {
		exitIf(errors.New("Directory \"%s\" already exists"))
	}
}

func checkDependencies() {
	out, err := exec.Command("which", "git").Output()
	if len(out) == 0 || err != nil {
		exitIf(fmt.Errorf("Git not found, is it installed?"))
	}
}

func setup() config {
	conf := config{}

	in := bufio.NewReader(os.Stdin)

	fmt.Println("Enter your app's name: ")
	line, err := in.ReadString('\n')
	exitIf(err)
	conf.AppName = strings.TrimSpace(line)

	in.Reset(os.Stdin)

	fmt.Println("Enter author's name for license: ")
	line, err = in.ReadString('\n')
	exitIf(err)
	conf.Author = strings.TrimSpace(line)

	conf.Year = time.Now().Year()

	return conf
}

func memoTemplate(name string) []byte {
	templ, err := Asset("templates/" + name)
	exitIf(err)
	return templ
}

func writeStatic(name string, mode os.FileMode) {
	exitIf(ioutil.WriteFile(name, memoTemplate(filepath.Base(name)), mode))
}

func writeTemplate(name string, c config, mode os.FileMode) {
	f, e := os.Create(name)
	exitIf(e)
	exitIf(f.Chmod(mode))
	defer f.Close()
	tmpl, err := template.New(name).Parse(string(memoTemplate(name)))
	exitIf(err)
	exitIf(tmpl.Execute(f, c))
}

func initGit() {
	_, err := exec.Command("git", "init").Output()
	exitIf(err)
}
<|endoftext|>


<commit_before>// Package adapt provides an algorithm for adaptive hierarchical interpolation
// with local refinements.
package adapt

import (
	"runtime"
)

// Grid is a sparse grid in [0, 1]^n.
type Grid interface {
	Compute(indices []uint64) []float64
	Refine(indices []uint64) []uint64
	Parent(index []uint64, i uint)
	Sibling(index []uint64, i uint)
}

// Basis is a functional basis in [0, 1]^n.
type Basis interface {
	Compute(index []uint64, point []float64) float64
	Integrate(index []uint64) float64
}

// Interpolator is an instance of the algorithm.
type Interpolator struct {
	grid   Grid
	basis  Basis
	config Config
}

// Progress contains information about the interpolation process.
type Progress struct {
	Iteration uint      // The iteration number.
	Level     uint      // The interpolation level.
	Accepted  uint      // The number of accepted nodes.
	Rejected  uint      // The number of rejected nodes.
	Current   uint      // The number of nodes of the current iteration.
	Integral  []float64 // The integral over the whole domain.
}

// New creates a new interpolator.
func New(grid Grid, basis Basis, config *Config) *Interpolator {
	interpolator := &Interpolator{
		grid:   grid,
		basis:  basis,
		config: *config,
	}

	config = &interpolator.config
	if config.Workers == 0 {
		config.Workers = uint(runtime.GOMAXPROCS(0))
	}
	if config.Rate == 0 {
		config.Rate = 1
	}

	return interpolator
}

// Compute constructs an interpolant for a function.
func (self *Interpolator) Compute(target Target) *Surrogate {
	config := &self.config

	ni, no := target.Dimensions()
	nw := config.Workers

	surrogate := newSurrogate(ni, no)
	tracker := newQueue(ni, config)
	history := newHash(ni)

	na, nr, nc := uint(0), uint(0), uint(1)

	indices := make([]uint64, nc*ni)
	nodes := self.grid.Compute(indices)

	integral, compensation := make([]float64, no), make([]float64, no)

	for k := uint(0); nc > 0; k++ {
		progress := Progress{
			Iteration: k,
			Level:     tracker.lnow,
			Accepted:  na,
			Rejected:  nr,
			Current:   nc,
			Integral:  integral,
		}

		target.Monitor(&progress)

		surpluses := subtract(
			invoke(target.Compute, nodes, ni, no, nw),
			approximate(self.basis, surrogate.Indices,
				surrogate.Surpluses, nodes, ni, no, nw),
		)

		scores := measure(self.basis, indices, ni)
		for i := uint(0); i < nc; i++ {
			location := Location{
				Node:    nodes[i*ni : (i+1)*ni],
				Surplus: surpluses[i*no : (i+1)*no],
				Volume:  scores[i],
			}
			scores[i] = target.Score(&location, &progress)
		}

		indices, surpluses, scores = compact(indices, surpluses, scores, ni, no, nc)

		nn := uint(len(scores))

		tracker.push(indices, scores)
		surrogate.push(indices, surpluses)
		surrogate.step(tracker.lnow, nn, nc-nn)

		cumulate(self.basis, indices, surpluses, ni, no, nn, integral, compensation)

		indices = history.unseen(self.grid.Refine(tracker.pull()))
		if config.Balance {
			indices = append(indices, balance(self.grid, history, indices)...)
		}

		nodes = self.grid.Compute(indices)

		nn = uint(len(indices)) / ni
		na, nr, nc = na+nn, nr+nc-nn, nn
	}

	return surrogate
}

// Evaluate computes the values of an interpolant at a set of points.
func (self *Interpolator) Evaluate(surrogate *Surrogate, points []float64) []float64 {
	return approximate(self.basis, surrogate.Indices, surrogate.Surpluses, points,
		surrogate.Inputs, surrogate.Outputs, self.config.Workers)
}

// Integrate computes the integral of an interpolant over [0, 1]^n.
func (self *Interpolator) Integrate(surrogate *Surrogate) []float64 {
	ni, no, nn := surrogate.Inputs, surrogate.Outputs, surrogate.Nodes

	integral, compensation := make([]float64, no), make([]float64, no)
	cumulate(self.basis, surrogate.Indices, surrogate.Surpluses,
		ni, no, nn, integral, compensation)

	return integral
}
<commit_msg>Fix the na and nr numbers<commit_after>// Package adapt provides an algorithm for adaptive hierarchical interpolation
// with local refinements.
package adapt

import (
	"runtime"
)

// Grid is a sparse grid in [0, 1]^n.
type Grid interface {
	Compute(indices []uint64) []float64
	Refine(indices []uint64) []uint64
	Parent(index []uint64, i uint)
	Sibling(index []uint64, i uint)
}

// Basis is a functional basis in [0, 1]^n.
type Basis interface {
	Compute(index []uint64, point []float64) float64
	Integrate(index []uint64) float64
}

// Interpolator is an instance of the algorithm.
type Interpolator struct {
	grid   Grid
	basis  Basis
	config Config
}

// Progress contains information about the interpolation process.
type Progress struct {
	Iteration uint      // The iteration number.
	Level     uint      // The interpolation level.
	Accepted  uint      // The number of accepted nodes.
	Rejected  uint      // The number of rejected nodes.
	Current   uint      // The number of nodes of the current iteration.
	Integral  []float64 // The integral over the whole domain.
}

// New creates a new interpolator.
func New(grid Grid, basis Basis, config *Config) *Interpolator {
	interpolator := &Interpolator{
		grid:   grid,
		basis:  basis,
		config: *config,
	}

	config = &interpolator.config
	if config.Workers == 0 {
		config.Workers = uint(runtime.GOMAXPROCS(0))
	}
	if config.Rate == 0 {
		config.Rate = 1
	}

	return interpolator
}

// Compute constructs an interpolant for a function.
func (self *Interpolator) Compute(target Target) *Surrogate {
	config := &self.config

	ni, no := target.Dimensions()
	nw := config.Workers

	surrogate := newSurrogate(ni, no)
	tracker := newQueue(ni, config)
	history := newHash(ni)

	na, nr, nc := uint(0), uint(0), uint(1)

	indices := make([]uint64, nc*ni)
	nodes := self.grid.Compute(indices)

	integral, compensation := make([]float64, no), make([]float64, no)

	for k := uint(0); nc > 0; k++ {
		progress := Progress{
			Iteration: k,
			Level:     tracker.lnow,
			Accepted:  na,
			Rejected:  nr,
			Current:   nc,
			Integral:  integral,
		}

		target.Monitor(&progress)

		surpluses := subtract(
			invoke(target.Compute, nodes, ni, no, nw),
			approximate(self.basis, surrogate.Indices,
				surrogate.Surpluses, nodes, ni, no, nw),
		)

		scores := measure(self.basis, indices, ni)
		for i := uint(0); i < nc; i++ {
			location := Location{
				Node:    nodes[i*ni : (i+1)*ni],
				Surplus: surpluses[i*no : (i+1)*no],
				Volume:  scores[i],
			}
			scores[i] = target.Score(&location, &progress)
		}

		indices, surpluses, scores = compact(indices, surpluses, scores, ni, no, nc)

		nn := uint(len(scores))
		na, nr = na+nn, nr+nc-nn

		tracker.push(indices, scores)
		surrogate.push(indices, surpluses)
		surrogate.step(tracker.lnow, nn, nc-nn)

		cumulate(self.basis, indices, surpluses, ni, no, nn, integral, compensation)

		indices = history.unseen(self.grid.Refine(tracker.pull()))
		if config.Balance {
			indices = append(indices, balance(self.grid, history, indices)...)
		}

		nodes = self.grid.Compute(indices)

		nc = uint(len(indices)) / ni
	}

	return surrogate
}

// Evaluate computes the values of an interpolant at a set of points.
func (self *Interpolator) Evaluate(surrogate *Surrogate, points []float64) []float64 {
	return approximate(self.basis, surrogate.Indices, surrogate.Surpluses, points,
		surrogate.Inputs, surrogate.Outputs, self.config.Workers)
}

// Integrate computes the integral of an interpolant over [0, 1]^n.
func (self *Interpolator) Integrate(surrogate *Surrogate) []float64 {
	ni, no, nn := surrogate.Inputs, surrogate.Outputs, surrogate.Nodes

	integral, compensation := make([]float64, no), make([]float64, no)
	cumulate(self.basis, surrogate.Indices, surrogate.Surpluses,
		ni, no, nn, integral, compensation)

	return integral
}
<|endoftext|>