<commit_before>//
package mesosproxy

import (
	"encoding/json"
	"errors"
	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/proxy"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var errInvalidPort = errors.New("Invalid port specified.")

type mesosUpstream struct {
	from        string
	mesosMaster string
	framework   string
	taskName    string
	hosts       *atomic.Value
	Policy      proxy.Policy

	FailTimeout time.Duration
	MaxFails    int32
	HealthCheck struct {
		Path     string
		Interval time.Duration
	}

	SyncInterval time.Duration
	lastSync     time.Time
	syncing      int32
	syncWg       sync.WaitGroup

	Scheme string
	Port   int

	proxyHeaders http.Header
}

type mesosState struct {
	GitSha     string `json:"git_sha"`
	GitTag     string `json:"git_tag"`
	Leader     string `json:"leader"`
	Frameworks []struct {
		Id     string `json:"id"`
		Name   string `json:"name"`
		Active bool   `json:"active"`
		Tasks  []struct {
			Name      string `json:"name"`
			State     string `json:"state"`
			SlaveId   string `json:"slave_id"`
			Resources struct {
				Cpus  float64 `json:"cpus"`
				Disk  float64 `json:"disk"`
				Mem   float64 `json:"mem"`
				Ports string  `json:"ports"`
			} `json:"resources"`
		} `json:"tasks"`
	} `json:"frameworks"`
	Slaves []struct {
		Hostname string `json:"hostname"`
		Id       string `json:"id"`
	} `json:"slaves"`
}

// New creates a new instance of proxy middleware.
func Proxy(c *setup.Controller) (middleware.Middleware, error) {
	if upstreams, err := newMesosUpstreams(c); err == nil {
		return func(next middleware.Handler) middleware.Handler {
			return proxy.Proxy{Next: next, Upstreams: upstreams}
		}, nil
	} else {
		return nil, err
	}
}

func newMesosUpstreams(c *setup.Controller) ([]proxy.Upstream, error) {
	var upstreams []proxy.Upstream

	for c.Next() {
		upstream := &mesosUpstream{
			from:        "",
			hosts:       new(atomic.Value),
			Policy:      &proxy.Random{},
			FailTimeout: 10 * time.Second,
			MaxFails:    1,

			SyncInterval: 10 * time.Second,
			Scheme:       "http",
		}
		upstream.hosts.Store(proxy.HostPool([]*proxy.UpstreamHost{}))
		var proxyHeaders http.Header
		var port string
		if !c.Args(&upstream.from, &upstream.mesosMaster, &upstream.framework, &upstream.taskName, &port) {
			return upstreams, c.ArgErr()
		}
		if p, err := strconv.Atoi(port); err == nil {
			if p == 0 {
				return upstreams, errInvalidPort
			} else {
				upstream.Port = p
			}
		} else {
			return upstreams, err
		}

		for c.NextBlock() {
			switch c.Val() {
			case "policy":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				switch c.Val() {
				case "random":
					upstream.Policy = &proxy.Random{}
				case "round_robin":
					upstream.Policy = &proxy.RoundRobin{}
				case "least_conn":
					upstream.Policy = &proxy.LeastConn{}
				default:
					return upstreams, c.ArgErr()
				}
			case "fail_timeout":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				if dur, err := time.ParseDuration(c.Val()); err == nil {
					upstream.FailTimeout = dur
				} else {
					return upstreams, err
				}
			case "max_fails":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				if n, err := strconv.Atoi(c.Val()); err == nil {
					upstream.MaxFails = int32(n)
				} else {
					return upstreams, err
				}
			case "health_check":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				upstream.HealthCheck.Path = c.Val()
				upstream.HealthCheck.Interval = 30 * time.Second
				if c.NextArg() {
					if dur, err := time.ParseDuration(c.Val()); err == nil {
						upstream.HealthCheck.Interval = dur
					} else {
						return upstreams, err
					}
				}
			case "proxy_header":
				var header, value string
				if !c.Args(&header, &value) {
					return upstreams, c.ArgErr()
				}
				if proxyHeaders == nil {
					proxyHeaders = make(map[string][]string)
				}
				proxyHeaders.Add(header, value)
			case "sync_interval":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				if dur, err := time.ParseDuration(c.Val()); err == nil {
					upstream.SyncInterval = dur
				} else {
					return upstreams, err
				}
			case "scheme":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				upstream.Scheme = c.Val()
			}
		}
		upstream.proxyHeaders = proxyHeaders

		go upstream.syncWorker(nil)
		if upstream.HealthCheck.Path != "" {
			go upstream.healthCheckWorker(nil)
		}

		upstreams = append(upstreams, upstream)
	}
	return upstreams, nil
}

func (u *mesosUpstream) Hosts() proxy.HostPool {
	return u.hosts.Load().(proxy.HostPool)
}

func (u *mesosUpstream) healthCheck(hosts proxy.HostPool) {
	for _, host := range hosts {
		hostUrl := host.Name + u.HealthCheck.Path
		if r, err := http.Get(hostUrl); err == nil {
			io.Copy(ioutil.Discard, r.Body)
			r.Body.Close()
			host.Unhealthy = r.StatusCode < 200 || r.StatusCode >= 400
		} else {
			host.Unhealthy = true
		}
	}
}

func (u *mesosUpstream) healthCheckWorker(stop chan struct{}) {
	ticker := time.NewTicker(u.HealthCheck.Interval)
	u.healthCheck(u.Hosts())
	for {
		select {
		case <-ticker.C:
			u.healthCheck(u.Hosts())
		case <-stop:
			// TODO: the library should provide a stop channel and global
			// waitgroup to allow goroutines started by plugins a chance
			// to clean themselves up.
		}
	}
}

func (u *mesosUpstream) syncWorker(stop chan struct{}) {
	ticker := time.NewTicker(u.SyncInterval)
	u.sync()
	for {
		select {
		case <-ticker.C:
			u.sync()
		case <-stop:
			// TODO: the library should provide a stop channel and global
			// waitgroup to allow goroutines started by plugins a chance
			// to clean themselves up.
		}
	}
}

func (u *mesosUpstream) sync() {
	var syncing int32
	syncing = atomic.AddInt32(&u.syncing, 1)
	if syncing > 1 {
		atomic.AddInt32(&u.syncing, -1)
		u.syncWg.Wait()
		return
	}
	u.syncWg.Add(1)
	defer func() {
		u.syncWg.Done()
		atomic.AddInt32(&u.syncing, -1)
		u.lastSync = time.Now()
	}()
	var state mesosState
	// TODO: Use upstream.mesosMaster
	if resp, err := http.Get("http://mesos:5050/state.json"); err == nil {
		defer resp.Body.Close()
		if err := json.NewDecoder(resp.Body).Decode(&state); err != nil {
			return
		}
	} else {
		return
	}

	hosts := make(proxy.HostPool, 0, 4)
	for _, framework := range state.Frameworks {
		if framework.Name == u.framework {
			for _, task := range framework.Tasks {
				if task.Name == u.taskName && task.State == "TASK_RUNNING" {
					host := &proxy.UpstreamHost{
						Name:         task.SlaveId,
						Conns:        0,
						Fails:        0,
						FailTimeout:  u.FailTimeout,
						Unhealthy:    false,
						ExtraHeaders: u.proxyHeaders,
						CheckDown: func(upstream *mesosUpstream) proxy.UpstreamHostDownFunc {
							return func(uh *proxy.UpstreamHost) bool {
								if uh.Unhealthy {
									return true
								}
								if uh.Fails >= upstream.MaxFails &&
									upstream.MaxFails != 0 {
									return true
								}
								return false
							}
						}(u),
					}
					if u.Port > 0 {
						host.Name = host.Name + ":" + strconv.Itoa(u.Port)
					} else if u.Port < 0 {
						idx := (u.Port * -1) - 1
						if len(task.Resources.Ports) > 2 {
							portResource := task.Resources.Ports[1 : len(task.Resources.Ports)-1]
							ports := strings.Split(portResource, " ")
							if idx < len(ports) {
								selectedPort := ports[idx]
								if strings.Index(selectedPort, "-") != -1 {
									selectedPort = strings.Split(selectedPort, "-")[0]
									host.Name = host.Name + ":" + selectedPort
								}
							} else {
								continue
							}
						} else {
							continue
						}
					}
					hosts = append(hosts, host)
				}
			}
			break
		}
	}

	for _, host := range hosts {
		id, port := func() (string, string) {
			k := strings.Split(host.Name, ":")
			return k[0], k[1]
		}()
		for _, slave := range state.Slaves {
			if id == slave.Id {
				host.Name = u.Scheme + "://" + slave.Hostname + ":" + port
				break
			}
		}
	}
	oldPool := u.Hosts()
	isSame := len(oldPool) == len(hosts)
	for i, host := range hosts {
		found := false
		for _, oldHost := range oldPool {
			if oldHost.Name == host.Name {
				hosts[i] = oldHost
				found = true
				break
			}
		}
		if !found {
			isSame = false
		}
	}

	for _, host := range hosts {
		if host.ReverseProxy == nil {
			if baseUrl, err := url.Parse(host.Name); err == nil {
				host.ReverseProxy = proxy.NewSingleHostReverseProxy(baseUrl)
			} else {
				return
			}
		}
	}

	if !isSame {
		if u.HealthCheck.Path != "" {
			u.healthCheck(hosts)
		}
		u.hosts.Store(hosts)
	}
}

func (u *mesosUpstream) From() string {
	return u.from
}

func (u *mesosUpstream) Select() *proxy.UpstreamHost {
	pool := u.Hosts()
	if len(pool) == 0 {
		u.sync()
		pool = u.Hosts()
	}
	if len(pool) == 1 {
		if pool[0].Down() {
			return nil
		}
		return pool[0]
	}
	allDown := true
	for _, host := range pool {
		if !host.Down() {
			allDown = false
			break
		}
	}
	if allDown {
		return nil
	}

	if u.Policy == nil {
		return (&proxy.Random{}).Select(pool)
	} else {
		return u.Policy.Select(pool)
	}
}
<commit_msg>Support custom defined mesos master from zookeeper<commit_after>package mesosproxy

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/mesos/mesos-go/mesosproto"
	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/proxy"
	"github.com/samuel/go-zookeeper/zk"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var errInvalidPort = errors.New("Invalid port specified.")

type mesosUpstream struct {
	from        string
	mesosMaster string
	framework   string
	taskName    string
	hosts       *atomic.Value
	Policy      proxy.Policy

	FailTimeout time.Duration
	MaxFails    int32
	HealthCheck struct {
		Path     string
		Interval time.Duration
	}

	SyncInterval time.Duration
	lastSync     time.Time
	syncing      int32
	syncWg       sync.WaitGroup

	Scheme string
	Port   int

	proxyHeaders http.Header
}

type mesosState struct {
	GitSha     string `json:"git_sha"`
	GitTag     string `json:"git_tag"`
	Leader     string `json:"leader"`
	Frameworks []struct {
		Id     string `json:"id"`
		Name   string `json:"name"`
		Active bool   `json:"active"`
		Tasks  []struct {
			Name      string `json:"name"`
			State     string `json:"state"`
			SlaveId   string `json:"slave_id"`
			Resources struct {
				Cpus  float64 `json:"cpus"`
				Disk  float64 `json:"disk"`
				Mem   float64 `json:"mem"`
				Ports string  `json:"ports"`
			} `json:"resources"`
		} `json:"tasks"`
	} `json:"frameworks"`
	Slaves []struct {
		Hostname string `json:"hostname"`
		Id       string `json:"id"`
	} `json:"slaves"`
}

// New creates a new instance of proxy middleware.
func Proxy(c *setup.Controller) (middleware.Middleware, error) {
	if upstreams, err := newMesosUpstreams(c); err == nil {
		return func(next middleware.Handler) middleware.Handler {
			return proxy.Proxy{Next: next, Upstreams: upstreams}
		}, nil
	} else {
		return nil, err
	}
}

func newMesosUpstreams(c *setup.Controller) ([]proxy.Upstream, error) {
	var upstreams []proxy.Upstream

	for c.Next() {
		upstream := &mesosUpstream{
			from:        "",
			hosts:       new(atomic.Value),
			Policy:      &proxy.Random{},
			FailTimeout: 10 * time.Second,
			MaxFails:    1,

			SyncInterval: 10 * time.Second,
			Scheme:       "http",
		}
		upstream.hosts.Store(proxy.HostPool([]*proxy.UpstreamHost{}))
		var proxyHeaders http.Header
		var port string
		if !c.Args(&upstream.from, &upstream.mesosMaster, &upstream.framework, &upstream.taskName, &port) {
			return upstreams, c.ArgErr()
		}
		if p, err := strconv.Atoi(port); err == nil {
			if p == 0 {
				return upstreams, errInvalidPort
			} else {
				upstream.Port = p
			}
		} else {
			return upstreams, err
		}

		for c.NextBlock() {
			switch c.Val() {
			case "policy":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				switch c.Val() {
				case "random":
					upstream.Policy = &proxy.Random{}
				case "round_robin":
					upstream.Policy = &proxy.RoundRobin{}
				case "least_conn":
					upstream.Policy = &proxy.LeastConn{}
				default:
					return upstreams, c.ArgErr()
				}
			case "fail_timeout":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				if dur, err := time.ParseDuration(c.Val()); err == nil {
					upstream.FailTimeout = dur
				} else {
					return upstreams, err
				}
			case "max_fails":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				if n, err := strconv.Atoi(c.Val()); err == nil {
					upstream.MaxFails = int32(n)
				} else {
					return upstreams, err
				}
			case "health_check":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				upstream.HealthCheck.Path = c.Val()
				upstream.HealthCheck.Interval = 30 * time.Second
				if c.NextArg() {
					if dur, err := time.ParseDuration(c.Val()); err == nil {
						upstream.HealthCheck.Interval = dur
					} else {
						return upstreams, err
					}
				}
			case "proxy_header":
				var header, value string
				if !c.Args(&header, &value) {
					return upstreams, c.ArgErr()
				}
				if proxyHeaders == nil {
					proxyHeaders = make(map[string][]string)
				}
				proxyHeaders.Add(header, value)
			case "sync_interval":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				if dur, err := time.ParseDuration(c.Val()); err == nil {
					upstream.SyncInterval = dur
				} else {
					return upstreams, err
				}
			case "scheme":
				if !c.NextArg() {
					return upstreams, c.ArgErr()
				}
				upstream.Scheme = c.Val()
			}
		}
		upstream.proxyHeaders = proxyHeaders

		go upstream.syncWorker(nil)
		if upstream.HealthCheck.Path != "" {
			go upstream.healthCheckWorker(nil)
		}

		upstreams = append(upstreams, upstream)
	}
	return upstreams, nil
}

func (u *mesosUpstream) Hosts() proxy.HostPool {
	return u.hosts.Load().(proxy.HostPool)
}

func (u *mesosUpstream) healthCheck(hosts proxy.HostPool) {
	for _, host := range hosts {
		hostUrl := host.Name + u.HealthCheck.Path
		if r, err := http.Get(hostUrl); err == nil {
			io.Copy(ioutil.Discard, r.Body)
			r.Body.Close()
			host.Unhealthy = r.StatusCode < 200 || r.StatusCode >= 400
		} else {
			host.Unhealthy = true
		}
	}
}

func (u *mesosUpstream) healthCheckWorker(stop chan struct{}) {
	ticker := time.NewTicker(u.HealthCheck.Interval)
	u.healthCheck(u.Hosts())
	for {
		select {
		case <-ticker.C:
			u.healthCheck(u.Hosts())
		case <-stop:
			// TODO: the library should provide a stop channel and global
			// waitgroup to allow goroutines started by plugins a chance
			// to clean themselves up.
		}
	}
}

func (u *mesosUpstream) syncWorker(stop chan struct{}) {
	ticker := time.NewTicker(u.SyncInterval)
	u.sync()
	for {
		select {
		case <-ticker.C:
			u.sync()
		case <-stop:
			// TODO: the library should provide a stop channel and global
			// waitgroup to allow goroutines started by plugins a chance
			// to clean themselves up.
		}
	}
}

func (u *mesosUpstream) sync() {
	var syncing int32
	syncing = atomic.AddInt32(&u.syncing, 1)
	if syncing > 1 {
		atomic.AddInt32(&u.syncing, -1)
		u.syncWg.Wait()
		return
	}
	u.syncWg.Add(1)
	defer func() {
		u.syncWg.Done()
		atomic.AddInt32(&u.syncing, -1)
		u.lastSync = time.Now()
	}()
	var state mesosState
	// TODO: Use upstream.mesosMaster

	var masterHosts []string

	if path, err := url.Parse(u.mesosMaster); err == nil {
		switch path.Scheme {
		case "zk":
			if path.Path == "" || path.Path == "/" {
				log.Printf("[ERROR] no path specified for mesos zk lookup \"%s\"", u.mesosMaster)
				return
			}
			zookeeperPath := path.Path
			if zookeeperPath[0] != '/' {
				zookeeperPath = "/" + zookeeperPath
			}
			if zoo, _, err := zk.Connect(strings.Split(path.Host, ","), 10*time.Second); err == nil {
				defer zoo.Close()
				if children, _, err := zoo.Children(zookeeperPath); err == nil {
					sort.Strings(children)
					for _, child := range children {
						if strings.HasPrefix(child, "info_") {
							if data, _, err := zoo.Get(zookeeperPath + "/" + child); err == nil {
								masterInfo := new(mesosproto.MasterInfo)
								if err := masterInfo.Unmarshal(data); err == nil {
									masterHosts = []string{fmt.Sprintf("%s:%d", masterInfo.GetHostname(), masterInfo.GetPort())}
								} else {
									log.Printf("[ERROR] parsing mesos master from zookeeper. \"%s\"", err.Error())
									return
								}
							} else {
								log.Printf("[ERROR] getting mesos master from zookeeper. \"%s\"", err.Error())
								return
							}
						}
					}
				} else {
					log.Printf("[ERROR] getting mesos masters from zookeeper. \"%s\"", err.Error())
					return
				}
			}
		case "http", "https":
			masterHosts = strings.Split(path.Host, ",")
		default:
			log.Printf("[ERROR] unknown scheme in parsing mesos master url \"%s\"", u.mesosMaster)
			return
		}
	} else {
		masterHosts = strings.Split(u.mesosMaster, ",")
	}

	if len(masterHosts) == 0 {
		log.Printf("[ERROR] No reachable masters.")
		return
	}
	var masterErr error
	for _, host := range masterHosts {
		if resp, err := http.Get("http://" + host + "/state.json"); err == nil {
			defer resp.Body.Close()
			if err := json.NewDecoder(resp.Body).Decode(&state); err == nil {
				masterErr = nil
				break
			} else {
				masterErr = err
			}
		} else {
			masterErr = err
		}
	}
	if masterErr != nil {
		log.Printf("[ERROR] Failed to reach masters. \"%s\"", masterErr.Error())
		return
	}

	hosts := make(proxy.HostPool, 0, 4)
	for _, framework := range state.Frameworks {
		if framework.Name == u.framework {
			for _, task := range framework.Tasks {
				if task.Name == u.taskName && task.State == "TASK_RUNNING" {
					host := &proxy.UpstreamHost{
						Name:         task.SlaveId,
						Conns:        0,
						Fails:        0,
						FailTimeout:  u.FailTimeout,
						Unhealthy:    false,
						ExtraHeaders: u.proxyHeaders,
						CheckDown: func(upstream *mesosUpstream) proxy.UpstreamHostDownFunc {
							return func(uh *proxy.UpstreamHost) bool {
								if uh.Unhealthy {
									return true
								}
								if uh.Fails >= upstream.MaxFails &&
									upstream.MaxFails != 0 {
									return true
								}
								return false
							}
						}(u),
					}
					if u.Port > 0 {
						host.Name = host.Name + ":" + strconv.Itoa(u.Port)
					} else if u.Port < 0 {
						idx := (u.Port * -1) - 1
						if len(task.Resources.Ports) > 2 {
							portResource := task.Resources.Ports[1 : len(task.Resources.Ports)-1]
							ports := strings.Split(portResource, " ")
							if idx < len(ports) {
								selectedPort := ports[idx]
								if strings.Index(selectedPort, "-") != -1 {
									selectedPort = strings.Split(selectedPort, "-")[0]
									host.Name = host.Name + ":" + selectedPort
								}
							} else {
								continue
							}
						} else {
							continue
						}
					}
					hosts = append(hosts, host)
				}
			}
			break
		}
	}

	for _, host := range hosts {
		id, port := func() (string, string) {
			k := strings.Split(host.Name, ":")
			return k[0], k[1]
		}()
		for _, slave := range state.Slaves {
			if id == slave.Id {
				host.Name = u.Scheme + "://" + slave.Hostname + ":" + port
				break
			}
		}
	}
	oldPool := u.Hosts()
	isSame := len(oldPool) == len(hosts)
	for i, host := range hosts {
		found := false
		for _, oldHost := range oldPool {
			if oldHost.Name == host.Name {
				hosts[i] = oldHost
				found = true
				break
			}
		}
		if !found {
			isSame = false
		}
	}

	for _, host := range hosts {
		if host.ReverseProxy == nil {
			if baseUrl, err := url.Parse(host.Name); err == nil {
				host.ReverseProxy = proxy.NewSingleHostReverseProxy(baseUrl)
			} else {
				return
			}
		}
	}

	if !isSame {
		if u.HealthCheck.Path != "" {
			u.healthCheck(hosts)
		}
		u.hosts.Store(hosts)
	}
}

func (u *mesosUpstream) From() string {
	return u.from
}

func (u *mesosUpstream) Select() *proxy.UpstreamHost {
	pool := u.Hosts()
	if len(pool) == 0 {
		u.sync()
		pool = u.Hosts()
	}
	if len(pool) == 1 {
		if pool[0].Down() {
			return nil
		}
		return pool[0]
	}
	allDown := true
	for _, host := range pool {
		if !host.Down() {
			allDown = false
			break
		}
	}
	if allDown {
		return nil
	}

	if u.Policy == nil {
		return (&proxy.Random{}).Select(pool)
	} else {
		return u.Policy.Select(pool)
	}
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

// ForbiddenImports are the packages from the stdlib that should not be used in
// our code.
var ForbiddenImports = map[string]bool{
	"errors": true,
}

var runCrossCompile = flag.Bool("cross-compile", true, "run cross compilation tests")

func init() {
	flag.Parse()
}

// CIEnvironment is implemented by environments where tests can be run.
type CIEnvironment interface {
	Prepare() error
	RunTests() error
	Teardown() error
}

// TravisEnvironment is the environment in which Travis tests run.
type TravisEnvironment struct {
	goxOSArch []string
	env       map[string]string
}

func (env *TravisEnvironment) getMinio() error {
	tempfile, err := os.Create(filepath.Join(os.Getenv("GOPATH"), "bin", "minio"))
	if err != nil {
		return fmt.Errorf("create tempfile for minio download failed: %v\n", err)
	}

	url := fmt.Sprintf("https://dl.minio.io/server/minio/release/%s-%s/minio",
		runtime.GOOS, runtime.GOARCH)
	msg("downloading %v\n", url)
	res, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("error downloading minio server: %v\n", err)
	}

	_, err = io.Copy(tempfile, res.Body)
	if err != nil {
		return fmt.Errorf("error saving minio server to file: %v\n", err)
	}

	err = res.Body.Close()
	if err != nil {
		return fmt.Errorf("error closing HTTP download: %v\n", err)
	}

	err = tempfile.Close()
	if err != nil {
		msg("closing tempfile failed: %v\n", err)
		return fmt.Errorf("error closing minio server file: %v\n", err)
	}

	err = os.Chmod(tempfile.Name(), 0755)
	if err != nil {
		return fmt.Errorf("chmod(minio-server) failed: %v", err)
	}

	msg("downloaded minio server to %v\n", tempfile.Name())
	return nil
}

// Prepare installs dependencies and starts services in order to run the tests.
func (env *TravisEnvironment) Prepare() error {
	env.env = make(map[string]string)

	msg("preparing environment for Travis CI\n")

	pkgs := []string{
		"golang.org/x/tools/cmd/cover",
		"github.com/pierrre/gotestcover",
		"github.com/NebulousLabs/glyphcheck",
		"github.com/restic/rest-server/cmd/rest-server",
	}

	for _, pkg := range pkgs {
		err := run("go", "get", pkg)
		if err != nil {
			return err
		}
	}

	if err := env.getMinio(); err != nil {
		return err
	}

	if *runCrossCompile {
		// only test cross compilation on linux with Travis
		if err := run("go", "get", "github.com/mitchellh/gox"); err != nil {
			return err
		}
		if runtime.GOOS == "linux" {
			env.goxOSArch = []string{
				"linux/386", "linux/amd64",
				"windows/386", "windows/amd64",
				"darwin/386", "darwin/amd64",
				"freebsd/386", "freebsd/amd64",
				"openbsd/386", "openbsd/amd64",
				"linux/arm", "freebsd/arm",
			}
		} else {
			env.goxOSArch = []string{runtime.GOOS + "/" + runtime.GOARCH}
		}

		msg("gox: OS/ARCH %v\n", env.goxOSArch)
	}

	return nil
}

// Teardown stops backend services and cleans the environment again.
func (env *TravisEnvironment) Teardown() error {
	msg("run travis teardown\n")
	return nil
}

// RunTests starts the tests for Travis.
func (env *TravisEnvironment) RunTests() error {
	// do not run fuse tests on darwin
	if runtime.GOOS == "darwin" {
		msg("skip fuse integration tests on %v\n", runtime.GOOS)
		_ = os.Setenv("RESTIC_TEST_FUSE", "0")
	}

	env.env["GOPATH"] = os.Getenv("GOPATH")

	// ensure that the following tests cannot be silently skipped on Travis
	ensureTests := []string{
		"restic/backend/rest.TestBackendREST",
		"restic/backend/sftp.TestBackendSFTP",
		"restic/backend/s3.TestBackendMinio",
	}

	// if the test s3 repository is available, make sure that the test is not skipped
	if os.Getenv("RESTIC_TEST_S3_REPOSITORY") != "" {
		ensureTests = append(ensureTests, "restic/backend/s3.TestBackendS3")
	} else {
		msg("S3 repository not available\n")
	}

	// if the test swift service is available, make sure that the test is not skipped
	if os.Getenv("RESTIC_TEST_SWIFT") != "" {
		ensureTests = append(ensureTests, "restic/backend/swift.TestBackendSwift")
	} else {
		msg("Swift service not available\n")
	}

	// if the test b2 repository is available, make sure that the test is not skipped
	if os.Getenv("RESTIC_TEST_B2_REPOSITORY") != "" {
		ensureTests = append(ensureTests, "restic/backend/b2.TestBackendB2")
	} else {
		msg("B2 repository not available\n")
	}

	env.env["RESTIC_TEST_DISALLOW_SKIP"] = strings.Join(ensureTests, ",")

	if *runCrossCompile {
		// compile for all target architectures with tags
		for _, tags := range []string{"release", "debug"} {
			err := runWithEnv(env.env, "gox", "-verbose",
				"-osarch", strings.Join(env.goxOSArch, " "),
				"-tags", tags,
				"-output", "/tmp/{{.Dir}}_{{.OS}}_{{.Arch}}",
				"./cmd/restic")
			if err != nil {
				return err
			}
		}
	}

	// run the build script
	if err := run("go", "run", "build.go"); err != nil {
		return err
	}

	// run the tests and gather coverage information
	err := runWithEnv(env.env, "gotestcover", "-coverprofile", "all.cov", "github.com/restic/restic/cmd/...", "github.com/restic/restic/internal/...")
	if err != nil {
		return err
	}

	if err = runGofmt(); err != nil {
		return err
	}

	if err = runGlyphcheck(); err != nil {
		return err
	}

	deps, err := findImports()
	if err != nil {
		return err
	}

	foundForbiddenImports := false
	for name, imports := range deps {
		for _, pkg := range imports {
			if _, ok := ForbiddenImports[pkg]; ok {
				fmt.Fprintf(os.Stderr, "========== package %v imports forbidden package %v\n", name, pkg)
				foundForbiddenImports = true
			}
		}
	}

	if foundForbiddenImports {
		return errors.New("CI: forbidden imports found")
	}

	return nil
}

// AppveyorEnvironment is the environment on Windows.
type AppveyorEnvironment struct{}

// Prepare installs dependencies and starts services in order to run the tests.
func (env *AppveyorEnvironment) Prepare() error {
	msg("preparing environment for Appveyor CI\n")
	return nil
}

// RunTests start the tests.
func (env *AppveyorEnvironment) RunTests() error {
	return run("go", "run", "build.go", "-v", "-T")
}

// Teardown is a noop.
func (env *AppveyorEnvironment) Teardown() error {
	return nil
}

// findGoFiles returns a list of go source code file names below dir.
func findGoFiles(dir string) (list []string, err error) {
	err = filepath.Walk(dir, func(name string, fi os.FileInfo, err error) error {
		relpath, err := filepath.Rel(dir, name)
		if err != nil {
			return err
		}

		if relpath == "vendor" || relpath == "pkg" {
			return filepath.SkipDir
		}

		if filepath.Ext(relpath) == ".go" {
			list = append(list, relpath)
		}

		return err
	})

	return list, err
}

func msg(format string, args ...interface{}) {
	fmt.Printf("CI: "+format, args...)
}

func updateEnv(env []string, override map[string]string) []string {
	var newEnv []string
	for _, s := range env {
		d := strings.SplitN(s, "=", 2)
		key := d[0]

		if _, ok := override[key]; ok {
			continue
		}

		newEnv = append(newEnv, s)
	}

	for k, v := range override {
		newEnv = append(newEnv, k+"="+v)
	}

	return newEnv
}

func findImports() (map[string][]string, error) {
	res := make(map[string][]string)

	cwd, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("Getwd() returned error: %v", err)
	}

	gopath := cwd + ":" + filepath.Join(cwd, "vendor")

	cmd := exec.Command("go", "list", "-f", `{{.ImportPath}} {{join .Imports " "}}`, "./src/...")
	cmd.Env = updateEnv(os.Environ(), map[string]string{"GOPATH": gopath})
	cmd.Stderr = os.Stderr

	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	sc := bufio.NewScanner(bytes.NewReader(output))
	for sc.Scan() {
		wordScanner := bufio.NewScanner(strings.NewReader(sc.Text()))
		wordScanner.Split(bufio.ScanWords)

		if !wordScanner.Scan() {
			return nil, fmt.Errorf("package name not found in line: %s", output)
		}
		name := wordScanner.Text()
		var deps []string

		for wordScanner.Scan() {
			deps = append(deps, wordScanner.Text())
		}

		res[name] = deps
	}

	return res, nil
}

func runGofmt() error {
	dir, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Getwd(): %v\n", err)
	}

	files, err := findGoFiles(dir)
	if err != nil {
		return fmt.Errorf("error finding Go files: %v\n", err)
	}

	msg("runGofmt() with %d files\n", len(files))
	args := append([]string{"-l"}, files...)
	cmd := exec.Command("gofmt", args...)
	cmd.Stderr = os.Stderr

	buf, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("error running gofmt: %v\noutput: %s\n", err, buf)
	}

	if len(buf) > 0 {
		return fmt.Errorf("not formatted with `gofmt`:\n%s\n", buf)
	}

	return nil
}

func runGlyphcheck() error {
	cmd := exec.Command("glyphcheck", "./...")
	cmd.Stderr = os.Stderr

	buf, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("error running glyphcheck: %v\noutput: %s\n", err, buf)
	}

	return nil
}

func run(command string, args ...string) error {
	msg("run %v %v\n", command, strings.Join(args, " "))
	return runWithEnv(nil, command, args...)
}

// runWithEnv calls a command with the current environment, except the entries
// of the env map are set additionally.
func runWithEnv(env map[string]string, command string, args ...string) error {
	msg("runWithEnv %v %v\n", command, strings.Join(args, " "))
	cmd := exec.Command(command, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if env != nil {
		cmd.Env = updateEnv(os.Environ(), env)
	}
	err := cmd.Run()

	if err != nil {
		return fmt.Errorf("error running %v %v: %v",
			command, strings.Join(args, " "), err)
	}
	return nil
}

func isTravis() bool {
	return os.Getenv("TRAVIS_BUILD_DIR") != ""
}

func isAppveyor() bool {
	return runtime.GOOS == "windows"
}

func main() {
	var env CIEnvironment

	switch {
	case isTravis():
		env = &TravisEnvironment{}
	case isAppveyor():
		env = &AppveyorEnvironment{}
	default:
		fmt.Fprintln(os.Stderr, "unknown CI environment")
		os.Exit(1)
	}

	foundError := false
	for _, f := range []func() error{env.Prepare, env.RunTests, env.Teardown} {
		err := f()
		if err != nil {
			foundError = true
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
		}
	}

	if foundError {
		os.Exit(1)
	}
}
<commit_msg>Fix glyphcheck<commit_after>// +build ignore

package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

// ForbiddenImports are the packages from the stdlib that should not be used in
// our code.
var ForbiddenImports = map[string]bool{
	"errors": true,
}

var runCrossCompile = flag.Bool("cross-compile", true, "run cross compilation tests")

func init() {
	flag.Parse()
}

// CIEnvironment is implemented by environments where tests can be run.
type CIEnvironment interface {
	Prepare() error
	RunTests() error
	Teardown() error
}

// TravisEnvironment is the environment in which Travis tests run.
type TravisEnvironment struct {
	goxOSArch []string
	env       map[string]string
}

func (env *TravisEnvironment) getMinio() error {
	tempfile, err := os.Create(filepath.Join(os.Getenv("GOPATH"), "bin", "minio"))
	if err != nil {
		return fmt.Errorf("create tempfile for minio download failed: %v\n", err)
	}

	url := fmt.Sprintf("https://dl.minio.io/server/minio/release/%s-%s/minio",
		runtime.GOOS, runtime.GOARCH)
	msg("downloading %v\n", url)
	res, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("error downloading minio server: %v\n", err)
	}

	_, err = io.Copy(tempfile, res.Body)
	if err != nil {
		return fmt.Errorf("error saving minio server to file: %v\n", err)
	}

	err = res.Body.Close()
	if err != nil {
		return fmt.Errorf("error closing HTTP download: %v\n", err)
	}

	err = tempfile.Close()
	if err != nil {
		msg("closing tempfile failed: %v\n", err)
		return fmt.Errorf("error closing minio server file: %v\n", err)
	}

	err = os.Chmod(tempfile.Name(), 0755)
	if err != nil {
		return fmt.Errorf("chmod(minio-server) failed: %v", err)
	}

	msg("downloaded minio server to %v\n", tempfile.Name())
	return nil
}

// Prepare installs dependencies and starts services in order to run the tests.
func (env *TravisEnvironment) Prepare() error {
	env.env = make(map[string]string)

	msg("preparing environment for Travis CI\n")

	pkgs := []string{
		"golang.org/x/tools/cmd/cover",
		"github.com/pierrre/gotestcover",
		"github.com/NebulousLabs/glyphcheck",
		"github.com/restic/rest-server/cmd/rest-server",
	}

	for _, pkg := range pkgs {
		err := run("go", "get", pkg)
		if err != nil {
			return err
		}
	}

	if err := env.getMinio(); err != nil {
		return err
	}

	if *runCrossCompile {
		// only test cross compilation on linux with Travis
		if err := run("go", "get", "github.com/mitchellh/gox"); err != nil {
			return err
		}
		if runtime.GOOS == "linux" {
			env.goxOSArch = []string{
				"linux/386", "linux/amd64",
				"windows/386", "windows/amd64",
				"darwin/386", "darwin/amd64",
				"freebsd/386", "freebsd/amd64",
				"openbsd/386", "openbsd/amd64",
				"linux/arm", "freebsd/arm",
			}
		} else {
			env.goxOSArch = []string{runtime.GOOS + "/" + runtime.GOARCH}
		}

		msg("gox: OS/ARCH %v\n", env.goxOSArch)
	}

	return nil
}

// Teardown stops backend services and cleans the environment again.
func (env *TravisEnvironment) Teardown() error {
	msg("run travis teardown\n")
	return nil
}

// RunTests starts the tests for Travis.
func (env *TravisEnvironment) RunTests() error {
	// do not run fuse tests on darwin
	if runtime.GOOS == "darwin" {
		msg("skip fuse integration tests on %v\n", runtime.GOOS)
		_ = os.Setenv("RESTIC_TEST_FUSE", "0")
	}

	env.env["GOPATH"] = os.Getenv("GOPATH")

	// ensure that the following tests cannot be silently skipped on Travis
	ensureTests := []string{
		"restic/backend/rest.TestBackendREST",
		"restic/backend/sftp.TestBackendSFTP",
		"restic/backend/s3.TestBackendMinio",
	}

	// if the test s3 repository is available, make sure that the test is not skipped
	if os.Getenv("RESTIC_TEST_S3_REPOSITORY") != "" {
		ensureTests = append(ensureTests, "restic/backend/s3.TestBackendS3")
	} else {
		msg("S3 repository not available\n")
	}

	// if the test swift service is available, make sure that the test is not skipped
	if os.Getenv("RESTIC_TEST_SWIFT") != "" {
		ensureTests = append(ensureTests, "restic/backend/swift.TestBackendSwift")
	} else {
		msg("Swift service not available\n")
	}

	// if the test b2 repository is available, make sure that the test is not skipped
	if os.Getenv("RESTIC_TEST_B2_REPOSITORY") != "" {
		ensureTests = append(ensureTests, "restic/backend/b2.TestBackendB2")
	} else {
		msg("B2 repository not available\n")
	}

	env.env["RESTIC_TEST_DISALLOW_SKIP"] = strings.Join(ensureTests, ",")

	if *runCrossCompile {
		// compile for all target architectures with tags
		for _, tags := range []string{"release", "debug"} {
			err := runWithEnv(env.env, "gox", "-verbose",
				"-osarch", strings.Join(env.goxOSArch, " "),
				"-tags", tags,
				"-output", "/tmp/{{.Dir}}_{{.OS}}_{{.Arch}}",
				"./cmd/restic")
			if err != nil {
				return err
			}
		}
	}

	// run the build script
	if err := run("go", "run", "build.go"); err != nil {
		return err
	}

	// run the tests and gather coverage information
	err := runWithEnv(env.env, "gotestcover", "-coverprofile", "all.cov", "github.com/restic/restic/cmd/...", "github.com/restic/restic/internal/...")
	if err != nil {
		return err
	}

	if err = runGofmt(); err != nil {
		return err
	}

	if err = runGlyphcheck(); err != nil {
		return err
	}

	deps, err := findImports()
	if err != nil {
		return err
	}

	foundForbiddenImports := false
	for name, imports := range deps {
		for _, pkg := range imports {
			if _, ok := ForbiddenImports[pkg]; ok {
				fmt.Fprintf(os.Stderr, "========== package %v imports forbidden package %v\n", name, pkg)
				foundForbiddenImports = true
			}
		}
	}

	if foundForbiddenImports {
		return errors.New("CI: forbidden imports found")
	}

	return nil
}

// AppveyorEnvironment is the environment on Windows.
type AppveyorEnvironment struct{}

// Prepare installs dependencies and starts services in order to run the tests.
func (env *AppveyorEnvironment) Prepare() error {
	msg("preparing environment for Appveyor CI\n")
	return nil
}

// RunTests start the tests.
func (env *AppveyorEnvironment) RunTests() error {
	return run("go", "run", "build.go", "-v", "-T")
}

// Teardown is a noop.
func (env *AppveyorEnvironment) Teardown() error {
	return nil
}

// findGoFiles returns a list of go source code file names below dir.
func findGoFiles(dir string) (list []string, err error) {
	err = filepath.Walk(dir, func(name string, fi os.FileInfo, err error) error {
		relpath, err := filepath.Rel(dir, name)
		if err != nil {
			return err
		}

		if relpath == "vendor" || relpath == "pkg" {
			return filepath.SkipDir
		}

		if filepath.Ext(relpath) == ".go" {
			list = append(list, relpath)
		}

		return err
	})

	return list, err
}

func msg(format string, args ...interface{}) {
	fmt.Printf("CI: "+format, args...)
}

func updateEnv(env []string, override map[string]string) []string {
	var newEnv []string
	for _, s := range env {
		d := strings.SplitN(s, "=", 2)
		key := d[0]

		if _, ok := override[key]; ok {
			continue
		}

		newEnv = append(newEnv, s)
	}

	for k, v := range override {
		newEnv = append(newEnv, k+"="+v)
	}

	return newEnv
}

func findImports() (map[string][]string, error) {
	res := make(map[string][]string)

	cwd, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("Getwd() returned error: %v", err)
	}

	gopath := cwd + ":" + filepath.Join(cwd, "vendor")

	cmd := exec.Command("go", "list", "-f", `{{.ImportPath}} {{join .Imports " "}}`, "./src/...")
	cmd.Env = updateEnv(os.Environ(), map[string]string{"GOPATH": gopath})
	cmd.Stderr = os.Stderr

	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	sc := bufio.NewScanner(bytes.NewReader(output))
	for sc.Scan() {
		wordScanner := bufio.NewScanner(strings.NewReader(sc.Text()))
		wordScanner.Split(bufio.ScanWords)

		if !wordScanner.Scan() {
			return nil, fmt.Errorf("package name not found in line: %s", output)
		}
		name := wordScanner.Text()
		var deps []string

		for wordScanner.Scan() {
			deps = append(deps, wordScanner.Text())
		}

		res[name] = deps
	}

	return res, nil
}

func runGofmt() error {
	dir, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Getwd(): %v\n", err)
	}

	files, err := findGoFiles(dir)
	if err != nil {
		return fmt.Errorf("error finding Go files: %v\n", err)
	}

	msg("runGofmt() with %d files\n", len(files))
	args := append([]string{"-l"}, files...)
	cmd := exec.Command("gofmt", args...)
	cmd.Stderr = os.Stderr

	buf, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("error running gofmt: %v\noutput: %s\n", err, buf)
	}

	if len(buf) > 0 {
		return fmt.Errorf("not formatted with `gofmt`:\n%s\n", buf)
	}

	return nil
}

func runGlyphcheck() error {
	cmd := exec.Command("glyphcheck", "./cmd/...", "./internal/...")
	cmd.Stderr = os.Stderr

	buf, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("error running glyphcheck: %v\noutput: %s\n", err, buf)
	}

	return nil
}

func run(command string, args ...string) error {
	msg("run %v %v\n", command, strings.Join(args, " "))
	return runWithEnv(nil, command, args...)
}

// runWithEnv calls a command with the current environment, except the entries
// of the env map are set additionally.
func runWithEnv(env map[string]string, command string, args ...string) error {
	msg("runWithEnv %v %v\n", command, strings.Join(args, " "))
	cmd := exec.Command(command, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if env != nil {
		cmd.Env = updateEnv(os.Environ(), env)
	}
	err := cmd.Run()

	if err != nil {
		return fmt.Errorf("error running %v %v: %v",
			command, strings.Join(args, " "), err)
	}
	return nil
}

func isTravis() bool {
	return os.Getenv("TRAVIS_BUILD_DIR") != ""
}

func isAppveyor() bool {
	return runtime.GOOS == "windows"
}

func main() {
	var env CIEnvironment

	switch {
	case isTravis():
		env = &TravisEnvironment{}
	case isAppveyor():
		env = &AppveyorEnvironment{}
	default:
		fmt.Fprintln(os.Stderr, "unknown CI environment")
		os.Exit(1)
	}

	foundError := false
	for _, f := range []func() error{env.Prepare, env.RunTests, env.Teardown} {
		err := f()
		if err != nil {
			foundError = true
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
		}
	}

	if foundError {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package common

import (
	"bufio"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strings"
)

// CommandPropertySet is a generic function that will set a property for a given plugin/app combination
func CommandPropertySet(pluginName, appName, property, value string, properties map[string]string, globalProperties map[string]bool) {
	if appName != "--global" {
		if err := VerifyAppName(appName); err != nil {
			LogFail(err.Error())
		}
	}
	if appName == "--global" && !globalProperties[property] {
		LogFail("Property cannot be specified globally")
	}
	if property == "" {
		LogFail("No property specified")
	}

	if _, ok := properties[property]; !ok {
		properties := reflect.ValueOf(properties).MapKeys()
		validPropertyList := make([]string, len(properties))
		for i := 0; i < len(properties); i++ {
			validPropertyList[i] = properties[i].String()
		}

		LogFail(fmt.Sprintf("Invalid property specified, valid properties include: %s", strings.Join(validPropertyList, ", ")))
	}

	if value != "" {
		LogInfo2Quiet(fmt.Sprintf("Setting %s to %s", property, value))
		PropertyWrite(pluginName, appName, property, value)
	} else {
		LogInfo2Quiet(fmt.Sprintf("Unsetting %s", property))
		err := PropertyDelete(pluginName, appName, property)
		if err != nil {
			LogFail(err.Error())
		}
	}
}

// PropertyClone clones a set of properties from one app to another
func PropertyClone(pluginName string, oldAppName string, newAppName string) error {
	properties, err := PropertyGetAll(pluginName, oldAppName)
	if err != nil {
		return nil
	}

	for property, value := range properties {
		if err := PropertyWrite(pluginName, newAppName, property, value); err != nil {
			return err
		}
	}

	return nil
}

// PropertyDelete deletes a property from the plugin properties for an app
func PropertyDelete(pluginName string, appName string, property string) error {
	propertyPath := getPropertyPath(pluginName, appName, property)
	if err := os.Remove(propertyPath); err != nil {
		if !PropertyExists(pluginName, appName, property) {
			return nil
		}

		return fmt.Errorf("Unable to remove %s property %s.%s", pluginName, appName, property)
	}

	return nil
}

// PropertyDestroy destroys the plugin properties for an app
func PropertyDestroy(pluginName string, appName string) error {
	if appName == "_all_" {
		pluginConfigPath := getPluginConfigPath(pluginName)
		return os.RemoveAll(pluginConfigPath)
	}

	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	return os.RemoveAll(pluginAppConfigRoot)
}

// PropertyExists returns whether a property exists or not
func PropertyExists(pluginName string, appName string, property string) bool {
	propertyPath := getPropertyPath(pluginName, appName, property)
	_, err := os.Stat(propertyPath)
	return !os.IsNotExist(err)
}

// PropertyGet returns the value for a given property
func PropertyGet(pluginName string, appName string, property string) string {
	return PropertyGetDefault(pluginName, appName, property, "")
}

// PropertyGetAll returns a map of all properties for a given app
func PropertyGetAll(pluginName string, appName string) (map[string]string, error) {
	properties := make(map[string]string)
	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	files, err := ioutil.ReadDir(pluginAppConfigRoot)
	if err != nil {
		return properties, err
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}
		property := file.Name()
		properties[property] = PropertyGet(pluginName, appName, property)
	}

	return properties, nil
}

// PropertyGetDefault returns the value for a given property with a specified default value
func PropertyGetDefault(pluginName, appName, property, defaultValue string) (val string) {
	if !PropertyExists(pluginName, appName, property) {
		val = defaultValue
		return
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	b, err := ioutil.ReadFile(propertyPath)
	if err != nil {
		LogWarn(fmt.Sprintf("Unable to read %s property %s.%s", pluginName, appName, property))
		return
	}
	val = string(b)
	return
}

// PropertyListAdd adds a property to a list at an optionally specified index
func PropertyListAdd(pluginName string, appName string, property string, value string, index int) error {
	if err := propertyTouch(pluginName, appName, property); err != nil {
		return err
	}

	scannedLines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	value = strings.TrimSpace(value)

	var lines []string
	for i, line := range scannedLines {
		if index != 0 && i == (index-1) {
			lines = append(lines, value)
		}
		lines = append(lines, line)
	}

	if index == 0 || index > len(scannedLines) {
		lines = append(lines, value)
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)
	return nil
}

// PropertyListGet returns a property list
func PropertyListGet(pluginName string, appName string, property string) (lines []string, err error) {
	if !PropertyExists(pluginName, appName, property) {
		return lines, nil
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.Open(propertyPath)
	if err != nil {
		return lines, err
	}
	defer file.Close()

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

	if err = scanner.Err(); err != nil {
		return lines, fmt.Errorf("Unable to read %s config value for %s.%s: %s", pluginName, appName, property, err.Error())
	}

	return lines, nil
}

// PropertyListLength returns the length of a property list
func PropertyListLength(pluginName string, appName string, property string) (length int, err error) {
	if !PropertyExists(pluginName, appName, property) {
		return length, nil
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.Open(propertyPath)
	if err != nil {
		return length, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err = scanner.Err(); err != nil {
		return length, fmt.Errorf("Unable to read %s config value for %s.%s: %s", pluginName, appName, property, err.Error())
	}

	length = len(lines)
	return length, nil
}

// PropertyListGetByIndex returns an entry within property list by index
func PropertyListGetByIndex(pluginName string, appName string, property string, index int) (propertyValue string, err error) {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return
	}

	found := false
	for i, line := range lines {
		if i == index {
			propertyValue = line
			found = true
		}
	}

	if !found {
		err = errors.New("Index not found")
	}

	return
}

// PropertyListGetByValue returns an entry within property list by value
func PropertyListGetByValue(pluginName string, appName string, property string, value string) (propertyValue string, err error) {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return
	}

	found := false
	for _, line := range lines {
		if line == value {
			propertyValue = line
			found = true
		}
	}

	if !found {
		err = errors.New("Value not found")
	}

	return
}

// PropertyListRemove removes a value from a property list
func PropertyListRemove(pluginName string, appName string, property string, value string) error {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	found := false
	w := bufio.NewWriter(file)
	for _, line := range lines {
		if line == value {
			found = true
			continue
		}
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)

	if !found {
		return errors.New("Property not found, nothing was removed")
	}

	return nil
}

// PropertyListRemoveByPrefix removes a value by prefix from a property list
func PropertyListRemoveByPrefix(pluginName string, appName string, property string, prefix string) error {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	found := false
	w := bufio.NewWriter(file)
	for _, line := range lines {
		if strings.HasPrefix(line, prefix) {
			found = true
			continue
		}
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)

	if !found {
		return errors.New("Property not found, nothing was removed")
	}

	return nil
}

// PropertyListSet sets a value within a property list at a specified index
func PropertyListSet(pluginName string, appName string, property string, value string, index int) error {
	if err := propertyTouch(pluginName, appName, property); err != nil {
		return err
	}

	scannedLines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	value = strings.TrimSpace(value)

	var lines []string
	if index >= len(scannedLines) {
		for _, line := range scannedLines {
			lines = append(lines, line)
		}
		lines = append(lines, value)
	} else {
		for i, line := range scannedLines {
			if i == index {
				lines = append(lines, value)
			} else {
				lines = append(lines, line)
			}
		}
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)
	return nil
}

// propertyTouch ensures a given application property file exists
func propertyTouch(pluginName string, appName string, property string) error {
	if err := makePluginAppPropertyPath(pluginName, appName); err != nil {
		return fmt.Errorf("Unable to create %s config directory for %s: %s", pluginName, appName, err.Error())
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	if PropertyExists(pluginName, appName, property) {
		return nil
	}

	file, err := os.Create(propertyPath)
	if err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}
	defer file.Close()

	return nil
}

// PropertyWrite writes a value for a given application property
func PropertyWrite(pluginName string, appName string, property string, value string) error {
	if err := propertyTouch(pluginName, appName, property); err != nil {
		return err
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.Create(propertyPath)
	if err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}
	defer file.Close()

	fmt.Fprintf(file, value)
	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)
	return nil
}

// PropertySetup creates the plugin config root
func PropertySetup(pluginName string) error {
	pluginConfigRoot := getPluginConfigPath(pluginName)
	if err := os.MkdirAll(pluginConfigRoot, 0755); err != nil {
		return err
	}
	if err := SetPermissions(filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config"), 0755); err != nil {
		return err
	}
	return SetPermissions(pluginConfigRoot, 0755)
}

func getPropertyPath(pluginName string, appName string, property string) string {
	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	return filepath.Join(pluginAppConfigRoot, property)
}

// getPluginAppPropertyPath returns the plugin property path for a given plugin/app combination
func getPluginAppPropertyPath(pluginName string, appName string) string {
	return filepath.Join(getPluginConfigPath(pluginName), appName)
}

// getPluginConfigPath returns the plugin property path for a given plugin
func getPluginConfigPath(pluginName string) string {
	return filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config", pluginName)
}

// makePluginAppPropertyPath ensures that a property path exists
func makePluginAppPropertyPath(pluginName string, appName string) error {
	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	if err := os.MkdirAll(pluginAppConfigRoot, 0755); err != nil {
		return err
	}
	return SetPermissions(pluginAppConfigRoot, 0755)
}
<commit_msg>chore: sort set properties<commit_after>package common

import (
	"bufio"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
)

// CommandPropertySet is a generic function that will set a property for a given plugin/app combination
func CommandPropertySet(pluginName, appName, property, value string, properties map[string]string, globalProperties map[string]bool) {
	if appName != "--global" {
		if err := VerifyAppName(appName); err != nil {
			LogFail(err.Error())
		}
	}
	if appName == "--global" && !globalProperties[property] {
		LogFail("Property cannot be specified globally")
	}
	if property == "" {
		LogFail("No property specified")
	}

	if _, ok := properties[property]; !ok {
		properties := reflect.ValueOf(properties).MapKeys()
		validPropertyList := make([]string, len(properties))
		for i := 0; i < len(properties); i++ {
			validPropertyList[i] = properties[i].String()
		}

		sort.Strings(validPropertyList)
		LogFail(fmt.Sprintf("Invalid property specified, valid properties include: %s", strings.Join(validPropertyList, ", ")))
	}

	if value != "" {
		LogInfo2Quiet(fmt.Sprintf("Setting %s to %s", property, value))
		PropertyWrite(pluginName, appName, property, value)
	} else {
		LogInfo2Quiet(fmt.Sprintf("Unsetting %s", property))
		err := PropertyDelete(pluginName, appName, property)
		if err != nil {
			LogFail(err.Error())
		}
	}
}

// PropertyClone clones a set of properties from one app to another
func PropertyClone(pluginName string, oldAppName string, newAppName string) error {
	properties, err := PropertyGetAll(pluginName, oldAppName)
	if err != nil {
		return nil
	}

	for property, value := range properties {
		if err := PropertyWrite(pluginName, newAppName, property, value); err != nil {
			return err
		}
	}

	return nil
}

// PropertyDelete deletes a property from the plugin properties for an app
func PropertyDelete(pluginName string, appName string, property string) error {
	propertyPath := getPropertyPath(pluginName, appName, property)
	if err := os.Remove(propertyPath); err != nil {
		if !PropertyExists(pluginName, appName, property) {
			return nil
		}

		return fmt.Errorf("Unable to remove %s property %s.%s", pluginName, appName, property)
	}

	return nil
}

// PropertyDestroy destroys the plugin properties for an app
func PropertyDestroy(pluginName string, appName string) error {
	if appName == "_all_" {
		pluginConfigPath := getPluginConfigPath(pluginName)
		return os.RemoveAll(pluginConfigPath)
	}

	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	return os.RemoveAll(pluginAppConfigRoot)
}

// PropertyExists returns whether a property exists or not
func PropertyExists(pluginName string, appName string, property string) bool {
	propertyPath := getPropertyPath(pluginName, appName, property)
	_, err := os.Stat(propertyPath)
	return !os.IsNotExist(err)
}

// PropertyGet returns the value for a given property
func PropertyGet(pluginName string, appName string, property string) string {
	return PropertyGetDefault(pluginName, appName, property, "")
}

// PropertyGetAll returns a map of all properties for a given app
func PropertyGetAll(pluginName string, appName string) (map[string]string, error) {
	properties := make(map[string]string)
	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	files, err := ioutil.ReadDir(pluginAppConfigRoot)
	if err != nil {
		return properties, err
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}
		property := file.Name()
		properties[property] = PropertyGet(pluginName, appName, property)
	}

	return properties, nil
}

// PropertyGetDefault returns the value for a given property with a specified default value
func PropertyGetDefault(pluginName, appName, property, defaultValue string) (val string) {
	if !PropertyExists(pluginName, appName, property) {
		val = defaultValue
		return
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	b, err := ioutil.ReadFile(propertyPath)
	if err != nil {
		LogWarn(fmt.Sprintf("Unable to read %s property %s.%s", pluginName, appName, property))
		return
	}
	val = string(b)
	return
}

// PropertyListAdd adds a property to a list at an optionally specified index
func PropertyListAdd(pluginName string, appName string, property string, value string, index int) error {
	if err := propertyTouch(pluginName, appName, property); err != nil {
		return err
	}

	scannedLines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	value = strings.TrimSpace(value)

	var lines []string
	for i, line := range scannedLines {
		if index != 0 && i == (index-1) {
			lines = append(lines, value)
		}
		lines = append(lines, line)
	}

	if index == 0 || index > len(scannedLines) {
		lines = append(lines, value)
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)
	return nil
}

// PropertyListGet returns a property list
func PropertyListGet(pluginName string, appName string, property string) (lines []string, err error) {
	if !PropertyExists(pluginName, appName, property) {
		return lines, nil
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.Open(propertyPath)
	if err != nil {
		return lines, err
	}
	defer file.Close()

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

	if err = scanner.Err(); err != nil {
		return lines, fmt.Errorf("Unable to read %s config value for %s.%s: %s", pluginName, appName, property, err.Error())
	}

	return lines, nil
}

// PropertyListLength returns the length of a property list
func PropertyListLength(pluginName string, appName string, property string) (length int, err error) {
	if !PropertyExists(pluginName, appName, property) {
		return length, nil
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.Open(propertyPath)
	if err != nil {
		return length, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err = scanner.Err(); err != nil {
		return length, fmt.Errorf("Unable to read %s config value for %s.%s: %s", pluginName, appName, property, err.Error())
	}

	length = len(lines)
	return length, nil
}

// PropertyListGetByIndex returns an entry within property list by index
func PropertyListGetByIndex(pluginName string, appName string, property string, index int) (propertyValue string, err error) {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return
	}

	found := false
	for i, line := range lines {
		if i == index {
			propertyValue = line
			found = true
		}
	}

	if !found {
		err = errors.New("Index not found")
	}

	return
}

// PropertyListGetByValue returns an entry within property list by value
func PropertyListGetByValue(pluginName string, appName string, property string, value string) (propertyValue string, err error) {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return
	}

	found := false
	for _, line := range lines {
		if line == value {
			propertyValue = line
			found = true
		}
	}

	if !found {
		err = errors.New("Value not found")
	}

	return
}

// PropertyListRemove removes a value from a property list
func PropertyListRemove(pluginName string, appName string, property string, value string) error {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	found := false
	w := bufio.NewWriter(file)
	for _, line := range lines {
		if line == value {
			found = true
			continue
		}
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)

	if !found {
		return errors.New("Property not found, nothing was removed")
	}

	return nil
}

// PropertyListRemoveByPrefix removes a value by prefix from a property list
func PropertyListRemoveByPrefix(pluginName string, appName string, property string, prefix string) error {
	lines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	found := false
	w := bufio.NewWriter(file)
	for _, line := range lines {
		if strings.HasPrefix(line, prefix) {
			found = true
			continue
		}
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)

	if !found {
		return errors.New("Property not found, nothing was removed")
	}

	return nil
}

// PropertyListSet sets a value within a property list at a specified index
func PropertyListSet(pluginName string, appName string, property string, value string, index int) error {
	if err := propertyTouch(pluginName, appName, property); err != nil {
		return err
	}

	scannedLines, err := PropertyListGet(pluginName, appName, property)
	if err != nil {
		return err
	}

	value = strings.TrimSpace(value)

	var lines []string
	if index >= len(scannedLines) {
		for _, line := range scannedLines {
			lines = append(lines, line)
		}
		lines = append(lines, value)
	} else {
		for i, line := range scannedLines {
			if i == index {
				lines = append(lines, value)
			} else {
				lines = append(lines, line)
			}
		}
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.OpenFile(propertyPath, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}

	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)
	return nil
}

// propertyTouch ensures a given application property file exists
func propertyTouch(pluginName string, appName string, property string) error {
	if err := makePluginAppPropertyPath(pluginName, appName); err != nil {
		return fmt.Errorf("Unable to create %s config directory for %s: %s", pluginName, appName, err.Error())
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	if PropertyExists(pluginName, appName, property) {
		return nil
	}

	file, err := os.Create(propertyPath)
	if err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}
	defer file.Close()

	return nil
}

// PropertyWrite writes a value for a given application property
func PropertyWrite(pluginName string, appName string, property string, value string) error {
	if err := propertyTouch(pluginName, appName, property); err != nil {
		return err
	}

	propertyPath := getPropertyPath(pluginName, appName, property)
	file, err := os.Create(propertyPath)
	if err != nil {
		return fmt.Errorf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error())
	}
	defer file.Close()

	fmt.Fprintf(file, value)
	file.Chmod(0600)
	SetPermissions(propertyPath, 0600)
	return nil
}

// PropertySetup creates the plugin config root
func PropertySetup(pluginName string) error {
	pluginConfigRoot := getPluginConfigPath(pluginName)
	if err := os.MkdirAll(pluginConfigRoot, 0755); err != nil {
		return err
	}
	if err := SetPermissions(filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config"), 0755); err != nil {
		return err
	}
	return SetPermissions(pluginConfigRoot, 0755)
}

func getPropertyPath(pluginName string, appName string, property string) string {
	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	return filepath.Join(pluginAppConfigRoot, property)
}

// getPluginAppPropertyPath returns the plugin property path for a given plugin/app combination
func getPluginAppPropertyPath(pluginName string, appName string) string {
	return filepath.Join(getPluginConfigPath(pluginName), appName)
}

// getPluginConfigPath returns the plugin property path for a given plugin
func getPluginConfigPath(pluginName string) string {
	return filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config", pluginName)
}

// makePluginAppPropertyPath ensures that a property path exists
func makePluginAppPropertyPath(pluginName string, appName string) error {
	pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
	if err := os.MkdirAll(pluginAppConfigRoot, 0755); err != nil {
		return err
	}
	return SetPermissions(pluginAppConfigRoot, 0755)
}
<|endoftext|>


<commit_before>// Copyright 2015 Dmitry Vyukov. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package mail

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/mail"
	"reflect"
)

func Fuzz(data []byte) int {
	msg, err := mail.ReadMessage(bytes.NewReader(data))
	if err != nil {
		return 0
	}
	msg.Header.AddressList("to")
	msg.Header.Date()
	if addr, err := mail.ParseAddress(msg.Header.Get("from")); err == nil {
		addr1, err := mail.ParseAddress(addr.String())
		if false {
			// https://github.com/golang/go/issues/11292
			// https://github.com/golang/go/issues/11293
			// https://github.com/golang/go/issues/11294
			if err != nil {
				panic(err)
			}
			if !reflect.DeepEqual(addr, addr1) {
				panic("addr changed")
			}
		}
	}
	io.Copy(ioutil.Discard, msg.Body)
	return 1
}
<commit_msg>mail: add fuzz target for ParseAddressList<commit_after>// Copyright 2015 Dmitry Vyukov. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package mail

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/mail"
	"reflect"
	"strings"
)

func Fuzz(data []byte) int {
	msg, err := mail.ReadMessage(bytes.NewReader(data))
	if err != nil {
		return 0
	}
	msg.Header.AddressList("to")
	msg.Header.Date()
	if addr, err := mail.ParseAddress(msg.Header.Get("from")); err == nil {
		addr1, err := mail.ParseAddress(addr.String())
		if err != nil {
			panic(err)
		}
		if !reflect.DeepEqual(addr, addr1) {
			panic("addr changed")
		}
	}
	io.Copy(ioutil.Discard, msg.Body)
	return 1
}

func FuzzParseAddressList(data []byte) int {
	list, err := mail.ParseAddressList(string(data))
	if err != nil {
		return 0
	}
	var addrs []string
	for _, addr := range list {
		addrs = append(addrs, addr.String())
	}
	list1, err := mail.ParseAddressList(strings.Join(addrs, ","))
	if err != nil {
		panic(err)
	}
	if !reflect.DeepEqual(list, list1) {
		panic("list changed")
	}
	return 1
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/elsy/helpers"
	"github.com/elsy/template"
)

func main() {
	if err := LoadConfigFile("lc.yml"); err != nil {
		panic(err)
	}

	app := cli.NewApp()
	app.Name = "lc"
	app.Version = helpers.BuildVersionString()
	app.Author = "Lancope"
	app.Email = "#arch channel on Slack"
	app.Usage = "Manages and builds Lancope projects"

	app.Flags = GlobalFlags()
	app.Commands = Commands()
	app.CommandNotFound = CommandNotFound
	app.Before = beforeHook
	app.After = afterHook
	app.RunAndExitOnError()

	if !CommandSuccess {
		os.Exit(1)
	}
}

func beforeHook(c *cli.Context) error {
	setLogLevel(c)
	preReqCheck(c)
	setComposeBinary(c)
	setComposeProjectName(c)
	setComposeTemplate(c)
	addSignalListener()
	return nil
}

func afterHook(c *cli.Context) error {
	return removeComposeTemplate()
}

func addSignalListener() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-c
		removeComposeTemplate()
		os.Exit(2)
	}()
}

func removeComposeTemplate() error {
	// clean up compose template if it exists
	if file := os.Getenv("LC_BASE_COMPOSE_FILE"); len(file) > 0 {
		logrus.Debugf("attempting to remove base compose file: %v", file)
		if err := os.Remove(file); err != nil {
			return err
		}
	}
	return nil
}

func setLogLevel(c *cli.Context) {
	if c.GlobalBool("debug") {
		logrus.SetLevel(logrus.DebugLevel)
	} else {
		logrus.SetLevel(logrus.InfoLevel)
	}
}

func preReqCheck(c *cli.Context) {
	if len(c.Args()) == 0 || c.Args()[0] == "system" {
		// system commands do not need docker
		return
	}

	// TODO: replace this with checking presence and version of local-docker-stack
	if _, err := exec.LookPath("docker"); err != nil {
		logrus.Fatal("could not find docker, please install local-docker-stack")
	}
	dockerCompose := c.GlobalString("docker-compose")
	if _, err := exec.LookPath(dockerCompose); err != nil {
		logrus.Fatalf("could not find docker compose binary: %q, please install local-docker-stack", dockerCompose)
	}

	if versionString, versionComponents, err := helpers.GetDockerComposeVersion(c); err != nil {
		logrus.Warnf("failed checking docker-compose version. Note that lc only supports docker-compose 1.5.0 or higher")
	} else {
		major, minor := versionComponents[0], versionComponents[1]
		// assuming we won't see any docker-compose versions less than 1.x
		if major == 1 && minor < 5 {
			logrus.Fatalf("found docker-compose version %s, lc only supports docker-compose 1.5.0 or higher", versionString)
		}
	}

	if err := helpers.EnsureDockerConnectivity(); err != nil {
		ip, _ := helpers.DockerIp()
		logrus.Fatalf("could not connect to docker daemon at %q, err: %q.", ip, err)

	}
}

func setComposeBinary(c *cli.Context) {
	os.Setenv("DOCKER_COMPOSE_BINARY", c.GlobalString("docker-compose"))
}

func setComposeProjectName(c *cli.Context) {
	var invalidChars = regexp.MustCompile("[^a-z0-9]")
	projectName := c.GlobalString("project-name")
	if len(projectName) == 0 {
		logrus.Debug("using current working directory for compose project name")
		path, _ := os.Getwd()
		projectName = filepath.Base(path)
	} else {
		logrus.Debugf("using configured value: %q for project name", projectName)
	}
	projectName = invalidChars.ReplaceAllString(strings.ToLower(projectName), "")
	os.Setenv("COMPOSE_PROJECT_NAME", projectName)
}

func setComposeTemplate(c *cli.Context) {
	templateName := c.GlobalString("template")
	enableScratchVolume := c.GlobalBool("enable-scratch-volumes")
	if len(templateName) > 0 {
		if yaml, err := template.Get(templateName, enableScratchVolume); err == nil {
			file := createTempComposeFile(yaml)
			logrus.Debugf("setting LC_BASE_COMPOSE_FILE to %v", file)
			os.Setenv("LC_BASE_COMPOSE_FILE", file)
		} else {
			logrus.Panicf("template %q does not exist", templateName)
		}
	}

	dataContainers := template.GetSharedExternalDataContainers(templateName)
	for _, dataContainer := range dataContainers {
		if err := dataContainer.Ensure(c.GlobalBool("offline")); err != nil {
			logrus.Panic("unable to create data container")
		}
	}
}

func createTempComposeFile(yaml string) string {
	cwd, _ := os.Getwd()
	fh, err := ioutil.TempFile(cwd, "lc_docker_compose_template")
	if err != nil {
		logrus.Panic("could not create temporary yaml file")
	}
	defer fh.Close()
	_, err = fh.WriteString(yaml)
	if err != nil {
		logrus.Panic("could not write to temporary yaml file")
	}
	return fh.Name()
}
<commit_msg>update author info<commit_after>package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/elsy/helpers"
	"github.com/elsy/template"
)

func main() {
	if err := LoadConfigFile("lc.yml"); err != nil {
		panic(err)
	}

	app := cli.NewApp()
	app.Name = "lc"
	app.Version = helpers.BuildVersionString()
	app.Author = "Cisco"
	app.Usage = "an opinionated, multi-language, build-tool based on Docker and Docker Compose"

	app.Flags = GlobalFlags()
	app.Commands = Commands()
	app.CommandNotFound = CommandNotFound
	app.Before = beforeHook
	app.After = afterHook
	app.RunAndExitOnError()

	if !CommandSuccess {
		os.Exit(1)
	}
}

func beforeHook(c *cli.Context) error {
	setLogLevel(c)
	preReqCheck(c)
	setComposeBinary(c)
	setComposeProjectName(c)
	setComposeTemplate(c)
	addSignalListener()
	return nil
}

func afterHook(c *cli.Context) error {
	return removeComposeTemplate()
}

func addSignalListener() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-c
		removeComposeTemplate()
		os.Exit(2)
	}()
}

func removeComposeTemplate() error {
	// clean up compose template if it exists
	if file := os.Getenv("LC_BASE_COMPOSE_FILE"); len(file) > 0 {
		logrus.Debugf("attempting to remove base compose file: %v", file)
		if err := os.Remove(file); err != nil {
			return err
		}
	}
	return nil
}

func setLogLevel(c *cli.Context) {
	if c.GlobalBool("debug") {
		logrus.SetLevel(logrus.DebugLevel)
	} else {
		logrus.SetLevel(logrus.InfoLevel)
	}
}

func preReqCheck(c *cli.Context) {
	if len(c.Args()) == 0 || c.Args()[0] == "system" {
		// system commands do not need docker
		return
	}

	// TODO: replace this with checking presence and version of local-docker-stack
	if _, err := exec.LookPath("docker"); err != nil {
		logrus.Fatal("could not find docker, please install local-docker-stack")
	}
	dockerCompose := c.GlobalString("docker-compose")
	if _, err := exec.LookPath(dockerCompose); err != nil {
		logrus.Fatalf("could not find docker compose binary: %q, please install local-docker-stack", dockerCompose)
	}

	if versionString, versionComponents, err := helpers.GetDockerComposeVersion(c); err != nil {
		logrus.Warnf("failed checking docker-compose version. Note that lc only supports docker-compose 1.5.0 or higher")
	} else {
		major, minor := versionComponents[0], versionComponents[1]
		// assuming we won't see any docker-compose versions less than 1.x
		if major == 1 && minor < 5 {
			logrus.Fatalf("found docker-compose version %s, lc only supports docker-compose 1.5.0 or higher", versionString)
		}
	}

	if err := helpers.EnsureDockerConnectivity(); err != nil {
		ip, _ := helpers.DockerIp()
		logrus.Fatalf("could not connect to docker daemon at %q, err: %q.", ip, err)

	}
}

func setComposeBinary(c *cli.Context) {
	os.Setenv("DOCKER_COMPOSE_BINARY", c.GlobalString("docker-compose"))
}

func setComposeProjectName(c *cli.Context) {
	var invalidChars = regexp.MustCompile("[^a-z0-9]")
	projectName := c.GlobalString("project-name")
	if len(projectName) == 0 {
		logrus.Debug("using current working directory for compose project name")
		path, _ := os.Getwd()
		projectName = filepath.Base(path)
	} else {
		logrus.Debugf("using configured value: %q for project name", projectName)
	}
	projectName = invalidChars.ReplaceAllString(strings.ToLower(projectName), "")
	os.Setenv("COMPOSE_PROJECT_NAME", projectName)
}

func setComposeTemplate(c *cli.Context) {
	templateName := c.GlobalString("template")
	enableScratchVolume := c.GlobalBool("enable-scratch-volumes")
	if len(templateName) > 0 {
		if yaml, err := template.Get(templateName, enableScratchVolume); err == nil {
			file := createTempComposeFile(yaml)
			logrus.Debugf("setting LC_BASE_COMPOSE_FILE to %v", file)
			os.Setenv("LC_BASE_COMPOSE_FILE", file)
		} else {
			logrus.Panicf("template %q does not exist", templateName)
		}
	}

	dataContainers := template.GetSharedExternalDataContainers(templateName)
	for _, dataContainer := range dataContainers {
		if err := dataContainer.Ensure(c.GlobalBool("offline")); err != nil {
			logrus.Panic("unable to create data container")
		}
	}
}

func createTempComposeFile(yaml string) string {
	cwd, _ := os.Getwd()
	fh, err := ioutil.TempFile(cwd, "lc_docker_compose_template")
	if err != nil {
		logrus.Panic("could not create temporary yaml file")
	}
	defer fh.Close()
	_, err = fh.WriteString(yaml)
	if err != nil {
		logrus.Panic("could not write to temporary yaml file")
	}
	return fh.Name()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"
	"time"

	"github.com/gaeshi-gaeshi/thermo-smart/HeatersController"
	"github.com/gaeshi-gaeshi/thermo-smart/TemperatureSensorController"
)

func main() {
	if os.Args[1] == "--reset" {
		HeatersController.SetNumberOfWorkingHeaters(0)

		return
	}

	targetTemperatureAsFloat64, error := strconv.ParseFloat(os.Args[1], 32)
	if error != nil {
		fmt.Println(error)
		return
	}

	targetTemperature := float32(targetTemperatureAsFloat64)

	fmt.Printf("Target temperature - %f\n", targetTemperature)

	for {
		currentTemperature, error := TemperatureSensorController.ReadTemperature()
		if error != nil {
			fmt.Println(error)
			return
		}

		temperatureDifference := targetTemperature - currentTemperature
		if temperatureDifference > 1 {
			HeatersController.SetNumberOfWorkingHeaters(3)
		} else if temperatureDifference <= 1 && temperatureDifference > 0 {
			HeatersController.SetNumberOfWorkingHeaters(2)
		} else if temperatureDifference <= 0 && temperatureDifference > -1 {
			HeatersController.SetNumberOfWorkingHeaters(1)
		} else {
			HeatersController.SetNumberOfWorkingHeaters(0)
		}

		fmt.Printf("Current temperature - %f\n", currentTemperature)
		fmt.Printf("Currently working heaters - %d\n", HeatersController.GetNumberOfWorkingHeaters())

		time.Sleep(time.Minute)
	}
}
<commit_msg>Fixed precision of precision of printed temperatures.<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"
	"time"

	"github.com/gaeshi-gaeshi/thermo-smart/HeatersController"
	"github.com/gaeshi-gaeshi/thermo-smart/TemperatureSensorController"
)

func main() {
	if os.Args[1] == "--reset" {
		HeatersController.SetNumberOfWorkingHeaters(0)

		return
	}

	targetTemperatureAsFloat64, error := strconv.ParseFloat(os.Args[1], 32)
	if error != nil {
		fmt.Println(error)
		return
	}

	targetTemperature := float32(targetTemperatureAsFloat64)

	fmt.Printf("Target temperature - %.2f\n", targetTemperature)

	for {
		currentTemperature, error := TemperatureSensorController.ReadTemperature()
		if error != nil {
			fmt.Println(error)
			return
		}

		temperatureDifference := targetTemperature - currentTemperature
		if temperatureDifference > 1 {
			HeatersController.SetNumberOfWorkingHeaters(3)
		} else if temperatureDifference <= 1 && temperatureDifference > 0 {
			HeatersController.SetNumberOfWorkingHeaters(2)
		} else if temperatureDifference <= 0 && temperatureDifference > -1 {
			HeatersController.SetNumberOfWorkingHeaters(1)
		} else {
			HeatersController.SetNumberOfWorkingHeaters(0)
		}

		fmt.Printf("Current temperature - %.2f\n", currentTemperature)
		fmt.Printf("Currently working heaters - %d\n", HeatersController.GetNumberOfWorkingHeaters())

		time.Sleep(time.Minute)
	}
}
<|endoftext|>


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

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/ava-labs/avalanchego/app/process"
	"github.com/ava-labs/avalanchego/config"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
)

var (
	// GitCommit should be optionally set at compile time.
	GitCommit string
)

// main is the entry point to AvalancheGo.
func main() {
	fmt.Println(process.Header)

	// Get the config
	rootConfig, version, displayVersion, err := config.GetConfig(GitCommit)
	if err != nil {
		fmt.Printf("couldn't get config: %s", err)
		os.Exit(1)
	}

	if displayVersion {
		fmt.Print(version)
		os.Exit(0)
	}

	// Set the log directory for this process by adding a subdirectory
	// "daemon" to the log directory given in the config
	logConfigCopy := rootConfig.LoggingConfig
	logConfigCopy.Directory = filepath.Join(logConfigCopy.Directory, "daemon")
	logFactory := logging.NewFactory(logConfigCopy)

	log, err := logFactory.Make()
	if err != nil {
		logFactory.Close()

		fmt.Printf("starting logger failed with: %s\n", err)
		os.Exit(1)
	}
	log.Info("using build directory at path '%s'", rootConfig.BuildDir)

	nodeManager := newNodeManager(rootConfig.BuildDir, log)
	_ = utils.HandleSignals(
		func(os.Signal) {
			// SIGINT and SIGTERM cause all running nodes
			// to be ended and this program to exit with
			// exit code 0
			nodeManager.shutdown()
			os.Exit(0)
		},
		syscall.SIGINT, syscall.SIGTERM,
	)

	// Migrate the database if necessary
	migrationManager := newMigrationManager(nodeManager, rootConfig, log)
	if err := migrationManager.migrate(); err != nil {
		log.Error("error while running migration: %s", err)
		nodeManager.shutdown()
		os.Exit(1)
	}

	// Run normally
	log.Info("starting to run node in normal execution mode")
	exitCode, err := nodeManager.runNormal()
	log.Debug("node manager returned exit code %s, error %v", exitCode, err)
	nodeManager.shutdown() // make sure all the nodes are stopped

	logFactory.Close()
	os.Exit(exitCode)
}
<commit_msg>fixed version printing<commit_after>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/ava-labs/avalanchego/app/process"
	"github.com/ava-labs/avalanchego/config"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
)

var (
	// GitCommit should be optionally set at compile time.
	GitCommit string
)

// main is the entry point to AvalancheGo.
func main() {
	// Get the config
	rootConfig, version, displayVersion, err := config.GetConfig(GitCommit)
	if err != nil {
		fmt.Printf("couldn't get config: %s", err)
		os.Exit(1)
	}

	if displayVersion {
		fmt.Print(version)
		os.Exit(0)
	}

	fmt.Println(process.Header)

	// Set the log directory for this process by adding a subdirectory
	// "daemon" to the log directory given in the config
	logConfigCopy := rootConfig.LoggingConfig
	logConfigCopy.Directory = filepath.Join(logConfigCopy.Directory, "daemon")
	logFactory := logging.NewFactory(logConfigCopy)

	log, err := logFactory.Make()
	if err != nil {
		logFactory.Close()

		fmt.Printf("starting logger failed with: %s\n", err)
		os.Exit(1)
	}

	log.Info("using build directory at path '%s'", rootConfig.BuildDir)

	nodeManager := newNodeManager(rootConfig.BuildDir, log)
	_ = utils.HandleSignals(
		func(os.Signal) {
			// SIGINT and SIGTERM cause all running nodes
			// to be ended and this program to exit with
			// exit code 0
			nodeManager.shutdown()
			os.Exit(0)
		},
		syscall.SIGINT, syscall.SIGTERM,
	)

	// Migrate the database if necessary
	migrationManager := newMigrationManager(nodeManager, rootConfig, log)
	if err := migrationManager.migrate(); err != nil {
		log.Error("error while running migration: %s", err)
		nodeManager.shutdown()
		os.Exit(1)
	}

	// Run normally
	log.Info("starting to run node in normal execution mode")
	exitCode, err := nodeManager.runNormal()
	log.Debug("node manager returned exit code %s, error %v", exitCode, err)
	nodeManager.shutdown() // make sure all the nodes are stopped

	logFactory.Close()
	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/couchbaselabs/logg"
	"github.com/tleyden/checkerlution"
	ng "github.com/tleyden/neurgo"
)

func main() {

	logg.LogKeys["MAIN"] = true

	ng.SeedRandom()

	redTeam := checkerlution.RED_TEAM
	game := checkerlution.NewGame(redTeam)
	game.GameLoop()

}
<commit_msg>remove main<commit_after><|endoftext|>


<commit_before>package parser

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

//define address
const (
	Xunlei   = "http://bt.box.n0808.com/%s/%s/%s.torrent"
	Torcache = "https://torcache.net/torrent/%s.torrent"
)

//define errors
var (
	ErrNotFound = errors.New("not found")
	LibUrls     = []string{
		"http://www.torrent.org.cn/Home/torrent/download.html?hash=%s",
		"http://torcache.net/torrent/%s.torrent",
		"http://torrage.com/torrent/%s.torrent",
		"http://zoink.it/torrent/%s.torrent",
		"https://178.73.198.210/torrent/%s.torrent",
		"http://d1.torrentkittycn.com/?infohash=%s",
		"http://reflektor.karmorra.info/torrent/%s.torrent",
	}
)

//DownloadXunlei torrent
func DownloadXunlei(hash string, client *http.Client) (mi MetaInfo, err error) {
	mi.InfoHash = hash
	if len(hash) != 40 {
		err = errors.New("invalid hash len")
		return
	}

	//从迅雷种子库查找
	address := fmt.Sprintf(Xunlei, hash[:2], hash[len(hash)-2:], hash)
	req0, err := http.NewRequest("GET", address, nil)
	if err != nil {
		return
	}
	req0.Header.Set("User-Agent", "Mozilla/5.0")
	resp, err := client.Do(req0)
	if err != nil {
		return
	}
	if resp != nil {
		defer func() {
			// io.Copy(ioutil.Discard, resp.Body)
			resp.Body.Close()
		}()

		if resp.StatusCode == 200 {
			//解析种子
			err = mi.Parse(resp.Body)
		} else if resp.StatusCode == 404 {
			err = ErrNotFound
		} else {
			err = errors.New("refuse error")
		}
	}
	return
}

//Download torrent
func DownloadTorrent(hash string, client *http.Client) (mi MetaInfo, err error) {
	if len(hash) != 40 {
		err = errors.New("invalid hash len")
		return
	}
	mi, err = DownloadXunlei(hash, client)
	//迅雷解析成功，不用再調用後面的種子庫
	if err == nil {
		return
	}

	mi.InfoHash = hash
	//將來改用字典實現
	for _, lib_url := range LibUrls {
		address := fmt.Sprintf(lib_url, strings.ToUpper(hash))
		req0, err := http.NewRequest("GET", address, nil)
		if err != nil {
			continue
		}
		resp, err := client.Do(req0)
		if err != nil {
			continue
		}
		if resp != nil {
			defer func() {
				// io.Copy(ioutil.Discard, resp.Body)
				resp.Body.Close()
			}()

			if resp.StatusCode == 200 {
				//解析种子
				err = mi.Parse(resp.Body)
				return mi, err
			} else if resp.StatusCode == 404 {
				err = ErrNotFound
			} else {
				err = errors.New("refuse error")
			}
		}
	}
	return
}

func pretty(v interface{}) {
	b, _ := json.MarshalIndent(v, " ", "  ")
	fmt.Println(string(b))
}
<commit_msg>增加itorrents种子库，移除其他种子库<commit_after>package parser

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

//define address
const (
	Xunlei   = "http://bt.box.n0808.com/%s/%s/%s.torrent"
	Torcache = "https://torcache.net/torrent/%s.torrent"
)

//define errors
var (
	ErrNotFound = errors.New("not found")
	LibUrls     = []string{
		// "http://www.torrent.org.cn/Home/torrent/download.html?hash=%s",
		// "http://torcache.net/torrent/%s.torrent",
		// "http://torrage.com/torrent/%s.torrent",
		// "http://zoink.it/torrent/%s.torrent",
		// "https://178.73.198.210/torrent/%s.torrent",
		// "http://d1.torrentkittycn.com/?infohash=%s",
		// "http://reflektor.karmorra.info/torrent/%s.torrent",
		"http://itorrents.org/torrent/%s.torrent",
	}
)

//DownloadXunlei torrent
func DownloadXunlei(hash string, client *http.Client) (mi MetaInfo, err error) {
	mi.InfoHash = hash
	if len(hash) != 40 {
		err = errors.New("invalid hash len")
		return
	}

	//从迅雷种子库查找
	address := fmt.Sprintf(Xunlei, hash[:2], hash[len(hash)-2:], hash)
	req0, err := http.NewRequest("GET", address, nil)
	if err != nil {
		return
	}
	req0.Header.Set("User-Agent", "Mozilla/5.0")
	resp, err := client.Do(req0)
	if err != nil {
		return
	}
	if resp != nil {
		defer func() {
			// io.Copy(ioutil.Discard, resp.Body)
			resp.Body.Close()
		}()

		if resp.StatusCode == 200 {
			//解析种子
			err = mi.Parse(resp.Body)
		} else if resp.StatusCode == 404 {
			err = ErrNotFound
		} else {
			err = errors.New("refuse error")
		}
	}
	return
}

//Download torrent
func DownloadTorrent(hash string, client *http.Client) (mi MetaInfo, err error) {
	if len(hash) != 40 {
		err = errors.New("invalid hash len")
		return
	}
	// mi, err = DownloadXunlei(hash, client)
	// //迅雷解析成功，不用再調用後面的種子庫
	// if err == nil {
	// 	return
	// }

	mi.InfoHash = hash
	//將來改用字典實現
	for _, lib_url := range LibUrls {
		address := fmt.Sprintf(lib_url, strings.ToUpper(hash))
		req0, err := http.NewRequest("GET", address, nil)
		if err != nil {
			continue
		}
		resp, err := client.Do(req0)
		if err != nil {
			continue
		}
		if resp != nil {
			defer func() {
				// io.Copy(ioutil.Discard, resp.Body)
				resp.Body.Close()
			}()

			if resp.StatusCode == 200 {
				//解析种子
				err = mi.Parse(resp.Body)
				return mi, err
			} else if resp.StatusCode == 404 {
				err = ErrNotFound
			} else {
				err = errors.New("refuse error")
			}
		}
	}
	return
}

func pretty(v interface{}) {
	b, _ := json.MarshalIndent(v, " ", "  ")
	fmt.Println(string(b))
}
<|endoftext|>