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

package main

import (
	"encoding/json"
	"fmt"
	"net"

	"github.com/containernetworking/cni/pkg/skel"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/containernetworking/cni/pkg/version"

	types020 "github.com/containernetworking/cni/pkg/types/020"
)

// The top-level network config - IPAM plugins are passed the full configuration
// of the calling plugin, not just the IPAM section.
type Net struct {
	Name       string      `json:"name"`
	CNIVersion string      `json:"cniVersion"`
	IPAM       *IPAMConfig `json:"ipam"`
}

type IPAMConfig struct {
	Name      string
	Type      string         `json:"type"`
	Routes    []*types.Route `json:"routes"`
	Addresses []Address      `json:"addresses"`
	DNS       types.DNS      `json:"dns"`
}

type Address struct {
	AddressStr string `json:"address"`
	Gateway    net.IP `json:"gateway,omitempty"`
	Address    net.IPNet
	Version    string
}

func main() {
	// TODO: implement plugin version
	skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
}

func cmdGet(args *skel.CmdArgs) error {
	// TODO: implement
	return fmt.Errorf("not implemented")
}

// canonicalizeIP makes sure a provided ip is in standard form
func canonicalizeIP(ip *net.IP) error {
	if ip.To4() != nil {
		*ip = ip.To4()
		return nil
	} else if ip.To16() != nil {
		*ip = ip.To16()
		return nil
	}
	return fmt.Errorf("IP %s not v4 nor v6", *ip)
}

// NewIPAMConfig creates a NetworkConfig from the given network name.
func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
	n := Net{}
	if err := json.Unmarshal(bytes, &n); err != nil {
		return nil, "", err
	}

	if n.IPAM == nil {
		return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
	}

	// Validate all ranges
	numV4 := 0
	numV6 := 0
	for i := range n.IPAM.Addresses {
		ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
		if err != nil {
			return nil, "", fmt.Errorf("invalid CIDR %s: %s", n.IPAM.Addresses[i].AddressStr, err)
		}
		n.IPAM.Addresses[i].Address = *addr
		n.IPAM.Addresses[i].Address.IP = ip

		if err := canonicalizeIP(&n.IPAM.Addresses[i].Address.IP); err != nil {
			return nil, "", fmt.Errorf("invalid address %d: %s", i, err)
		}

		if n.IPAM.Addresses[i].Address.IP.To4() != nil {
			n.IPAM.Addresses[i].Version = "4"
			numV4++
		} else {
			n.IPAM.Addresses[i].Version = "6"
			numV6++
		}
	}

	// CNI spec 0.2.0 and below supported only one v4 and v6 address
	if numV4 > 1 || numV6 > 1 {
		for _, v := range types020.SupportedVersions {
			if n.CNIVersion == v {
				return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
			}
		}
	}

	// Copy net name into IPAM so not to drag Net struct around
	n.IPAM.Name = n.Name

	return n.IPAM, n.CNIVersion, nil
}

func cmdAdd(args *skel.CmdArgs) error {
	ipamConf, confVersion, err := LoadIPAMConfig(args.StdinData, args.Args)
	if err != nil {
		return err
	}

	result := &current.Result{}
	result.DNS = ipamConf.DNS
	result.Routes = ipamConf.Routes
	for _, v := range ipamConf.Addresses {
		result.IPs = append(result.IPs, &current.IPConfig{
			Version: v.Version,
			Address: v.Address,
			Gateway: v.Gateway})
	}

	result.Routes = ipamConf.Routes
	return types.PrintResult(result, confVersion)
}

func cmdDel(args *skel.CmdArgs) error {
	// Nothing required because of no resource allocation in static plugin.
	return nil
}
<commit_msg>remove duplicated assginment.<commit_after>// Copyright 2018 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"net"

	"github.com/containernetworking/cni/pkg/skel"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/containernetworking/cni/pkg/version"

	types020 "github.com/containernetworking/cni/pkg/types/020"
)

// The top-level network config - IPAM plugins are passed the full configuration
// of the calling plugin, not just the IPAM section.
type Net struct {
	Name       string      `json:"name"`
	CNIVersion string      `json:"cniVersion"`
	IPAM       *IPAMConfig `json:"ipam"`
}

type IPAMConfig struct {
	Name      string
	Type      string         `json:"type"`
	Routes    []*types.Route `json:"routes"`
	Addresses []Address      `json:"addresses"`
	DNS       types.DNS      `json:"dns"`
}

type Address struct {
	AddressStr string `json:"address"`
	Gateway    net.IP `json:"gateway,omitempty"`
	Address    net.IPNet
	Version    string
}

func main() {
	// TODO: implement plugin version
	skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
}

func cmdGet(args *skel.CmdArgs) error {
	// TODO: implement
	return fmt.Errorf("not implemented")
}

// canonicalizeIP makes sure a provided ip is in standard form
func canonicalizeIP(ip *net.IP) error {
	if ip.To4() != nil {
		*ip = ip.To4()
		return nil
	} else if ip.To16() != nil {
		*ip = ip.To16()
		return nil
	}
	return fmt.Errorf("IP %s not v4 nor v6", *ip)
}

// NewIPAMConfig creates a NetworkConfig from the given network name.
func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
	n := Net{}
	if err := json.Unmarshal(bytes, &n); err != nil {
		return nil, "", err
	}

	if n.IPAM == nil {
		return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
	}

	// Validate all ranges
	numV4 := 0
	numV6 := 0
	for i := range n.IPAM.Addresses {
		ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
		if err != nil {
			return nil, "", fmt.Errorf("invalid CIDR %s: %s", n.IPAM.Addresses[i].AddressStr, err)
		}
		n.IPAM.Addresses[i].Address = *addr
		n.IPAM.Addresses[i].Address.IP = ip

		if err := canonicalizeIP(&n.IPAM.Addresses[i].Address.IP); err != nil {
			return nil, "", fmt.Errorf("invalid address %d: %s", i, err)
		}

		if n.IPAM.Addresses[i].Address.IP.To4() != nil {
			n.IPAM.Addresses[i].Version = "4"
			numV4++
		} else {
			n.IPAM.Addresses[i].Version = "6"
			numV6++
		}
	}

	// CNI spec 0.2.0 and below supported only one v4 and v6 address
	if numV4 > 1 || numV6 > 1 {
		for _, v := range types020.SupportedVersions {
			if n.CNIVersion == v {
				return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
			}
		}
	}

	// Copy net name into IPAM so not to drag Net struct around
	n.IPAM.Name = n.Name

	return n.IPAM, n.CNIVersion, nil
}

func cmdAdd(args *skel.CmdArgs) error {
	ipamConf, confVersion, err := LoadIPAMConfig(args.StdinData, args.Args)
	if err != nil {
		return err
	}

	result := &current.Result{}
	result.DNS = ipamConf.DNS
	result.Routes = ipamConf.Routes
	for _, v := range ipamConf.Addresses {
		result.IPs = append(result.IPs, &current.IPConfig{
			Version: v.Version,
			Address: v.Address,
			Gateway: v.Gateway})
	}

	return types.PrintResult(result, confVersion)
}

func cmdDel(args *skel.CmdArgs) error {
	// Nothing required because of no resource allocation in static plugin.
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/sourcegraph/thesrc"
	"github.com/sourcegraph/thesrc/api"
	"github.com/sourcegraph/thesrc/app"
	"github.com/sourcegraph/thesrc/classifier"
	"github.com/sourcegraph/thesrc/datastore"
	"github.com/sourcegraph/thesrc/importer"
	"github.com/sourcegraph/thesrc/router"
)

var (
	baseURLStr = flag.String("url", "http://thesrc.org", "base URL of thesrc")
	baseURL    *url.URL
)

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, `thesrc is a web news and link server.

Usage:

        thesrc [options] command [arg...]

The commands are:
`)
		for _, c := range subcmds {
			fmt.Fprintf(os.Stderr, "    %-24s %s\n", c.name, c.description)
		}
		fmt.Fprintln(os.Stderr, `
Use "thesrc command -h" for more information about a command.

The options are:
`)
		flag.PrintDefaults()
		os.Exit(1)
	}
}

func main() {
	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
	}
	log.SetFlags(0)

	var err error
	baseURL, err = url.Parse(*baseURLStr)
	if err != nil {
		log.Fatal(err)
	}

	subcmd := flag.Arg(0)
	for _, c := range subcmds {
		if c.name == subcmd {
			c.run(flag.Args()[1:])
			return
		}
	}

	fmt.Fprintf(os.Stderr, "unknown subcmd %q\n", subcmd)
	fmt.Fprintln(os.Stderr, `Run "thesrc -h" for usage.`)
	os.Exit(1)
}

type subcmd struct {
	name        string
	description string
	run         func(args []string)
}

var subcmds = []subcmd{
	{"post", "submit a post", postCmd},
	{"import", "import posts from other sites", importCmd},
	{"classify", "classify posts", classifyCmd},
	{"serve", "start web server", serveCmd},
	{"createdb", "create the database schema", createDBCmd},
}

var apiclient = thesrc.NewClient(nil)

func postCmd(args []string) {
	fs := flag.NewFlagSet("post", flag.ExitOnError)
	title := fs.String("title", "", "title of post")
	linkURL := fs.String("link", "", "link URL")
	body := fs.String("body", "", "body of post")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc post [options]

Submits a post.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	if *title == "" {
		log.Fatal(`Title must not be empty. See "thesrc post -h" for usage.`)
	}
	if *linkURL == "" {
		log.Fatal(`Link URL must not be empty. See "thesrc post -h" for usage.`)
	}

	post := &thesrc.Post{
		Title:   *title,
		LinkURL: *linkURL,
		Body:    *body,
	}
	created, err := apiclient.Posts.Submit(post)
	if err != nil {
		log.Fatal(err)
	}

	if created {
		fmt.Print("created: ")
	} else {
		fmt.Print("exists:  ")
	}

	url, err := router.App().Get(router.Post).URL("ID", strconv.Itoa(post.ID))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(baseURL.ResolveReference(url))
}

func importCmd(args []string) {
	fs := flag.NewFlagSet("import", flag.ExitOnError)
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc import [options]

Imports posts from other sites.

The available sites are:
`)
		for _, f := range importer.Fetchers {
			fmt.Fprintln(os.Stderr, "  ", f.Site())
		}
		fmt.Fprintln(os.Stderr, `

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	var numTotal, numCreated int
	var mu sync.Mutex
	importer.Imported = func(site string, post *thesrc.Post, created bool) {
		mu.Lock()
		defer mu.Unlock()
		numTotal++
		if !created {
			return
		}
		fmt.Printf("%-12s  %-50s\n              %-60s\n", site, post.Title, post.LinkURL)
		numCreated++
	}

	datastore.Connect()
	var failed bool
	var wg sync.WaitGroup
	for _, f_ := range importer.Fetchers {
		f := f_
		wg.Add(1)
		go func() {
			defer wg.Done()
			if err := importer.Import(f); err != nil {
				log.Printf("Error fetching from %s: %s.", f.Site(), err)
				mu.Lock()
				failed = true
				mu.Unlock()
			}
		}()
	}
	wg.Wait()

	log.Printf("# import: %d new posts, %d already existed", numCreated, numTotal-numCreated)
	if failed {
		os.Exit(1)
	}
}

func classifyCmd(args []string) {
	fs := flag.NewFlagSet("classify", flag.ExitOnError)
	concurrency := fs.Int("c", 10, "concurrent classifiers")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc classify [options]

Classifies posts.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	var mu sync.Mutex
	summary := map[string]int{}

	workChan := make(chan *thesrc.Post)
	quitChan := make(chan struct{})
	for i := 0; i < *concurrency; i++ {
		go func() {
			for {
				select {
				case post := <-workChan:
					c, err := classifier.Classify(post)
					if err != nil {
						log.Printf("Error classifying %q: %s. (Continuing...)", post.LinkURL, err)
						continue
					}
					changed := firstWord(c) != firstWord(post.Classification)
					if changed {
						post.Classification = c
						if _, err := datastore.DBH.Update(post); err != nil {
							log.Fatal(err)
						}
						mu.Lock()
						summary[firstWord(post.Classification)]++
						mu.Unlock()
					}
					fmt.Printf("%v %-20s %s\n", changed, post.Classification, post.LinkURL)
				case <-quitChan:
					return
				}
			}
		}()
	}

	datastore.Connect()
	perPage := 100
	for pg := 1; true; pg++ {
		log.Println("Fetching more posts...")
		posts, err := apiclient.Posts.List(&thesrc.PostListOptions{ListOptions: thesrc.ListOptions{PerPage: perPage, Page: pg}})
		if err != nil {
			log.Fatal(err)
		}
		if len(posts) == 0 {
			break
		}

		for _, post := range posts {
			workChan <- post
		}
	}

	close(quitChan)

	fmt.Fprintf(os.Stderr, "# classified posts: %v\n", summary)
}

func firstWord(s string) string {
	i := strings.Index(s, " ")
	if i == -1 {
		return ""
	}
	return s[:i]
}

func serveCmd(args []string) {
	fs := flag.NewFlagSet("serve", flag.ExitOnError)
	httpAddr := fs.String("http", ":5000", "HTTP service address")
	templateDir := fs.String("tmpl-dir", app.TemplateDir, "template directory")
	staticDir := fs.String("static-dir", app.StaticDir, "static assets directory")
	reload := flag.Bool("reload", true, "reload templates on each request (dev mode)")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc serve [options]

Starts the web server that serves the app and API.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	app.StaticDir = *staticDir
	app.TemplateDir = *templateDir
	app.ReloadTemplates = *reload
	app.LoadTemplates()

	datastore.Connect()

	m := http.NewServeMux()
	m.Handle("/api/", http.StripPrefix("/api", api.Handler()))
	m.Handle("/", app.Handler())

	log.Print("Listening on ", *httpAddr)
	err := http.ListenAndServe(*httpAddr, m)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func createDBCmd(args []string) {
	fs := flag.NewFlagSet("createdb", flag.ExitOnError)
	drop := fs.Bool("drop", false, "drop DB before creating")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc createdb [options]

Creates the necessary DB tables and indexes.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	datastore.Connect()
	if *drop {
		datastore.Drop()
	}
	datastore.Create()
}
<commit_msg>add TODO about a post update endpoint<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/sourcegraph/thesrc"
	"github.com/sourcegraph/thesrc/api"
	"github.com/sourcegraph/thesrc/app"
	"github.com/sourcegraph/thesrc/classifier"
	"github.com/sourcegraph/thesrc/datastore"
	"github.com/sourcegraph/thesrc/importer"
	"github.com/sourcegraph/thesrc/router"
)

var (
	baseURLStr = flag.String("url", "http://thesrc.org", "base URL of thesrc")
	baseURL    *url.URL
)

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, `thesrc is a web news and link server.

Usage:

        thesrc [options] command [arg...]

The commands are:
`)
		for _, c := range subcmds {
			fmt.Fprintf(os.Stderr, "    %-24s %s\n", c.name, c.description)
		}
		fmt.Fprintln(os.Stderr, `
Use "thesrc command -h" for more information about a command.

The options are:
`)
		flag.PrintDefaults()
		os.Exit(1)
	}
}

func main() {
	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
	}
	log.SetFlags(0)

	var err error
	baseURL, err = url.Parse(*baseURLStr)
	if err != nil {
		log.Fatal(err)
	}

	subcmd := flag.Arg(0)
	for _, c := range subcmds {
		if c.name == subcmd {
			c.run(flag.Args()[1:])
			return
		}
	}

	fmt.Fprintf(os.Stderr, "unknown subcmd %q\n", subcmd)
	fmt.Fprintln(os.Stderr, `Run "thesrc -h" for usage.`)
	os.Exit(1)
}

type subcmd struct {
	name        string
	description string
	run         func(args []string)
}

var subcmds = []subcmd{
	{"post", "submit a post", postCmd},
	{"import", "import posts from other sites", importCmd},
	{"classify", "classify posts", classifyCmd},
	{"serve", "start web server", serveCmd},
	{"createdb", "create the database schema", createDBCmd},
}

var apiclient = thesrc.NewClient(nil)

func postCmd(args []string) {
	fs := flag.NewFlagSet("post", flag.ExitOnError)
	title := fs.String("title", "", "title of post")
	linkURL := fs.String("link", "", "link URL")
	body := fs.String("body", "", "body of post")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc post [options]

Submits a post.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	if *title == "" {
		log.Fatal(`Title must not be empty. See "thesrc post -h" for usage.`)
	}
	if *linkURL == "" {
		log.Fatal(`Link URL must not be empty. See "thesrc post -h" for usage.`)
	}

	post := &thesrc.Post{
		Title:   *title,
		LinkURL: *linkURL,
		Body:    *body,
	}
	created, err := apiclient.Posts.Submit(post)
	if err != nil {
		log.Fatal(err)
	}

	if created {
		fmt.Print("created: ")
	} else {
		fmt.Print("exists:  ")
	}

	url, err := router.App().Get(router.Post).URL("ID", strconv.Itoa(post.ID))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(baseURL.ResolveReference(url))
}

func importCmd(args []string) {
	fs := flag.NewFlagSet("import", flag.ExitOnError)
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc import [options]

Imports posts from other sites.

The available sites are:
`)
		for _, f := range importer.Fetchers {
			fmt.Fprintln(os.Stderr, "  ", f.Site())
		}
		fmt.Fprintln(os.Stderr, `

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	var numTotal, numCreated int
	var mu sync.Mutex
	importer.Imported = func(site string, post *thesrc.Post, created bool) {
		mu.Lock()
		defer mu.Unlock()
		numTotal++
		if !created {
			return
		}
		fmt.Printf("%-12s  %-50s\n              %-60s\n", site, post.Title, post.LinkURL)
		numCreated++
	}

	datastore.Connect()
	var failed bool
	var wg sync.WaitGroup
	for _, f_ := range importer.Fetchers {
		f := f_
		wg.Add(1)
		go func() {
			defer wg.Done()
			if err := importer.Import(f); err != nil {
				log.Printf("Error fetching from %s: %s.", f.Site(), err)
				mu.Lock()
				failed = true
				mu.Unlock()
			}
		}()
	}
	wg.Wait()

	log.Printf("# import: %d new posts, %d already existed", numCreated, numTotal-numCreated)
	if failed {
		os.Exit(1)
	}
}

func classifyCmd(args []string) {
	fs := flag.NewFlagSet("classify", flag.ExitOnError)
	concurrency := fs.Int("c", 10, "concurrent classifiers")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc classify [options]

Classifies posts.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	var mu sync.Mutex
	summary := map[string]int{}

	workChan := make(chan *thesrc.Post)
	quitChan := make(chan struct{})
	for i := 0; i < *concurrency; i++ {
		go func() {
			for {
				select {
				case post := <-workChan:
					c, err := classifier.Classify(post)
					if err != nil {
						log.Printf("Error classifying %q: %s. (Continuing...)", post.LinkURL, err)
						continue
					}
					changed := firstWord(c) != firstWord(post.Classification)
					if changed {
						post.Classification = c
						// TODO(sqs): add post update endpoint so we can run
						// `thesrc` against the HTTP API
						if _, err := datastore.DBH.Update(post); err != nil {
							log.Fatal(err)
						}
						mu.Lock()
						summary[firstWord(post.Classification)]++
						mu.Unlock()
					}
					fmt.Printf("%v %-20s %s\n", changed, post.Classification, post.LinkURL)
				case <-quitChan:
					return
				}
			}
		}()
	}

	datastore.Connect()
	perPage := 100
	for pg := 1; true; pg++ {
		log.Println("Fetching more posts...")
		posts, err := apiclient.Posts.List(&thesrc.PostListOptions{ListOptions: thesrc.ListOptions{PerPage: perPage, Page: pg}})
		if err != nil {
			log.Fatal(err)
		}
		if len(posts) == 0 {
			break
		}

		for _, post := range posts {
			workChan <- post
		}
	}

	close(quitChan)

	fmt.Fprintf(os.Stderr, "# classified posts: %v\n", summary)
}

func firstWord(s string) string {
	i := strings.Index(s, " ")
	if i == -1 {
		return ""
	}
	return s[:i]
}

func serveCmd(args []string) {
	fs := flag.NewFlagSet("serve", flag.ExitOnError)
	httpAddr := fs.String("http", ":5000", "HTTP service address")
	templateDir := fs.String("tmpl-dir", app.TemplateDir, "template directory")
	staticDir := fs.String("static-dir", app.StaticDir, "static assets directory")
	reload := flag.Bool("reload", true, "reload templates on each request (dev mode)")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc serve [options]

Starts the web server that serves the app and API.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	app.StaticDir = *staticDir
	app.TemplateDir = *templateDir
	app.ReloadTemplates = *reload
	app.LoadTemplates()

	datastore.Connect()

	m := http.NewServeMux()
	m.Handle("/api/", http.StripPrefix("/api", api.Handler()))
	m.Handle("/", app.Handler())

	log.Print("Listening on ", *httpAddr)
	err := http.ListenAndServe(*httpAddr, m)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func createDBCmd(args []string) {
	fs := flag.NewFlagSet("createdb", flag.ExitOnError)
	drop := fs.Bool("drop", false, "drop DB before creating")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: thesrc createdb [options]

Creates the necessary DB tables and indexes.

The options are:
`)
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	if fs.NArg() != 0 {
		fs.Usage()
	}

	datastore.Connect()
	if *drop {
		datastore.Drop()
	}
	datastore.Create()
}
<|endoftext|>


<commit_before>package surf

import (
	"bytes"
	"fmt"
	"github.com/headzoo/surf/browser"
	"github.com/headzoo/surf/jar"
	"github.com/headzoo/ut"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestGet(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	b := NewBrowser()
	var _ browser.Browsable = b

	err := b.Open(ts.URL)
	ut.AssertNil(err)
	ut.AssertEquals("Surf", b.Title())
	ut.AssertContains("
Hello, Surf!
", b.Body())

	buff := &bytes.Buffer{}
	l, err := b.Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, int(l))
	ut.AssertEquals(int(l), buff.Len())
}

func TestBookmarks(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	bookmarks := jar.NewMemoryBookmarks()
	b := NewBrowser()
	b.SetBookmarksJar(bookmarks)

	bookmarks.Save("test1", ts.URL)
	b.OpenBookmark("test1")
	ut.AssertEquals("Surf", b.Title())
	ut.AssertContains("
Hello, Surf!
", b.Body())

	err := b.BookmarkPage("test2")
	ut.AssertNil(err)
	b.OpenBookmark("test2")
	ut.AssertEquals("Surf", b.Title())
}

func TestClick(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/" {
			fmt.Fprint(w, htmlLinks)
		} else if r.URL.Path == "/page2" {
			fmt.Fprint(w, html)
		}
	}))
	defer ts.Close()

	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	err = b.Click("a:contains('click')")
	ut.AssertNil(err)
	ut.AssertContains("
Hello, Surf!
", b.Body())
}

func TestLinks(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, htmlLinks)
	}))
	defer ts.Close()

	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	links := b.Links()
	ut.AssertEquals(2, len(links))
	ut.AssertEquals("", links[0].ID)
	ut.AssertEquals(ts.URL+"/page2", links[0].URL.String())
	ut.AssertEquals("click", links[0].Text)
	ut.AssertEquals("page3", links[1].ID)
	ut.AssertEquals(ts.URL+"/page3", links[1].URL.String())
	ut.AssertEquals("no clicking", links[1].Text)
}

func TestImages(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	images := b.Images()
	ut.AssertEquals(2, len(images))
	ut.AssertEquals("imgur-image", images[0].ID)
	ut.AssertEquals("http://i.imgur.com/HW4bJtY.jpg", images[0].URL.String())
	ut.AssertEquals("", images[0].Alt)
	ut.AssertEquals("It's a...", images[0].Title)

	ut.AssertEquals("", images[1].ID)
	ut.AssertEquals(ts.URL+"/Cxagv.jpg", images[1].URL.String())
	ut.AssertEquals("A picture", images[1].Alt)
	ut.AssertEquals("", images[1].Title)

	buff := &bytes.Buffer{}
	l, err := images[0].Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, buff.Len())
	ut.AssertEquals(int(l), buff.Len())
}

func TestStylesheets(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()
	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	stylesheets := b.Stylesheets()
	ut.AssertEquals(2, len(stylesheets))
	ut.AssertEquals("http://godoc.org/-/site.css", stylesheets[0].URL.String())
	ut.AssertEquals("all", stylesheets[0].Media)
	ut.AssertEquals("text/css", stylesheets[0].Type)

	ut.AssertEquals(ts.URL+"/print.css", stylesheets[1].URL.String())
	ut.AssertEquals("print", stylesheets[1].Media)
	ut.AssertEquals("text/css", stylesheets[1].Type)

	buff := &bytes.Buffer{}
	l, err := stylesheets[0].Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, buff.Len())
	ut.AssertEquals(int(l), buff.Len())
}

func TestScripts(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()
	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	scripts := b.Scripts()
	ut.AssertEquals(2, len(scripts))
	ut.AssertEquals("http://godoc.org/-/site.js", scripts[0].URL.String())
	ut.AssertEquals("text/javascript", scripts[0].Type)

	ut.AssertEquals(ts.URL+"/jquery.min.js", scripts[1].URL.String())
	ut.AssertEquals("text/javascript", scripts[1].Type)

	buff := &bytes.Buffer{}
	l, err := scripts[0].Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, buff.Len())
	ut.AssertEquals(int(l), buff.Len())
}

var html = `<!doctype html>

		<title>Surf</title>
		<link href="/favicon.ico" rel="icon" type="image/x-icon">
		<link href="http://godoc.org/-/site.css" media="all" rel="stylesheet" type="text/css" />
		<link href="/print.css" rel="stylesheet" media="print" />

Hello, Surf!

`

var htmlLinks = `<!doctype html>

		<title>Surf</title>

Click the link below.

		click
		no clicking

`
<commit_msg>Formatting<commit_after>package surf

import (
	"bytes"
	"fmt"
	"github.com/headzoo/surf/browser"
	"github.com/headzoo/surf/jar"
	"github.com/headzoo/ut"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestGet(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	b := NewBrowser()
	var _ browser.Browsable = b

	err := b.Open(ts.URL)
	ut.AssertNil(err)
	ut.AssertEquals("Surf", b.Title())
	ut.AssertContains("
Hello, Surf!
", b.Body())

	buff := &bytes.Buffer{}
	l, err := b.Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, int(l))
	ut.AssertEquals(int(l), buff.Len())
}

func TestBookmarks(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	bookmarks := jar.NewMemoryBookmarks()
	b := NewBrowser()
	b.SetBookmarksJar(bookmarks)

	bookmarks.Save("test1", ts.URL)
	b.OpenBookmark("test1")
	ut.AssertEquals("Surf", b.Title())
	ut.AssertContains("
Hello, Surf!
", b.Body())

	err := b.BookmarkPage("test2")
	ut.AssertNil(err)
	b.OpenBookmark("test2")
	ut.AssertEquals("Surf", b.Title())
}

func TestClick(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/" {
			fmt.Fprint(w, html)
		} else if r.URL.Path == "/page2" {
			fmt.Fprint(w, html)
		}
	}))
	defer ts.Close()

	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	err = b.Click("a:contains('click')")
	ut.AssertNil(err)
	ut.AssertContains("
Hello, Surf!
", b.Body())
}

func TestLinks(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	links := b.Links()
	ut.AssertEquals(2, len(links))
	ut.AssertEquals("", links[0].ID)
	ut.AssertEquals(ts.URL+"/page2", links[0].URL.String())
	ut.AssertEquals("click", links[0].Text)
	ut.AssertEquals("page3", links[1].ID)
	ut.AssertEquals(ts.URL+"/page3", links[1].URL.String())
	ut.AssertEquals("no clicking", links[1].Text)
}

func TestImages(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()

	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	images := b.Images()
	ut.AssertEquals(2, len(images))
	ut.AssertEquals("imgur-image", images[0].ID)
	ut.AssertEquals("http://i.imgur.com/HW4bJtY.jpg", images[0].URL.String())
	ut.AssertEquals("", images[0].Alt)
	ut.AssertEquals("It's a...", images[0].Title)

	ut.AssertEquals("", images[1].ID)
	ut.AssertEquals(ts.URL+"/Cxagv.jpg", images[1].URL.String())
	ut.AssertEquals("A picture", images[1].Alt)
	ut.AssertEquals("", images[1].Title)

	buff := &bytes.Buffer{}
	l, err := images[0].Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, buff.Len())
	ut.AssertEquals(int(l), buff.Len())
}

func TestStylesheets(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()
	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	stylesheets := b.Stylesheets()
	ut.AssertEquals(2, len(stylesheets))
	ut.AssertEquals("http://godoc.org/-/site.css", stylesheets[0].URL.String())
	ut.AssertEquals("all", stylesheets[0].Media)
	ut.AssertEquals("text/css", stylesheets[0].Type)

	ut.AssertEquals(ts.URL+"/print.css", stylesheets[1].URL.String())
	ut.AssertEquals("print", stylesheets[1].Media)
	ut.AssertEquals("text/css", stylesheets[1].Type)

	buff := &bytes.Buffer{}
	l, err := stylesheets[0].Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, buff.Len())
	ut.AssertEquals(int(l), buff.Len())
}

func TestScripts(t *testing.T) {
	ut.Run(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(w, html)
	}))
	defer ts.Close()
	b := NewBrowser()
	err := b.Open(ts.URL)
	ut.AssertNil(err)

	scripts := b.Scripts()
	ut.AssertEquals(2, len(scripts))
	ut.AssertEquals("http://godoc.org/-/site.js", scripts[0].URL.String())
	ut.AssertEquals("text/javascript", scripts[0].Type)

	ut.AssertEquals(ts.URL+"/jquery.min.js", scripts[1].URL.String())
	ut.AssertEquals("text/javascript", scripts[1].Type)

	buff := &bytes.Buffer{}
	l, err := scripts[0].Download(buff)
	ut.AssertNil(err)
	ut.AssertGreaterThan(0, buff.Len())
	ut.AssertEquals(int(l), buff.Len())
}

var html = `<!doctype html>

		<title>Surf</title>
		<link href="/favicon.ico" rel="icon" type="image/x-icon">
		<link href="http://godoc.org/-/site.css" media="all" rel="stylesheet" type="text/css" />
		<link href="/print.css" rel="stylesheet" media="print" />

Hello, Surf!

Click the link below.

		click
		no clicking

`
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"strings"

	"github.com/spf13/viper"
)

var (
	defaultBaseURL   = "https://api.exercism.io/v1"
	defaultEndpoints = map[string]string{
		"download":      "/solutions/%s",
		"submit":        "/solutions/%s",
		"prepare-track": "/tracks/%s",
		"ping":          "/ping",
	}
)

// APIConfig provides API-specific configuration values.
type APIConfig struct {
	*Config
	BaseURL   string
	Endpoints map[string]string
}

// NewAPIConfig loads the config file in the config directory.
func NewAPIConfig() (*APIConfig, error) {
	cfg := NewEmptyAPIConfig()

	if err := cfg.Load(viper.New()); err != nil {
		return nil, err
	}

	cfg.SetDefaults()

	return cfg, nil
}

// SetDefaults ensures that we have all the necessary settings for the API.
func (cfg *APIConfig) SetDefaults() {
	if cfg.BaseURL == "" {
		cfg.BaseURL = defaultBaseURL
	}
	if cfg.Endpoints == nil {
		cfg.Endpoints = defaultEndpoints
		return
	}

	for key, endpoint := range defaultEndpoints {
		if cfg.Endpoints[key] == "" {
			cfg.Endpoints[key] = endpoint
		}
	}
}

// URL provides the API URL for a given endpoint key.
func (cfg *APIConfig) URL(key string, args ...interface{}) string {
	pattern := fmt.Sprintf("%s%s", cfg.BaseURL, cfg.Endpoints[key])
	if args != nil {
		return fmt.Sprintf(pattern, args...)
	}

	return pattern
}

// NewEmptyAPIConfig doesn't load the config from file or set default values.
func NewEmptyAPIConfig() *APIConfig {
	return &APIConfig{
		Config: New(Dir(), "api"),
	}
}

// Write stores the config to disk.
func (cfg *APIConfig) Write() error {
	cfg.BaseURL = strings.Trim(cfg.BaseURL, "/")
	cfg.SetDefaults()

	return Write(cfg)
}

// Load reads a viper configuration into the config.
func (cfg *APIConfig) Load(v *viper.Viper) error {
	cfg.readIn(v)
	return v.Unmarshal(&cfg)
}
<commit_msg>Swap conditional to put guard clause first<commit_after>package config

import (
	"fmt"
	"strings"

	"github.com/spf13/viper"
)

var (
	defaultBaseURL   = "https://api.exercism.io/v1"
	defaultEndpoints = map[string]string{
		"download":      "/solutions/%s",
		"submit":        "/solutions/%s",
		"prepare-track": "/tracks/%s",
		"ping":          "/ping",
	}
)

// APIConfig provides API-specific configuration values.
type APIConfig struct {
	*Config
	BaseURL   string
	Endpoints map[string]string
}

// NewAPIConfig loads the config file in the config directory.
func NewAPIConfig() (*APIConfig, error) {
	cfg := NewEmptyAPIConfig()

	if err := cfg.Load(viper.New()); err != nil {
		return nil, err
	}

	cfg.SetDefaults()

	return cfg, nil
}

// SetDefaults ensures that we have all the necessary settings for the API.
func (cfg *APIConfig) SetDefaults() {
	if cfg.BaseURL == "" {
		cfg.BaseURL = defaultBaseURL
	}
	if cfg.Endpoints == nil {
		cfg.Endpoints = defaultEndpoints
		return
	}

	for key, endpoint := range defaultEndpoints {
		if cfg.Endpoints[key] == "" {
			cfg.Endpoints[key] = endpoint
		}
	}
}

// URL provides the API URL for a given endpoint key.
func (cfg *APIConfig) URL(key string, args ...interface{}) string {
	pattern := fmt.Sprintf("%s%s", cfg.BaseURL, cfg.Endpoints[key])
	if args == nil {
		return pattern
	}
	return fmt.Sprintf(pattern, args...)
}

// NewEmptyAPIConfig doesn't load the config from file or set default values.
func NewEmptyAPIConfig() *APIConfig {
	return &APIConfig{
		Config: New(Dir(), "api"),
	}
}

// Write stores the config to disk.
func (cfg *APIConfig) Write() error {
	cfg.BaseURL = strings.Trim(cfg.BaseURL, "/")
	cfg.SetDefaults()

	return Write(cfg)
}

// Load reads a viper configuration into the config.
func (cfg *APIConfig) Load(v *viper.Viper) error {
	cfg.readIn(v)
	return v.Unmarshal(&cfg)
}
<|endoftext|>


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

/*
Package ssh in sshpiper is compatible with golang.org/x/crypto/ssh.
All func and datatype left unchanged. You can use it like golang.org/x/crypto/ssh.

Also, sshpiper provide additional APIs that allows you to build a piped ssh connection.

SSH is a transport security protocol, an authentication protocol and a
family of application protocols. The most typical application level
protocol is a remote shell and this is specifically implemented.  However,
the multiplexed nature of SSH is exposed to users that wish to support
others.

References:
  [PROTOCOL.certkeys]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys
  [SSH-PARAMETERS]:    http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
*/
package ssh
<commit_msg>minor fix in doc.go<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
SSHPiper provides additional APIs to ssh that allows you to build a piped ssh connection.
Also, package ssh in sshpiper is compatible with golang.org/x/crypto/ssh.
All func and datatype left unchanged. You can use it like golang.org/x/crypto/ssh.

SSH is a transport security protocol, an authentication protocol and a
family of application protocols. The most typical application level
protocol is a remote shell and this is specifically implemented.  However,
the multiplexed nature of SSH is exposed to users that wish to support
others.

References:
  [PROTOCOL.certkeys]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys
  [SSH-PARAMETERS]:    http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
*/
package ssh
<|endoftext|>


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

package ssh

import (
	"crypto"
	"crypto/rand"
	"fmt"
	"io"
	"sync"

	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
)

// These are string constants in the SSH protocol.
const (
	compressionNone = "none"
	serviceUserAuth = "ssh-userauth"
	serviceSSH      = "ssh-connection"
)

// supportedCiphers specifies the supported ciphers in preference order.
var defaultSupportedCiphers = []string{
	"aes128-ctr", "aes192-ctr", "aes256-ctr",
	"aes128-gcm@openssh.com",
	"arcfour256", "arcfour128",
}
var discouragedSupportedCiphers = []string{
	"aes128-cbc", "aes192-cbc", "aes256-cbc", "3des-cbc",
}
var supportedCiphers = append(defaultSupportedCiphers, discouragedSupportedCiphers...)

func AllSupportedCiphers() []string {
	return append(supportedCiphers)
}

// supportedKexAlgos specifies the supported key-exchange algorithms in
// preference order.
var supportedKexAlgos = []string{
	// P384 and P521 are not constant-time yet, but since we don't
	// reuse ephemeral keys, using them for ECDH should be OK.
	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
	kexAlgoDH14SHA1, kexAlgoDH1SHA1,
}

// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{
	CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
	CertAlgoECDSA384v01, CertAlgoECDSA521v01,

	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
	KeyAlgoRSA, KeyAlgoDSA,
}

// supportedMACs specifies a default set of MAC algorithms in preference order.
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
// because they have reached the end of their useful life.
var supportedMACs = []string{
	"hmac-sha1", "hmac-sha1-96",
}

var supportedCompressions = []string{compressionNone}

// hashFuncs keeps the mapping of supported algorithms to their respective
// hashes needed for signature verification.
var hashFuncs = map[string]crypto.Hash{
	KeyAlgoRSA:          crypto.SHA1,
	KeyAlgoDSA:          crypto.SHA1,
	KeyAlgoECDSA256:     crypto.SHA256,
	KeyAlgoECDSA384:     crypto.SHA384,
	KeyAlgoECDSA521:     crypto.SHA512,
	CertAlgoRSAv01:      crypto.SHA1,
	CertAlgoDSAv01:      crypto.SHA1,
	CertAlgoECDSA256v01: crypto.SHA256,
	CertAlgoECDSA384v01: crypto.SHA384,
	CertAlgoECDSA521v01: crypto.SHA512,
}

// unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted.
func unexpectedMessageError(expected, got uint8) error {
	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
}

// parseError results from a malformed SSH message.
func parseError(tag uint8) error {
	return fmt.Errorf("ssh: parse error in message type %d", tag)
}

func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) {
	for _, clientAlgo := range clientAlgos {
		for _, serverAlgo := range serverAlgos {
			if clientAlgo == serverAlgo {
				return clientAlgo, true
			}
		}
	}
	return
}

func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) {
	for _, clientCipher := range clientCiphers {
		for _, serverCipher := range serverCiphers {
			// reject the cipher if we have no cipherModes definition
			if clientCipher == serverCipher && cipherModes[clientCipher] != nil {
				return clientCipher, true
			}
		}
	}
	return
}

type directionAlgorithms struct {
	Cipher      string
	MAC         string
	Compression string
}

type algorithms struct {
	kex     string
	hostKey string
	w       directionAlgorithms
	r       directionAlgorithms
}

func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms) {
	var ok bool
	result := &algorithms{}
	result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos)
	if !ok {
		return
	}

	result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
	if !ok {
		return
	}

	result.w.Cipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
	if !ok {
		return
	}

	result.r.Cipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
	if !ok {
		return
	}

	result.w.MAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
	if !ok {
		return
	}

	result.r.MAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
	if !ok {
		return
	}

	result.w.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
	if !ok {
		return
	}

	result.r.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
	if !ok {
		return
	}

	return result
}

// If rekeythreshold is too small, we can't make any progress sending
// stuff.
const minRekeyThreshold uint64 = 256

// Config contains configuration data common to both ServerConfig and
// ClientConfig.
type Config struct {
	// Rand provides the source of entropy for cryptographic
	// primitives. If Rand is nil, the cryptographic random reader
	// in package crypto/rand will be used.
	Rand io.Reader

	// The maximum number of bytes sent or received after which a
	// new key is negotiated. It must be at least 256. If
	// unspecified, 1 gigabyte is used.
	RekeyThreshold uint64

	// The allowed key exchanges algorithms. If unspecified then a
	// default set of algorithms is used.
	KeyExchanges []string

	// The allowed cipher algorithms. If unspecified then a sensible
	// default is used.
	Ciphers []string

	// The allowed MAC algorithms. If unspecified then a sensible default
	// is used.
	MACs []string
}

// SetDefaults sets sensible values for unset fields in config. This is
// exported for testing: Configs passed to SSH functions are copied and have
// default values set automatically.
func (c *Config) SetDefaults() {
	if c.Rand == nil {
		c.Rand = rand.Reader
	}
	if c.Ciphers == nil {
		c.Ciphers = defaultSupportedCiphers
	}

	if c.KeyExchanges == nil {
		c.KeyExchanges = supportedKexAlgos
	}

	if c.MACs == nil {
		c.MACs = supportedMACs
	}

	if c.RekeyThreshold == 0 {
		// RFC 4253, section 9 suggests rekeying after 1G.
		c.RekeyThreshold = 1 << 30
	}
	if c.RekeyThreshold < minRekeyThreshold {
		c.RekeyThreshold = minRekeyThreshold
	}
}

// buildDataSignedForAuth returns the data that is signed in order to prove
// possession of a private key. See RFC 4252, section 7.
func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
	data := struct {
		Session []byte
		Type    byte
		User    string
		Service string
		Method  string
		Sign    bool
		Algo    []byte
		PubKey  []byte
	}{
		sessionId,
		msgUserAuthRequest,
		req.User,
		req.Service,
		req.Method,
		true,
		algo,
		pubKey,
	}
	return Marshal(data)
}

func appendU16(buf []byte, n uint16) []byte {
	return append(buf, byte(n>>8), byte(n))
}

func appendU32(buf []byte, n uint32) []byte {
	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}

func appendU64(buf []byte, n uint64) []byte {
	return append(buf,
		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
		byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}

func appendInt(buf []byte, n int) []byte {
	return appendU32(buf, uint32(n))
}

func appendString(buf []byte, s string) []byte {
	buf = appendU32(buf, uint32(len(s)))
	buf = append(buf, s...)
	return buf
}

func appendBool(buf []byte, b bool) []byte {
	if b {
		return append(buf, 1)
	}
	return append(buf, 0)
}

// newCond is a helper to hide the fact that there is no usable zero
// value for sync.Cond.
func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }

// window represents the buffer available to clients
// wishing to write to a channel.
type window struct {
	*sync.Cond
	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
	writeWaiters int
	closed       bool
}

// add adds win to the amount of window available
// for consumers.
func (w *window) add(win uint32) bool {
	// a zero sized window adjust is a noop.
	if win == 0 {
		return true
	}
	w.L.Lock()
	if w.win+win < win {
		w.L.Unlock()
		return false
	}
	w.win += win
	// It is unusual that multiple goroutines would be attempting to reserve
	// window space, but not guaranteed. Use broadcast to notify all waiters
	// that additional window is available.
	w.Broadcast()
	w.L.Unlock()
	return true
}

// close sets the window to closed, so all reservations fail
// immediately.
func (w *window) close() {
	w.L.Lock()
	w.closed = true
	w.Broadcast()
	w.L.Unlock()
}

// reserve reserves win from the available window capacity.
// If no capacity remains, reserve will block. reserve may
// return less than requested.
func (w *window) reserve(win uint32) (uint32, error) {
	var err error
	w.L.Lock()
	w.writeWaiters++
	w.Broadcast()
	for w.win == 0 && !w.closed {
		w.Wait()
	}
	w.writeWaiters--
	if w.win < win {
		win = w.win
	}
	w.win -= win
	if w.closed {
		err = io.EOF
	}
	w.L.Unlock()
	return win, err
}

// waitWriterBlocked waits until some goroutine is blocked for further
// writes. It is used in tests only.
func (w *window) waitWriterBlocked() {
	w.Cond.L.Lock()
	for w.writeWaiters == 0 {
		w.Cond.Wait()
	}
	w.Cond.L.Unlock()
}
<commit_msg>Add hmac-md5 to the list of supported MACs<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssh

import (
	"crypto"
	"crypto/rand"
	"fmt"
	"io"
	"sync"

	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
)

// These are string constants in the SSH protocol.
const (
	compressionNone = "none"
	serviceUserAuth = "ssh-userauth"
	serviceSSH      = "ssh-connection"
)

// supportedCiphers specifies the supported ciphers in preference order.
var defaultSupportedCiphers = []string{
	"aes128-ctr", "aes192-ctr", "aes256-ctr",
	"aes128-gcm@openssh.com",
	"arcfour256", "arcfour128",
}
var discouragedSupportedCiphers = []string{
	"aes128-cbc", "aes192-cbc", "aes256-cbc", "3des-cbc",
}
var supportedCiphers = append(defaultSupportedCiphers, discouragedSupportedCiphers...)

func AllSupportedCiphers() []string {
	return append(supportedCiphers)
}

// supportedKexAlgos specifies the supported key-exchange algorithms in
// preference order.
var supportedKexAlgos = []string{
	// P384 and P521 are not constant-time yet, but since we don't
	// reuse ephemeral keys, using them for ECDH should be OK.
	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
	kexAlgoDH14SHA1, kexAlgoDH1SHA1,
}

// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{
	CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
	CertAlgoECDSA384v01, CertAlgoECDSA521v01,

	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
	KeyAlgoRSA, KeyAlgoDSA,
}

// supportedMACs specifies a default set of MAC algorithms in preference order.
// This is based on RFC 4253, section 6.4
var supportedMACs = []string{
	"hmac-sha1", "hmac-sha1-96", "hmac-md5",
}

var supportedCompressions = []string{compressionNone}

// hashFuncs keeps the mapping of supported algorithms to their respective
// hashes needed for signature verification.
var hashFuncs = map[string]crypto.Hash{
	KeyAlgoRSA:          crypto.SHA1,
	KeyAlgoDSA:          crypto.SHA1,
	KeyAlgoECDSA256:     crypto.SHA256,
	KeyAlgoECDSA384:     crypto.SHA384,
	KeyAlgoECDSA521:     crypto.SHA512,
	CertAlgoRSAv01:      crypto.SHA1,
	CertAlgoDSAv01:      crypto.SHA1,
	CertAlgoECDSA256v01: crypto.SHA256,
	CertAlgoECDSA384v01: crypto.SHA384,
	CertAlgoECDSA521v01: crypto.SHA512,
}

// unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted.
func unexpectedMessageError(expected, got uint8) error {
	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
}

// parseError results from a malformed SSH message.
func parseError(tag uint8) error {
	return fmt.Errorf("ssh: parse error in message type %d", tag)
}

func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) {
	for _, clientAlgo := range clientAlgos {
		for _, serverAlgo := range serverAlgos {
			if clientAlgo == serverAlgo {
				return clientAlgo, true
			}
		}
	}
	return
}

func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) {
	for _, clientCipher := range clientCiphers {
		for _, serverCipher := range serverCiphers {
			// reject the cipher if we have no cipherModes definition
			if clientCipher == serverCipher && cipherModes[clientCipher] != nil {
				return clientCipher, true
			}
		}
	}
	return
}

type directionAlgorithms struct {
	Cipher      string
	MAC         string
	Compression string
}

type algorithms struct {
	kex     string
	hostKey string
	w       directionAlgorithms
	r       directionAlgorithms
}

func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms) {
	var ok bool
	result := &algorithms{}
	result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos)
	if !ok {
		return
	}

	result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
	if !ok {
		return
	}

	result.w.Cipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
	if !ok {
		return
	}

	result.r.Cipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
	if !ok {
		return
	}

	result.w.MAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
	if !ok {
		return
	}

	result.r.MAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
	if !ok {
		return
	}

	result.w.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
	if !ok {
		return
	}

	result.r.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
	if !ok {
		return
	}

	return result
}

// If rekeythreshold is too small, we can't make any progress sending
// stuff.
const minRekeyThreshold uint64 = 256

// Config contains configuration data common to both ServerConfig and
// ClientConfig.
type Config struct {
	// Rand provides the source of entropy for cryptographic
	// primitives. If Rand is nil, the cryptographic random reader
	// in package crypto/rand will be used.
	Rand io.Reader

	// The maximum number of bytes sent or received after which a
	// new key is negotiated. It must be at least 256. If
	// unspecified, 1 gigabyte is used.
	RekeyThreshold uint64

	// The allowed key exchanges algorithms. If unspecified then a
	// default set of algorithms is used.
	KeyExchanges []string

	// The allowed cipher algorithms. If unspecified then a sensible
	// default is used.
	Ciphers []string

	// The allowed MAC algorithms. If unspecified then a sensible default
	// is used.
	MACs []string
}

// SetDefaults sets sensible values for unset fields in config. This is
// exported for testing: Configs passed to SSH functions are copied and have
// default values set automatically.
func (c *Config) SetDefaults() {
	if c.Rand == nil {
		c.Rand = rand.Reader
	}
	if c.Ciphers == nil {
		c.Ciphers = defaultSupportedCiphers
	}

	if c.KeyExchanges == nil {
		c.KeyExchanges = supportedKexAlgos
	}

	if c.MACs == nil {
		c.MACs = supportedMACs
	}

	if c.RekeyThreshold == 0 {
		// RFC 4253, section 9 suggests rekeying after 1G.
		c.RekeyThreshold = 1 << 30
	}
	if c.RekeyThreshold < minRekeyThreshold {
		c.RekeyThreshold = minRekeyThreshold
	}
}

// buildDataSignedForAuth returns the data that is signed in order to prove
// possession of a private key. See RFC 4252, section 7.
func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
	data := struct {
		Session []byte
		Type    byte
		User    string
		Service string
		Method  string
		Sign    bool
		Algo    []byte
		PubKey  []byte
	}{
		sessionId,
		msgUserAuthRequest,
		req.User,
		req.Service,
		req.Method,
		true,
		algo,
		pubKey,
	}
	return Marshal(data)
}

func appendU16(buf []byte, n uint16) []byte {
	return append(buf, byte(n>>8), byte(n))
}

func appendU32(buf []byte, n uint32) []byte {
	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}

func appendU64(buf []byte, n uint64) []byte {
	return append(buf,
		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
		byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}

func appendInt(buf []byte, n int) []byte {
	return appendU32(buf, uint32(n))
}

func appendString(buf []byte, s string) []byte {
	buf = appendU32(buf, uint32(len(s)))
	buf = append(buf, s...)
	return buf
}

func appendBool(buf []byte, b bool) []byte {
	if b {
		return append(buf, 1)
	}
	return append(buf, 0)
}

// newCond is a helper to hide the fact that there is no usable zero
// value for sync.Cond.
func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }

// window represents the buffer available to clients
// wishing to write to a channel.
type window struct {
	*sync.Cond
	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
	writeWaiters int
	closed       bool
}

// add adds win to the amount of window available
// for consumers.
func (w *window) add(win uint32) bool {
	// a zero sized window adjust is a noop.
	if win == 0 {
		return true
	}
	w.L.Lock()
	if w.win+win < win {
		w.L.Unlock()
		return false
	}
	w.win += win
	// It is unusual that multiple goroutines would be attempting to reserve
	// window space, but not guaranteed. Use broadcast to notify all waiters
	// that additional window is available.
	w.Broadcast()
	w.L.Unlock()
	return true
}

// close sets the window to closed, so all reservations fail
// immediately.
func (w *window) close() {
	w.L.Lock()
	w.closed = true
	w.Broadcast()
	w.L.Unlock()
}

// reserve reserves win from the available window capacity.
// If no capacity remains, reserve will block. reserve may
// return less than requested.
func (w *window) reserve(win uint32) (uint32, error) {
	var err error
	w.L.Lock()
	w.writeWaiters++
	w.Broadcast()
	for w.win == 0 && !w.closed {
		w.Wait()
	}
	w.writeWaiters--
	if w.win < win {
		win = w.win
	}
	w.win -= win
	if w.closed {
		err = io.EOF
	}
	w.L.Unlock()
	return win, err
}

// waitWriterBlocked waits until some goroutine is blocked for further
// writes. It is used in tests only.
func (w *window) waitWriterBlocked() {
	w.Cond.L.Lock()
	for w.writeWaiters == 0 {
		w.Cond.Wait()
	}
	w.Cond.L.Unlock()
}
<|endoftext|>


<commit_before>package ssh

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"

	"github.com/blacknon/lssh/conf"
	termbox "github.com/nsf/termbox-go"
)

//var (
//	stdin []byte
//)

type ConInfoCmd struct {
	Index           int
	Count           int
	Cmd             []string
	Server          string
	ServerMaxLength int
	Addr            string
	Port            string
	User            string
	Pass            string
	KeyPath         string
	Flag            ConInfoCmdFlag

	StdinTempPath string
}

type ConInfoCmdFlag struct {
	PesudoTerm bool
	Parallel   bool
}

func getTmpName() string {
	var n uint64
	binary.Read(rand.Reader, binary.LittleEndian, &n)
	return strconv.FormatUint(n, 36) + ".lssh.tmp"
}

func outColorStrings(num int, inStrings string) (str string) {
	color := num % 5
	switch color {
	// Red
	case 1:
		str = fmt.Sprintf("\x1b[31m%s\x1b[0m", inStrings)
	// Yellow
	case 2:
		str = fmt.Sprintf("\x1b[33m%s\x1b[0m", inStrings)
	// Blue
	case 3:
		str = fmt.Sprintf("\x1b[34m%s\x1b[0m", inStrings)
	// Magenta
	case 4:
		str = fmt.Sprintf("\x1b[35m%s\x1b[0m", inStrings)
	// Cyan
	case 0:
		str = fmt.Sprintf("\x1b[36m%s\x1b[0m", inStrings)
	}
	return
}

// exec ssh command function
func (c *ConInfoCmd) Run() int {
	auth := []ssh.AuthMethod{}
	if c.KeyPath != "" {
		// Read PublicKey
		buffer, err := ioutil.ReadFile(c.KeyPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error:%s%n", err)
			return 1
		}
		key, err := ssh.ParsePrivateKey(buffer)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error:%s%n", err)
			return 1
		}
		auth = []ssh.AuthMethod{ssh.PublicKeys(key)}
	} else {
		auth = []ssh.AuthMethod{ssh.Password(c.Pass)}
	}

	config := &ssh.ClientConfig{
		User:            c.User,
		Auth:            auth,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         60 * time.Second,
	}

	// New connect
	conn, err := ssh.Dial("tcp", c.Addr+":"+c.Port, config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot connect %v: %v \n", c.Port, err)
		return 1
	}
	defer conn.Close()

	// New Session
	session, err := conn.NewSession()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot open new session: %v \n", err)
		return 1
	}
	defer session.Close()
	go func() {
		time.Sleep(2419200 * time.Second)
		conn.Close()
	}()

	// if PesudoTerm Enable
	if c.Flag.PesudoTerm == true {
		modes := ssh.TerminalModes{
			ssh.ECHO:          0,
			ssh.TTY_OP_ISPEED: 14400,
			ssh.TTY_OP_OSPEED: 14400,
		}

		// Get terminal window size
		if err := termbox.Init(); err != nil {
			panic(err)
		}
		width, hight := termbox.Size()
		termbox.Close()

		if err := session.RequestPty("xterm", hight, width, modes); err != nil {
			session.Close()
			fmt.Errorf("request for pseudo terminal failed: %s \n", err)
		}
	}

	// stdin tmp file Open.
	stdinTempRead, _ := os.OpenFile(c.StdinTempPath, os.O_RDONLY, 0600)
	session.Stdin = stdinTempRead

	// exec command join
	execCmd := strings.Join(c.Cmd, " ")

	if c.Count == 1 {
		session.Stdout = os.Stdout
		session.Stderr = os.Stderr

		err = session.Run(execCmd)

		if err != nil {
			fmt.Fprint(os.Stderr, err)
			if ee, ok := err.(*ssh.ExitError); ok {
				return ee.ExitStatus()
			}
		}
		return 0
	} else if c.Flag.Parallel == true {
		// stdout and stderr to stdoutBuf
		var stdoutBuf bytes.Buffer
		session.Stdout = &stdoutBuf
		session.Stderr = &stdoutBuf

		// cmdStatus: Chan can not continuously read buffer in for.
		//                For this reason, the processing end is detected using a variable.
		cmdStatus := true

		// Exec Command(parallel)
		go func() {
			err = session.Run(execCmd)
			cmdStatus = false
		}()

		// var "x" is Readed Byte position from Buffer
		x := 0
		for {
			time.Sleep(100 * time.Millisecond)

			stdoutBufToStr := stdoutBuf.String()

			if len(stdoutBufToStr) == 0 {
				continue
			}

			stdoutBufToByte := []byte(stdoutBufToStr)
			stdoutBufArray := regexp.MustCompile("\r\n|\n\r|\n|\r").Split(string(stdoutBufToByte[x:]), -1)
			x = len(stdoutBufToStr)

			for i, v := range stdoutBufArray {
				if i == len(stdoutBufArray)-1 {
					break
				}
				lineHeader := fmt.Sprintf("%-*s", c.ServerMaxLength, c.Server)
				fmt.Println(outColorStrings(c.Index, lineHeader)+":: ", v)
			}

			if cmdStatus == true {
				continue
			} else {
				break
			}
		}

		if err != nil {
			fmt.Fprint(os.Stderr, err)
			if ee, ok := err.(*ssh.ExitError); ok {
				return ee.ExitStatus()
			}
		}
	} else {
		// stdout and stderr to stdoutBuf
		var stdoutBuf bytes.Buffer
		session.Stdout = &stdoutBuf
		session.Stderr = &stdoutBuf

		// Exec Command(for loop)
		err = session.Run(execCmd)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			if ee, ok := err.(*ssh.ExitError); ok {
				return ee.ExitStatus()
			}
		}

		stdoutBufArray := regexp.MustCompile("\r\n|\n\r|\n|\r").Split(stdoutBuf.String(), -1)
		for i, v := range stdoutBufArray {
			if i == len(stdoutBufArray)-1 {
				break
			}
			lineHeader := fmt.Sprintf("%-*s", c.ServerMaxLength, c.Server)
			fmt.Println(outColorStrings(c.Index, lineHeader)+":: ", v)
		}
	}
	return 0
}

// remote ssh server exec command only
func ConSshCmd(serverList []string, confList conf.Config, tFlag bool, pFlag bool, execCmd ...string) int {
	// Create tmp file
	stdinTemp, err := ioutil.TempFile("", getTmpName())
	if err != nil {
		panic(err)
	}
	defer os.Remove(stdinTemp.Name())

	// Stdin only pipes
	if terminal.IsTerminal(syscall.Stdin) == false {
		io.Copy(stdinTemp, os.Stdin)
	}

	// get connect server name max length
	conServerNameMax := 0
	for _, conServerName := range serverList {
		if conServerNameMax < len(conServerName) {
			conServerNameMax = len(conServerName)
		}
	}

	//if conServerCnt > 1 {
	finished := make(chan bool)

	// for command exec
	x := 1
	for _, v := range serverList {
		y := x
		c := new(ConInfoCmd)
		conServer := v
		go func() {
			c.StdinTempPath = stdinTemp.Name()

			c.Index = y
			c.Count = len(serverList)
			c.Server = conServer
			c.ServerMaxLength = conServerNameMax
			c.Addr = confList.Server[c.Server].Addr
			c.User = confList.Server[c.Server].User
			c.Port = "22"
			if confList.Server[c.Server].Port != "" {
				c.Port = confList.Server[c.Server].Port
			}
			c.Pass = ""
			if confList.Server[c.Server].Pass != "" {
				c.Pass = confList.Server[c.Server].Pass
			}
			c.KeyPath = ""
			if confList.Server[c.Server].Key != "" {
				c.KeyPath = confList.Server[c.Server].Key
			}
			c.Cmd = execCmd

			c.Run()

			finished <- true
		}()
		x++
	}

	for i := 1; i <= len(serverList); i++ {
		<-finished
	}
	return 0
}
<commit_msg>delete stdin temp file<commit_after>package ssh

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"

	"github.com/blacknon/lssh/conf"
	termbox "github.com/nsf/termbox-go"
)

var (
	stdin []byte
)

type ConInfoCmd struct {
	Index           int
	Count           int
	Cmd             []string
	Server          string
	ServerMaxLength int
	Addr            string
	Port            string
	User            string
	Pass            string
	KeyPath         string
	Flag            ConInfoCmdFlag

	StdinTempPath string
}

type ConInfoCmdFlag struct {
	PesudoTerm bool
	Parallel   bool
}

func outColorStrings(num int, inStrings string) (str string) {
	color := num % 5
	switch color {
	// Red
	case 1:
		str = fmt.Sprintf("\x1b[31m%s\x1b[0m", inStrings)
	// Yellow
	case 2:
		str = fmt.Sprintf("\x1b[33m%s\x1b[0m", inStrings)
	// Blue
	case 3:
		str = fmt.Sprintf("\x1b[34m%s\x1b[0m", inStrings)
	// Magenta
	case 4:
		str = fmt.Sprintf("\x1b[35m%s\x1b[0m", inStrings)
	// Cyan
	case 0:
		str = fmt.Sprintf("\x1b[36m%s\x1b[0m", inStrings)
	}
	return
}

// exec ssh command function
func (c *ConInfoCmd) Run() int {
	auth := []ssh.AuthMethod{}
	if c.KeyPath != "" {
		// Read PublicKey
		buffer, err := ioutil.ReadFile(c.KeyPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error:%s%n", err)
			return 1
		}
		key, err := ssh.ParsePrivateKey(buffer)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error:%s%n", err)
			return 1
		}
		auth = []ssh.AuthMethod{ssh.PublicKeys(key)}
	} else {
		auth = []ssh.AuthMethod{ssh.Password(c.Pass)}
	}

	config := &ssh.ClientConfig{
		User:            c.User,
		Auth:            auth,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         60 * time.Second,
	}

	// New connect
	conn, err := ssh.Dial("tcp", c.Addr+":"+c.Port, config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot connect %v: %v \n", c.Port, err)
		return 1
	}
	defer conn.Close()

	// New Session
	session, err := conn.NewSession()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot open new session: %v \n", err)
		return 1
	}
	defer session.Close()
	go func() {
		time.Sleep(2419200 * time.Second)
		conn.Close()
	}()

	// if PesudoTerm Enable
	if c.Flag.PesudoTerm == true {
		modes := ssh.TerminalModes{
			ssh.ECHO:          0,
			ssh.TTY_OP_ISPEED: 14400,
			ssh.TTY_OP_OSPEED: 14400,
		}

		// Get terminal window size
		if err := termbox.Init(); err != nil {
			panic(err)
		}
		width, hight := termbox.Size()
		termbox.Close()

		if err := session.RequestPty("xterm", hight, width, modes); err != nil {
			session.Close()
			fmt.Errorf("request for pseudo terminal failed: %s \n", err)
		}
	}

	// get stdin
	session.Stdin = bytes.NewReader(stdin)

	// exec command join
	execCmd := strings.Join(c.Cmd, " ")

	if c.Count == 1 {
		session.Stdout = os.Stdout
		session.Stderr = os.Stderr

		err = session.Run(execCmd)

		if err != nil {
			fmt.Fprint(os.Stderr, err)
			if ee, ok := err.(*ssh.ExitError); ok {
				return ee.ExitStatus()
			}
		}
		return 0
	} else if c.Flag.Parallel == true {
		// stdout and stderr to stdoutBuf
		var stdoutBuf bytes.Buffer
		session.Stdout = &stdoutBuf
		session.Stderr = &stdoutBuf

		// cmdStatus: Chan can not continuously read buffer in for.
		//                For this reason, the processing end is detected using a variable.
		cmdStatus := true

		// Exec Command(parallel)
		go func() {
			err = session.Run(execCmd)
			cmdStatus = false
		}()

		// var "x" is Readed Byte position from Buffer
		x := 0
		for {
			time.Sleep(100 * time.Millisecond)

			stdoutBufToStr := stdoutBuf.String()

			if len(stdoutBufToStr) == 0 {
				continue
			}

			stdoutBufToByte := []byte(stdoutBufToStr)
			stdoutBufArray := regexp.MustCompile("\r\n|\n\r|\n|\r").Split(string(stdoutBufToByte[x:]), -1)
			x = len(stdoutBufToStr)

			for i, v := range stdoutBufArray {
				if i == len(stdoutBufArray)-1 {
					break
				}
				lineHeader := fmt.Sprintf("%-*s", c.ServerMaxLength, c.Server)
				fmt.Println(outColorStrings(c.Index, lineHeader)+":: ", v)
			}

			if cmdStatus == true {
				continue
			} else {
				break
			}
		}

		if err != nil {
			fmt.Fprint(os.Stderr, err)
			if ee, ok := err.(*ssh.ExitError); ok {
				return ee.ExitStatus()
			}
		}
	} else {
		// stdout and stderr to stdoutBuf
		var stdoutBuf bytes.Buffer
		session.Stdout = &stdoutBuf
		session.Stderr = &stdoutBuf

		// Exec Command(for loop)
		err = session.Run(execCmd)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			if ee, ok := err.(*ssh.ExitError); ok {
				return ee.ExitStatus()
			}
		}

		stdoutBufArray := regexp.MustCompile("\r\n|\n\r|\n|\r").Split(stdoutBuf.String(), -1)
		for i, v := range stdoutBufArray {
			if i == len(stdoutBufArray)-1 {
				break
			}
			lineHeader := fmt.Sprintf("%-*s", c.ServerMaxLength, c.Server)
			fmt.Println(outColorStrings(c.Index, lineHeader)+":: ", v)
		}
	}
	return 0
}

// remote ssh server exec command only
func ConSshCmd(serverList []string, confList conf.Config, tFlag bool, pFlag bool, execCmd ...string) int {
	// Stdin only pipes
	if terminal.IsTerminal(syscall.Stdin) == false {
		stdin, _ = ioutil.ReadAll(os.Stdin)
	}

	// get connect server name max length
	conServerNameMax := 0
	for _, conServerName := range serverList {
		if conServerNameMax < len(conServerName) {
			conServerNameMax = len(conServerName)
		}
	}

	//if conServerCnt > 1 {
	finished := make(chan bool)

	// for command exec
	x := 1
	for _, v := range serverList {
		y := x
		c := new(ConInfoCmd)
		conServer := v
		go func() {
			c.Index = y
			c.Count = len(serverList)
			c.Server = conServer
			c.ServerMaxLength = conServerNameMax
			c.Addr = confList.Server[c.Server].Addr
			c.User = confList.Server[c.Server].User
			c.Port = "22"
			if confList.Server[c.Server].Port != "" {
				c.Port = confList.Server[c.Server].Port
			}
			c.Pass = ""
			if confList.Server[c.Server].Pass != "" {
				c.Pass = confList.Server[c.Server].Pass
			}
			c.KeyPath = ""
			if confList.Server[c.Server].Key != "" {
				c.KeyPath = confList.Server[c.Server].Key
			}
			c.Cmd = execCmd

			c.Run()
			finished <- true
		}()
		x++
	}

	for i := 1; i <= len(serverList); i++ {
		<-finished
	}
	return 0
}
<|endoftext|>


<commit_before>package ssh2docker

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/kr/pty"
	"golang.org/x/crypto/ssh"
)

type Server struct {
	sshConfig *ssh.ServerConfig
}

func NewServer() (*Server, error) {
	server := Server{}
	server.sshConfig = &ssh.ServerConfig{
		PasswordCallback: server.PasswordCallback,
	}
	return &server, nil
}

func (s *Server) AddHostKeyFile(keypath string) error {
	keystring, err := ioutil.ReadFile("host_rsa")
	if err != nil {
		return err
	}

	hostkey, err := ssh.ParsePrivateKey(keystring)
	if err != nil {
		return err
	}

	s.sshConfig.AddHostKey(hostkey)
	return nil
}

func (s *Server) PasswordCallback(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
	return nil, nil
}

func (s *Server) Handle(netConn net.Conn) error {
	conn, chans, reqs, err := ssh.NewServerConn(netConn, s.sshConfig)
	if err != nil {
		logrus.Errorf("%s", err)
		return err
	}

	logrus.Infof("conn: User=%q, ClientVersion=%1", conn.User(), fmt.Sprintf("%x", conn.ClientVersion()))

	go func(in <-chan *ssh.Request) {
		for req := range in {
			if req.WantReply {
				req.Reply(false, nil)
			}
		}
	}(reqs)

	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			logrus.Errorf("%s", err)
			return err
		}

		logrus.Infof("Creating pty...")
		f, tty, err := pty.Open()
		if err != nil {
			logrus.Errorf("%s", err)
			continue
		}

		termEnv := "xterm"

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false
				switch req.Type {
				case "shell":
					if len(req.Payload) != 0 {
						break
					}
					ok = true

					args := []string{"run", "-it", "--rm", conn.User(), "/bin/sh"}
					logrus.Infof("Executing docker %s", args)
					cmd := exec.Command("docker", args...)
					cmd.Env = []string{
						"TERM=" + termEnv,
						"DOCKER_HOST=" + os.Getenv("DOCKER_HOST"),
						"DOCKER_CERT_PATH=" + os.Getenv("DOCKER_CERT_PATH"),
						"DOCKER_TLS_VERIFY=" + os.Getenv("DOCKER_TLS_VERIFY"),
					}

					defer tty.Close()
					cmd.Stdout = tty
					cmd.Stdin = tty
					cmd.Stderr = tty
					cmd.SysProcAttr = &syscall.SysProcAttr{
						Setctty: true,
						Setsid:  true,
					}

					err := cmd.Start()
					if err != nil {
						logrus.Warnf("%s", err)
						continue
					}

					var once sync.Once
					close := func() {
						channel.Close()
						logrus.Infof("session closed")
					}

					go func() {
						io.Copy(channel, f)
						once.Do(close)
					}()

					go func() {
						io.Copy(f, channel)
						once.Do(close)
					}()

				case "pty-req":
					ok = true
					termLen := req.Payload[3]
					termEnv = string(req.Payload[4 : termLen+4])
					w, h := parseDims(req.Payload[termLen+4:])
					SetWinsize(f.Fd(), w, h)
					logrus.Infof("pty-req: %s", termEnv)

				case "window-changed":
					w, h := parseDims(req.Payload)
					SetWinsize(f.Fd(), w, h)
					continue
				}

				if req.WantReply {
					if !ok {
						logrus.Infof("Declining %s request...", req.Type)
					}
					req.Reply(ok, nil)
				}
			}
		}(requests)
	}

	return nil
}
<commit_msg>Update ssh2docker.go<commit_after>package ssh2docker

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/kr/pty"
	"golang.org/x/crypto/ssh"
)

type Server struct {
	sshConfig *ssh.ServerConfig
}

func NewServer() (*Server, error) {
	server := Server{}
	server.sshConfig = &ssh.ServerConfig{
		PasswordCallback: server.PasswordCallback,
	}
	return &server, nil
}

func (s *Server) AddHostKeyFile(keypath string) error {
	keystring, err := ioutil.ReadFile("host_rsa")
	if err != nil {
		return err
	}

	hostkey, err := ssh.ParsePrivateKey(keystring)
	if err != nil {
		return err
	}

	s.sshConfig.AddHostKey(hostkey)
	return nil
}

func (s *Server) PasswordCallback(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
	return nil, nil
}

func (s *Server) Handle(netConn net.Conn) error {
	conn, chans, reqs, err := ssh.NewServerConn(netConn, s.sshConfig)
	if err != nil {
		logrus.Errorf("%s", err)
		return err
	}

	logrus.Infof("conn: User=%q, ClientVersion=%1", conn.User(), fmt.Sprintf("%x", conn.ClientVersion()))

	go func(in <-chan *ssh.Request) {
		for req := range in {
			if req.WantReply {
				req.Reply(false, nil)
			}
		}
	}(reqs)

	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			logrus.Errorf("%s", err)
			return err
		}

		logrus.Infof("Creating pty...")
		f, tty, err := pty.Open()
		if err != nil {
			logrus.Errorf("%s", err)
			continue
		}

		termEnv := "xterm"

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false
				switch req.Type {
				case "shell":
					if len(req.Payload) != 0 {
						break
					}
					ok = true

					args := []string{"run", "-it", "--rm", "quay.io/omen/jail", "--volumes-from", conn.User() + "__data", "/bin/sh"}
					logrus.Infof("Executing docker %s", args)
					cmd := exec.Command("docker", args...)
					cmd.Env = []string{
						"TERM=" + termEnv,
						"DOCKER_HOST=" + os.Getenv("DOCKER_HOST"),
						"DOCKER_CERT_PATH=" + os.Getenv("DOCKER_CERT_PATH"),
						"DOCKER_TLS_VERIFY=" + os.Getenv("DOCKER_TLS_VERIFY"),
					}

					defer tty.Close()
					cmd.Stdout = tty
					cmd.Stdin = tty
					cmd.Stderr = tty
					cmd.SysProcAttr = &syscall.SysProcAttr{
						Setctty: true,
						Setsid:  true,
					}

					err := cmd.Start()
					if err != nil {
						logrus.Warnf("%s", err)
						continue
					}

					var once sync.Once
					close := func() {
						channel.Close()
						logrus.Infof("session closed")
					}

					go func() {
						io.Copy(channel, f)
						once.Do(close)
					}()

					go func() {
						io.Copy(f, channel)
						once.Do(close)
					}()

				case "pty-req":
					ok = true
					termLen := req.Payload[3]
					termEnv = string(req.Payload[4 : termLen+4])
					w, h := parseDims(req.Payload[termLen+4:])
					SetWinsize(f.Fd(), w, h)
					logrus.Infof("pty-req: %s", termEnv)

				case "window-changed":
					w, h := parseDims(req.Payload)
					SetWinsize(f.Fd(), w, h)
					continue
				}

				if req.WantReply {
					if !ok {
						logrus.Infof("Declining %s request...", req.Type)
					}
					req.Reply(ok, nil)
				}
			}
		}(requests)
	}

	return nil
}
<|endoftext|>


<commit_before>// +build linux darwin

package overseer

//this file attempts to contain all posix
//specific stuff, that needs to be implemented
//in some other way on other OSs... TODO!

import (
	"os"
	"os/exec"
	"syscall"
)

var (
	supported = true
	uid       = syscall.Getuid()
	gid       = syscall.Getgid()
	SIGUSR1   = syscall.SIGUSR1
	SIGUSR2   = syscall.SIGUSR2
	SIGTERM   = syscall.SIGTERM
)

func move(dst, src string) error {
	if err := os.Rename(src, dst); err == nil {
		return nil
	}
	//HACK: we're shelling out to mv because linux
	//throws errors when crossing device boundaries.
	//TODO see sys_posix_mv.go
	if err := exec.Command("mv", src, dst).Run(); err != nil {
		return err
	}

	// Run sync to 'commit' the mv by clearing caches
	return sync().Run()
}

func sync() *exec.Cmd {
	return exec.Command("sync")
}

func chmod(f *os.File, perms os.FileMode) error {
	return f.Chmod(perms)
}
func chown(f *os.File, uid, gid int) error {
	return f.Chown(uid, gid)
}
<commit_msg>rename command to avoid conflict (#24)<commit_after>// +build linux darwin

package overseer

//this file attempts to contain all posix
//specific stuff, that needs to be implemented
//in some other way on other OSs... TODO!

import (
	"os"
	"os/exec"
	"syscall"
)

var (
	supported = true
	uid       = syscall.Getuid()
	gid       = syscall.Getgid()
	SIGUSR1   = syscall.SIGUSR1
	SIGUSR2   = syscall.SIGUSR2
	SIGTERM   = syscall.SIGTERM
)

func move(dst, src string) error {
	if err := os.Rename(src, dst); err == nil {
		return nil
	}
	//HACK: we're shelling out to mv because linux
	//throws errors when crossing device boundaries.
	//TODO see sys_posix_mv.go
	if err := exec.Command("mv", src, dst).Run(); err != nil {
		return err
	}

	// Run sync to 'commit' the mv by clearing caches
	return syncCmd().Run()
}

func syncCmd() *exec.Cmd {
	return exec.Command("sync")
}

func chmod(f *os.File, perms os.FileMode) error {
	return f.Chmod(perms)
}
func chown(f *os.File, uid, gid int) error {
	return f.Chown(uid, gid)
}
<|endoftext|>


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

package model

import (
	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/config/element"
	"github.com/corestoreio/csfw/config/valuelabel"
	"github.com/corestoreio/csfw/store/scope"
	"github.com/corestoreio/csfw/util/cast"
	"github.com/juju/errgo"
)

// SourceModeller defines how to retrieve all option values. Mostly used for frontend output.
type SourceModeller interface {
	Options() valuelabel.Slice
}

var _ SourceModeller = (*basePath)(nil)

// Option as an optional argument for the New*() functions.
type Option func(*basePath)

// WithPkgCfg sets a global PackageConfiguration for retrieving the default
// value of a underlying type and for scope permission checking.
func WithPkgCfg(pkgcfg element.SectionSlice) Option {
	return func(b *basePath) {
		b.PkgCfg = pkgcfg
	}
}

// WithField creates a new SectionSlice and GroupSlice containing this one field.
// The field.ID gets overwritten by the 3rd path parts to match the path.
func WithField(f *element.Field) Option {
	return func(b *basePath) {
		pp := scope.PathSplit(b.string)
		f.ID = pp[2]
		b.PkgCfg = element.MustNewConfiguration(
			&element.Section{
				ID: pp[0],
				Groups: element.NewGroupSlice(
					&element.Group{
						ID:     pp[1],
						Fields: element.NewFieldSlice(f),
					},
				),
			},
		)
	}
}

// WithValueLabel sets a valuelabel slice for Options() and validation.
func WithValueLabel(vl valuelabel.Slice) Option {
	return func(b *basePath) {
		b.ValueLabel = vl
	}
}

// WithValueLabelByString sets a valuelabel slice for Options() and validation.
// Wrapper for valuelabel.NewByString
func WithValueLabelByString(pairs ...string) Option {
	return func(b *basePath) {
		b.ValueLabel = valuelabel.NewByString(pairs...)
	}
}

// WithValueLabelByInt sets a valuelabel slice for Options() and validation.
// Wrapper for valuelabel.NewByInt
func WithValueLabelByInt(vli valuelabel.Ints) Option {
	return func(b *basePath) {
		b.ValueLabel = valuelabel.NewByInt(vli)
	}
}

// basePath defines the path in the "core_config_data" table like a/b/c. All other
// types in this package inherits from this path type.
type basePath struct {
	string // contains the path

	// PkgCfg as in Package Configuration which is used for scope permission
	// checks and retrieving the default value. A nil PkgCfg gets ignored.
	PkgCfg element.SectionSlice

	// ValueLabel are all available options aka SourceModel in Magento slang.
	// This slice is also used for validation to get and write the correct values.
	// Validation gets triggered only when the slice has been set.
	// The Options() function will be used to access this slice.
	ValueLabel valuelabel.Slice
}

// NewPath creates a new basePath type
func NewPath(path string, opts ...Option) basePath {
	b := basePath{
		string: path,
	}
	for _, o := range opts {
		o(&b)
	}
	return b
}

// Write writes a value v to the config.Writer without checking if the value
// has changed. Checks if the Scope matches as defined in the non-nil PkgCfg.
func (p basePath) Write(w config.Writer, v interface{}, s scope.Scope, id int64) error {
	if p.PkgCfg != nil {
		f, err := p.PkgCfg.FindFieldByPath(p.string)
		if err != nil {
			return errgo.Mask(err)
		}
		if false == f.Scope.Has(s) {
			return errgo.Newf("Scope permission insufficient: Have '%s'; Want '%s'", s, f.Scope)
		}
	}
	return w.Write(config.Path(p.string), config.Value(v), config.Scope(s, id))
}

// String returns the path
func (p basePath) String() string {
	return p.string
}

// InScope checks if a field from a path is allowed for current scope.
// Returns nil on success.
func (p basePath) InScope(sg scope.Scoper) (err error) {
	_, err = p.field(sg)
	return
}

// Options returns a source model for all available options for a configuration
// value.
//
// Usually this function gets customized in a sub-type. Customization
// can have different arguments, etc but must always call this function to set
// valuelabel slice.
func (p basePath) Options() valuelabel.Slice {
	return p.ValueLabel
}

// FQPathInt64 generates a fully qualified configuration path.
// Example: general/country/allow would transform with StrScope scope.StrStores
// and storeID e.g. 4 into: stores/4/general/country/allow
func (p basePath) FQPathInt64(strScope scope.StrScope, scopeID int64) string {
	return strScope.FQPathInt64(scopeID, p.string)
}

// field searches for the field in a SectionSlice and checks if the scope in
// ScopedGetter is sufficient.
func (p basePath) field(sg scope.Scoper) (f *element.Field, err error) {
	f, err = p.PkgCfg.FindFieldByPath(p.string)
	if err != nil {
		return nil, errgo.Mask(err)
	}
	s, _ := sg.Scope()
	if false == f.Scope.Has(s) {
		return nil, errgo.Newf("Scope permission insufficient: Have '%s'; Want '%s'", s, f.Scope)
	}
	return
}

// lookupString searches the default value in element.SectionSlice and overwrites
// it with a value from ScopedGetter if ScopedGetter is not empty.
// validator can be nil which triggers the default validation method.
func (p basePath) lookupString(sg config.ScopedGetter) (v string, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToStringE(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.String(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.basePath.lookupString.ScopedGetter.String", "err", errSG, "path", p.string, "previousErr", err)
		}
	}
	return
}

func (p basePath) validateString(v string) (err error) {
	if p.ValueLabel != nil && false == p.ValueLabel.ContainsValString(v) {
		jv, jErr := p.ValueLabel.ToJSON()
		err = errgo.Newf("The value '%s' cannot be found within the allowed Options():\n%s\nJSON Error: %s", v, jv, jErr)
	}
	return
}

func (p basePath) lookupInt(sg config.ScopedGetter) (v int, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToIntE(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.Int(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.path.lookupString.ScopedGetter.Int", "err", errSG, "path", p.string, "previousErr", err)
		}
	}
	return
}

func (p basePath) validateInt(v int) (err error) {
	if p.ValueLabel != nil && false == p.ValueLabel.ContainsValInt(v) {
		jv, jErr := p.ValueLabel.ToJSON()
		err = errgo.Newf("The value '%d' cannot be found within the allowed Options():\n%s\nJSON Error: %s", v, jv, jErr)
	}
	return
}

func (p basePath) lookupFloat64(sg config.ScopedGetter) (v float64, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToFloat64E(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.Float64(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.path.lookupString.ScopedGetter.Float64", "err", errSG, "path", p.string, "previousErr", err)
		}
	}
	return
}

func (p basePath) validateFloat64(v float64) (err error) {
	if p.ValueLabel != nil && false == p.ValueLabel.ContainsValFloat64(v) {
		jv, jErr := p.ValueLabel.ToJSON()
		err = errgo.Newf("The value '%.14f' cannot be found within the allowed Options():\n%s\nJSON Error: %s", v, jv, jErr)
	}
	return
}

func (p basePath) lookupBool(sg config.ScopedGetter) (v bool, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToBoolE(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.Bool(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.path.lookupString.ScopedGetter.Bool", "err", errSG, "path", p.string, "previousErr", err)
		}
	}

	return
}
<commit_msg>config/element: Add "Not thread safe" warning to doc.<commit_after>// Copyright 2015, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package model

import (
	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/config/element"
	"github.com/corestoreio/csfw/config/valuelabel"
	"github.com/corestoreio/csfw/store/scope"
	"github.com/corestoreio/csfw/util/cast"
	"github.com/juju/errgo"
)

// SourceModeller defines how to retrieve all option values. Mostly used for frontend output.
type SourceModeller interface {
	Options() valuelabel.Slice
}

var _ SourceModeller = (*basePath)(nil)

// Option as an optional argument for the New*() functions.
// To read more about the recursion pattern:
// http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html
type Option func(*basePath) Option

// WithPkgCfg sets a global PackageConfiguration for retrieving the default
// value of a underlying type and for scope permission checking.
func WithPkgCfg(pkgcfg element.SectionSlice) Option {
	return func(b *basePath) Option {
		prev := b.PkgCfg
		b.PkgCfg = pkgcfg
		return WithPkgCfg(prev)
	}
}

// WithField creates a new SectionSlice and GroupSlice containing this one field.
// The field.ID gets overwritten by the 3rd path parts to match the path.
func WithField(f *element.Field) Option {
	return func(b *basePath) Option {
		prev := b.PkgCfg
		pp := scope.PathSplit(b.string)
		f.ID = pp[2]
		b.PkgCfg = element.MustNewConfiguration(
			&element.Section{
				ID: pp[0],
				Groups: element.NewGroupSlice(
					&element.Group{
						ID:     pp[1],
						Fields: element.NewFieldSlice(f),
					},
				),
			},
		)
		return WithPkgCfg(prev)
	}
}

// WithValueLabel sets a valuelabel slice for Options() and validation.
func WithValueLabel(vl valuelabel.Slice) Option {
	return func(b *basePath) Option {
		prev := b.ValueLabel
		b.ValueLabel = vl
		return WithValueLabel(prev)
	}
}

// WithValueLabelByString sets a valuelabel slice for Options() and validation.
// Wrapper for valuelabel.NewByString
func WithValueLabelByString(pairs ...string) Option {
	return func(b *basePath) Option {
		prev := b.ValueLabel
		b.ValueLabel = valuelabel.NewByString(pairs...)
		return WithValueLabel(prev)
	}
}

// WithValueLabelByInt sets a valuelabel slice for Options() and validation.
// Wrapper for valuelabel.NewByInt
func WithValueLabelByInt(vli valuelabel.Ints) Option {
	return func(b *basePath) Option {
		prev := b.ValueLabel
		b.ValueLabel = valuelabel.NewByInt(vli)
		return WithValueLabel(prev)
	}
}

// basePath defines the path in the "core_config_data" table like a/b/c. All other
// types in this package inherits from this path type.
type basePath struct {
	string // contains the path

	// PkgCfg as in Package Configuration which is used for scope permission
	// checks and retrieving the default value. A nil PkgCfg gets ignored.
	PkgCfg element.SectionSlice

	// ValueLabel are all available options aka SourceModel in Magento slang.
	// This slice is also used for validation to get and write the correct values.
	// Validation gets triggered only when the slice has been set.
	// The Options() function will be used to access this slice.
	ValueLabel valuelabel.Slice
}

// NewPath creates a new basePath type
func NewPath(path string, opts ...Option) basePath {
	b := basePath{
		string: path,
	}
	(&b).Option(opts...)
	return b
}

// Option sets the options and returns the last set previous option
func (p *basePath) Option(opts ...Option) (previous Option) {
	for _, o := range opts {
		previous = o(p)
	}
	return
}

// Write writes a value v to the config.Writer without checking if the value
// has changed. Checks if the Scope matches as defined in the non-nil PkgCfg.
func (p basePath) Write(w config.Writer, v interface{}, s scope.Scope, id int64) error {
	if p.PkgCfg != nil {
		f, err := p.PkgCfg.FindFieldByPath(p.string)
		if err != nil {
			return errgo.Mask(err)
		}
		if false == f.Scope.Has(s) {
			return errgo.Newf("Scope permission insufficient: Have '%s'; Want '%s'", s, f.Scope)
		}
	}
	return w.Write(config.Path(p.string), config.Value(v), config.Scope(s, id))
}

// String returns the path
func (p basePath) String() string {
	return p.string
}

// InScope checks if a field from a path is allowed for current scope.
// Returns nil on success.
func (p basePath) InScope(sg scope.Scoper) (err error) {
	_, err = p.field(sg)
	return
}

// Options returns a source model for all available options for a configuration
// value.
//
// Usually this function gets customized in a sub-type. Customization
// can have different arguments, etc but must always call this function to set
// valuelabel slice.
func (p basePath) Options() valuelabel.Slice {
	return p.ValueLabel
}

// FQPathInt64 generates a fully qualified configuration path.
// Example: general/country/allow would transform with StrScope scope.StrStores
// and storeID e.g. 4 into: stores/4/general/country/allow
func (p basePath) FQPathInt64(strScope scope.StrScope, scopeID int64) string {
	return strScope.FQPathInt64(scopeID, p.string)
}

// field searches for the field in a SectionSlice and checks if the scope in
// ScopedGetter is sufficient.
func (p basePath) field(sg scope.Scoper) (f *element.Field, err error) {
	f, err = p.PkgCfg.FindFieldByPath(p.string)
	if err != nil {
		return nil, errgo.Mask(err)
	}
	s, _ := sg.Scope()
	if false == f.Scope.Has(s) {
		return nil, errgo.Newf("Scope permission insufficient: Have '%s'; Want '%s'", s, f.Scope)
	}
	return
}

// lookupString searches the default value in element.SectionSlice and overwrites
// it with a value from ScopedGetter if ScopedGetter is not empty.
// validator can be nil which triggers the default validation method.
func (p basePath) lookupString(sg config.ScopedGetter) (v string, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToStringE(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.String(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.basePath.lookupString.ScopedGetter.String", "err", errSG, "path", p.string, "previousErr", err)
		}
	}
	return
}

func (p basePath) validateString(v string) (err error) {
	if p.ValueLabel != nil && false == p.ValueLabel.ContainsValString(v) {
		jv, jErr := p.ValueLabel.ToJSON()
		err = errgo.Newf("The value '%s' cannot be found within the allowed Options():\n%s\nJSON Error: %s", v, jv, jErr)
	}
	return
}

func (p basePath) lookupInt(sg config.ScopedGetter) (v int, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToIntE(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.Int(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.path.lookupString.ScopedGetter.Int", "err", errSG, "path", p.string, "previousErr", err)
		}
	}
	return
}

func (p basePath) validateInt(v int) (err error) {
	if p.ValueLabel != nil && false == p.ValueLabel.ContainsValInt(v) {
		jv, jErr := p.ValueLabel.ToJSON()
		err = errgo.Newf("The value '%d' cannot be found within the allowed Options():\n%s\nJSON Error: %s", v, jv, jErr)
	}
	return
}

func (p basePath) lookupFloat64(sg config.ScopedGetter) (v float64, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToFloat64E(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.Float64(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.path.lookupString.ScopedGetter.Float64", "err", errSG, "path", p.string, "previousErr", err)
		}
	}
	return
}

func (p basePath) validateFloat64(v float64) (err error) {
	if p.ValueLabel != nil && false == p.ValueLabel.ContainsValFloat64(v) {
		jv, jErr := p.ValueLabel.ToJSON()
		err = errgo.Newf("The value '%.14f' cannot be found within the allowed Options():\n%s\nJSON Error: %s", v, jv, jErr)
	}
	return
}

func (p basePath) lookupBool(sg config.ScopedGetter) (v bool, err error) {

	var f *element.Field
	if f, err = p.field(sg); err != nil {
		return
	}

	v, err = cast.ToBoolE(f.Default)
	if err != nil {
		err = errgo.Mask(err)
		return
	}

	if val, errSG := sg.Bool(p.string); errSG == nil {
		v = val
	} else {
		// errSG is usually a key not found error, but that one is uninteresting
		if PkgLog.IsDebug() {
			PkgLog.Debug("model.path.lookupString.ScopedGetter.Bool", "err", errSG, "path", p.string, "previousErr", err)
		}
	}

	return
}
<|endoftext|>