<commit_before>//this program generates the appropriate record files
//based on the templates.
package main

import (
	"flag"
	"fmt"
	//"github.com/CSUNetSec/protoparse/util"
	"io"
	"os"
	"strings"
	"text/template"
)

var (
	gentype    string
	type0      string
	fmarshal   string
	funmarshal string
	pkgname    string
)

var recfiletmpl = template.Must(template.New("tmpl").Parse(
	`package {{.packagename}}

/*
   This is an autogenerated file. Do not edit directly.
   Generator: github.com/CSUNetSec/protoparse/util/recordfilegen.go
*/

import (
	. "github.com/CSUNetSec/protoparse/util"
	"errors"
	"fmt"
	"os"
){{if .imports}}

{{range $imp := .imports}}
import (
	"{{$imp}}"{{end}}
){{end}}

var (
	errscanner = errors.New("scanner in underlying is not Open. Call Open() first")
	errind = errors.New("no such index in file")
)

type {{.typename}}RecordFiler interface {
	RecordFiler
	Put({{.type}}) (error)
}

type {{.typename}}RecordFile struct {
	*FlatRecordFile
}

func New{{.typename}}RecordFile(fname string) *{{.typename}}RecordFile {
	return &{{.typename}}RecordFile{
		NewFlatRecordFile(fname),
	}
}

func (recfile *{{.typename}}RecordFile) Put(rec {{.type}}) error {
	b := {{.fmarshal}}(rec)
	_, err := recfile.Write(b)
	return err
}

func (recfile *{{.typename}}RecordFile) Get(ind int) ({{.type}}, error) {
	if recfile.Scanner == nil {
		return {{.type0}}, errscanner
	}
	curind := 0
	for recfile.Scanner.Scan() {
		if curind == ind {
			return {{.funmarshal}}(recfile.Scanner.Bytes()), nil
		}
		curind++
	}
	return {{.type0}}, errind
}
`))

var Usage = func() {
	fmt.Fprintf(os.Stderr, ":%s [flags] outfile.go\n", os.Args[0])
	flag.PrintDefaults()
}

func init() {
	const (
		dgentype    = "string"
		dtype0      = `""`
		dfmarshal   = "[]byte"
		dfunmarshal = "string"
		dpkgname    = "main"
	)
	flag.StringVar(&gentype, "type", dgentype, "type to generate templated code for")
	flag.StringVar(&fmarshal, "fmarshal", dfmarshal, "function for marshaling that type to bytes")
	flag.StringVar(&funmarshal, "funmarshal", dfunmarshal, "function to unmarshal bytes to type")
	flag.StringVar(&pkgname, "pkgname", dpkgname, "package name for the resulting generated go file")
	flag.StringVar(&type0, "typeEmpty", dtype0, "the empty expression for the provided type")
}

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		Usage()
		return
	}
	if _, err := os.Stat(flag.Arg(0)); err == nil {
		fmt.Fprintf(os.Stderr, "output file already exists\n")
		return
	}
	fp, err := os.Create(flag.Arg(0))
	if err != nil {
		fmt.Fprintf(os.Stderr, "error creating file:%s\n", err)
		return
	}
	defer fp.Close()
	genRecordFile(fp)
}

//both funtions borrowed from timtadh/fs2/fs2-generic
func parseType(imports map[string]bool, fqtn string) string {
	ptr := ""
	if strings.HasPrefix(fqtn, "*") {
		ptr = "*"
		fqtn = strings.TrimLeft(fqtn, "*")
	}
	parts := strings.Split(fqtn, "/")
	if len(parts) == 1 {
		return ptr + fqtn
	}
	typename := ptr + strings.Join(parts[len(parts)-2:], ".")
	imp := strings.Join(parts[:len(parts)-1], "/")
	imports[imp] = true
	return typename
}

func parseFunc(imports map[string]bool, fqfn string) string {
	parts := strings.Split(fqfn, "/")
	if len(parts) == 1 {
		return fqfn
	}
	funcname := strings.Join(parts[len(parts)-2:], ".")
	imp := strings.Join(parts[:len(parts)-1], "/")
	imports[imp] = true
	return funcname
}

func genRecordFile(out io.Writer) {
	imports := make(map[string]bool)
	gentypeval := parseType(imports, gentype)
	fmarshalval := parseFunc(imports, fmarshal)
	funmarshalval := parseFunc(imports, funmarshal)
	typeparts := strings.Split(gentypeval, ".")
	justtypename := typeparts[len(typeparts)-1]
	impstrs := make([]string, 0, len(imports))
	for k := range imports {
		impstrs = append(impstrs, k)
	}
	err := recfiletmpl.Execute(out, map[string]interface{}{
		"packagename": pkgname,
		"imports":     impstrs,
		"type":        gentypeval,
		"fmarshal":    fmarshalval,
		"funmarshal":  funmarshalval,
		"typename":    justtypename,
		"type0":       type0,
	})
	if err != nil {
		fmt.Printf("error in template:%s\n", err)
	}
}
<commit_msg>updating the number of entries on Put() in the template<commit_after>//this program generates the appropriate record files
//based on the templates.
package main

import (
	"flag"
	"fmt"
	//"github.com/CSUNetSec/protoparse/util"
	"io"
	"os"
	"strings"
	"text/template"
)

var (
	gentype    string
	type0      string
	fmarshal   string
	funmarshal string
	pkgname    string
)

var recfiletmpl = template.Must(template.New("tmpl").Parse(
	`package {{.packagename}}

/*
   This is an autogenerated file. Do not edit directly.
   Generator: github.com/CSUNetSec/protoparse/util/recordfilegen.go
*/

import (
	. "github.com/CSUNetSec/protoparse/util"
	"errors"
	"fmt"
	"os"
){{if .imports}}

{{range $imp := .imports}}
import (
	"{{$imp}}"{{end}}
){{end}}

var (
	errscanner = errors.New("scanner in underlying is not Open. Call Open() first")
	errind = errors.New("no such index in file")
)

type {{.typename}}RecordFiler interface {
	RecordFiler
	Put({{.type}}) (error)
}

type {{.typename}}RecordFile struct {
	*FlatRecordFile
}

func New{{.typename}}RecordFile(fname string) *{{.typename}}RecordFile {
	return &{{.typename}}RecordFile{
		NewFlatRecordFile(fname),
	}
}

func (recfile *{{.typename}}RecordFile) Put(rec {{.type}}) error {
	b := {{.fmarshal}}(rec)
	_, err := recfile.Write(b)
	if err == nil {
		recfile.entries++
	}
	return err
}

func (recfile *{{.typename}}RecordFile) Get(ind int) ({{.type}}, error) {
	if recfile.Scanner == nil {
		return {{.type0}}, errscanner
	}
	curind := 0
	for recfile.Scanner.Scan() {
		if curind == ind {
			return {{.funmarshal}}(recfile.Scanner.Bytes()), nil
		}
		curind++
	}
	return {{.type0}}, errind
}
`))

var Usage = func() {
	fmt.Fprintf(os.Stderr, ":%s [flags] outfile.go\n", os.Args[0])
	flag.PrintDefaults()
}

func init() {
	const (
		dgentype    = "string"
		dtype0      = `""`
		dfmarshal   = "[]byte"
		dfunmarshal = "string"
		dpkgname    = "main"
	)
	flag.StringVar(&gentype, "type", dgentype, "type to generate templated code for")
	flag.StringVar(&fmarshal, "fmarshal", dfmarshal, "function for marshaling that type to bytes")
	flag.StringVar(&funmarshal, "funmarshal", dfunmarshal, "function to unmarshal bytes to type")
	flag.StringVar(&pkgname, "pkgname", dpkgname, "package name for the resulting generated go file")
	flag.StringVar(&type0, "typeEmpty", dtype0, "the empty expression for the provided type")
}

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		Usage()
		return
	}
	if _, err := os.Stat(flag.Arg(0)); err == nil {
		fmt.Fprintf(os.Stderr, "output file already exists\n")
		return
	}
	fp, err := os.Create(flag.Arg(0))
	if err != nil {
		fmt.Fprintf(os.Stderr, "error creating file:%s\n", err)
		return
	}
	defer fp.Close()
	genRecordFile(fp)
}

//both funtions borrowed from timtadh/fs2/fs2-generic
func parseType(imports map[string]bool, fqtn string) string {
	ptr := ""
	if strings.HasPrefix(fqtn, "*") {
		ptr = "*"
		fqtn = strings.TrimLeft(fqtn, "*")
	}
	parts := strings.Split(fqtn, "/")
	if len(parts) == 1 {
		return ptr + fqtn
	}
	typename := ptr + strings.Join(parts[len(parts)-2:], ".")
	imp := strings.Join(parts[:len(parts)-1], "/")
	imports[imp] = true
	return typename
}

func parseFunc(imports map[string]bool, fqfn string) string {
	parts := strings.Split(fqfn, "/")
	if len(parts) == 1 {
		return fqfn
	}
	funcname := strings.Join(parts[len(parts)-2:], ".")
	imp := strings.Join(parts[:len(parts)-1], "/")
	imports[imp] = true
	return funcname
}

func genRecordFile(out io.Writer) {
	imports := make(map[string]bool)
	gentypeval := parseType(imports, gentype)
	fmarshalval := parseFunc(imports, fmarshal)
	funmarshalval := parseFunc(imports, funmarshal)
	typeparts := strings.Split(gentypeval, ".")
	justtypename := typeparts[len(typeparts)-1]
	impstrs := make([]string, 0, len(imports))
	for k := range imports {
		impstrs = append(impstrs, k)
	}
	err := recfiletmpl.Execute(out, map[string]interface{}{
		"packagename": pkgname,
		"imports":     impstrs,
		"type":        gentypeval,
		"fmarshal":    fmarshalval,
		"funmarshal":  funmarshalval,
		"typename":    justtypename,
		"type0":       type0,
	})
	if err != nil {
		fmt.Printf("error in template:%s\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/storage/v1"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/consulted/gcssync"
	"os"
)

const (
	_                = iota
	errorAuthInfo    = iota
	errorProjectInfo = iota
	errorClientInit  = iota
	errorUploadFiles = iota
)

const (
	scope       = storage.DevstorageFull_controlScope
	authURL     = "https://accounts.google.com/o/oauth2/auth"
	tokenURL    = "https://accounts.google.com/o/oauth2/token"
	entityName  = "allUsers"
	redirectURL = "urn:ietf:wg:oauth:2.0:oob"
)

func main() {
	app := cli.NewApp()
	app.Name = "gcssync"
	app.Usage = "Sync files with Google Cloud Storage"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "cachefile",
			Value:  "cache.json",
			Usage:  "Cache file for caching auth tokens",
			EnvVar: "AUTH_CACHE_FILE",
		},
		cli.StringFlag{
			Name:   "bucketname, b",
			Value:  "",
			Usage:  "Name of bucket",
			EnvVar: "BUCKET_NAME",
		},
		cli.StringFlag{
			Name:   "projectid, p",
			Value:  "",
			Usage:  "Google project",
			EnvVar: "PROJECT_ID",
		},
		cli.StringFlag{
			Name:   "clientid, c",
			Value:  "",
			Usage:  "Auth client id",
			EnvVar: "AUTH_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "clientsecret, s",
			Value:  "",
			Usage:  "Client secrect",
			EnvVar: "AUTH_CLIENT_SECRET",
		},
		cli.StringFlag{
			Name:   "code",
			Value:  "",
			Usage:  "Authorization Code",
			EnvVar: "AUTH_CODE",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "list",
			ShortName: "l",
			Usage:     "List remote files",
			Action:    listFiles,
		},
		{
			Name:      "upload",
			ShortName: "u",
			Usage:     "Upload a single file",
			Action:    uploadFile,
		},
	}
	app.Run(os.Args)
}

func generateOAuthConfig(c *cli.Context) (*oauth.Config, error) {
	clientId := c.GlobalString("clientid")
	if clientId == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client ID")
	}
	clientSecret := c.GlobalString("clientsecret")
	if clientSecret == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client Secret")
	}

	return &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		Scope:        scope,
		AuthURL:      authURL,
		TokenURL:     tokenURL,
		TokenCache:   oauth.CacheFile(c.GlobalString("cachefile")),
		RedirectURL:  redirectURL,
	}, nil
}

func generateServiceConfig(c *cli.Context) (*gcssync.ServiceConfig, error) {
	projectID := c.GlobalString("projectid")
	if projectID == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Could not find project id")
	}
	bucketName := c.GlobalString("bucketname")
	if bucketName == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Cloud not find bucket name")
	}
	return &gcssync.ServiceConfig{
		ProjectID:  projectID,
		BucketName: bucketName,
	}, nil
}

func getClient(c *cli.Context) *gcssync.Client {
	oauthConfig, err := generateOAuthConfig(c)
	if err != nil {
		fmt.Println("Missing auth informations", err.Error())
		os.Exit(errorAuthInfo)
	}
	serviceConfig, err := generateServiceConfig(c)
	if err != nil {
		fmt.Println("Missing project config", err.Error())
		os.Exit(errorProjectInfo)
	}

	client, err := gcssync.NewClient(oauthConfig, c.GlobalString("code"), serviceConfig)
	if err != nil {
		fmt.Println("Error initilizing client: ", err.Error())
		os.Exit(errorClientInit)
	}

	return client
}

func listFiles(c *cli.Context) {
	client := getClient(c)
	client.ListFiles()
}

func uploadFile(c *cli.Context) {
	client := getClient(c)
	if len(c.Args()) != 2 {
		fmt.Println("Need local and remote name!")
		os.Exit(errorUploadFiles)
	}

	client.UploadFile(c.Args().Get(0), c.Args().Get(1))
}
<commit_msg>feat(sync): added command<commit_after>package main

import (
	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/storage/v1"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/consulted/gcssync"
	"os"
	"path/filepath"
)

const (
	_                = iota
	errorAuthInfo    = iota
	errorProjectInfo = iota
	errorClientInit  = iota
	errorUploadFiles = iota
	errorSyncFiles   = iota
)

const (
	scope       = storage.DevstorageFull_controlScope
	authURL     = "https://accounts.google.com/o/oauth2/auth"
	tokenURL    = "https://accounts.google.com/o/oauth2/token"
	entityName  = "allUsers"
	redirectURL = "urn:ietf:wg:oauth:2.0:oob"
)

func main() {
	app := cli.NewApp()
	app.Name = "gcssync"
	app.Usage = "Sync files with Google Cloud Storage"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "cachefile",
			Value:  "cache.json",
			Usage:  "Cache file for caching auth tokens",
			EnvVar: "AUTH_CACHE_FILE",
		},
		cli.StringFlag{
			Name:   "bucketname, b",
			Value:  "",
			Usage:  "Name of bucket",
			EnvVar: "BUCKET_NAME",
		},
		cli.StringFlag{
			Name:   "projectid, p",
			Value:  "",
			Usage:  "Google project",
			EnvVar: "PROJECT_ID",
		},
		cli.StringFlag{
			Name:   "clientid, c",
			Value:  "",
			Usage:  "Auth client id",
			EnvVar: "AUTH_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "clientsecret, s",
			Value:  "",
			Usage:  "Client secrect",
			EnvVar: "AUTH_CLIENT_SECRET",
		},
		cli.StringFlag{
			Name:   "code",
			Value:  "",
			Usage:  "Authorization Code",
			EnvVar: "AUTH_CODE",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "list",
			ShortName: "l",
			Usage:     "List remote files",
			Action:    listFiles,
		},
		{
			Name:      "upload",
			ShortName: "u",
			Usage:     "Upload a single file",
			Action:    uploadFile,
		},
		{
			Name:      "sync",
			ShortName: "s",
			Usage:     "Syncs a folder to a Google Cloudstorage bucket",
			Action:    syncFolder,
			// Flags: []cli.Flag{
			// 	cli.StringFlag{
			// 		Name:   "exclude,e",
			// 		Value:  "",
			// 		Usage:  "Exclude files matching this pattern",
			// 		EnvVar: "EXCLUDE_FILES",
			// 	},
			// },
		},
	}
	app.Run(os.Args)
}

func generateOAuthConfig(c *cli.Context) (*oauth.Config, error) {
	clientId := c.GlobalString("clientid")
	if clientId == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client ID")
	}
	clientSecret := c.GlobalString("clientsecret")
	if clientSecret == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client Secret")
	}

	return &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		Scope:        scope,
		AuthURL:      authURL,
		TokenURL:     tokenURL,
		TokenCache:   oauth.CacheFile(c.GlobalString("cachefile")),
		RedirectURL:  redirectURL,
	}, nil
}

func generateServiceConfig(c *cli.Context) (*gcssync.ServiceConfig, error) {
	projectID := c.GlobalString("projectid")
	if projectID == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Could not find project id")
	}
	bucketName := c.GlobalString("bucketname")
	if bucketName == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Cloud not find bucket name")
	}
	return &gcssync.ServiceConfig{
		ProjectID:  projectID,
		BucketName: bucketName,
	}, nil
}

func getClient(c *cli.Context) *gcssync.Client {
	oauthConfig, err := generateOAuthConfig(c)
	if err != nil {
		fmt.Println("Missing auth informations", err.Error())
		os.Exit(errorAuthInfo)
	}
	serviceConfig, err := generateServiceConfig(c)
	if err != nil {
		fmt.Println("Missing project config", err.Error())
		os.Exit(errorProjectInfo)
	}

	client, err := gcssync.NewClient(oauthConfig, c.GlobalString("code"), serviceConfig)
	if err != nil {
		fmt.Println("Error initilizing client: ", err.Error())
		os.Exit(errorClientInit)
	}

	return client
}

func listFiles(c *cli.Context) {
	client := getClient(c)
	client.ListFiles()
}

func uploadFile(c *cli.Context) {
	client := getClient(c)
	if len(c.Args()) != 2 {
		fmt.Println("Need local and remote name!")
		os.Exit(errorUploadFiles)
	}

	client.UploadFile(c.Args().Get(0), c.Args().Get(1))
}

func syncFolder(c *cli.Context) {
	client := getClient(c)
	var local, remote string
	switch len(c.Args()) {
	case 0:
		local = ""
		remote = ""
	case 1:
		local = c.Args().Get(0)
		remote = ""
	case 2:
		local = c.Args().Get(0)
		remote = c.Args().Get(1)
	default:
		fmt.Println("To many arguments")
		os.Exit(errorSyncFiles)
	}
	local, err := filepath.Abs(local)
	if err != nil {
		fmt.Println("Could not get absolute path")
		os.Exit(errorSyncFiles)
	}
	client.SyncFolder(local, remote /*c.String("exclude") */)
}
<|endoftext|>


<commit_before>// Unfancy resources embedding with Go.

package main

import (
	"flag"
	"log"
	"path/filepath"
	"strings"

	"github.com/omeid/go-resources"
)

var (
	pkg      = flag.String("package", "main", "`name` of the package to generate")
	varName  = flag.String("var", "FS", "`name` of the variable to assign the virtual filesystem to")
	tag      = flag.String("tag", "", "`tag` to use for the generated package (default no tag)")
	declare  = flag.Bool("declare", false, "whether to declare the -var (default false)")
	out      = flag.String("output", "", "`filename` to write the output to")
	trimPath = flag.String("trim", "", "path `prefix` to remove from the resulting file path in the virtual filesystem")
	width    = flag.Int("width", 12, "`number` of content bytes per line in generetated file")
	gofmt    = flag.Bool("fmt", false, "run output through gofmt, this is slow for huge files (default false)")
)

type nope struct{}

func main() {
	flag.Parse()

	if *out == "" {
		flag.PrintDefaults()
		log.Fatal("-output is required.")
	}

	config := resources.Config{
		Pkg:     *pkg,
		Var:     *varName,
		Tag:     *tag,
		Declare: *declare,
		Format:  *gofmt,
	}
	resources.BlockWidth = *width

	res := resources.New()
	res.Config = config

	files := make(map[string]nope)

	for _, g := range flag.Args() {
		matches, err := filepath.Glob(g)
		if err != nil {
			log.Fatal(err)
		}
		for _, m := range matches {
			files[m] = nope{}
		}
	}

	for file := range files {
		path := strings.TrimPrefix(file, *trimPath)
		err := res.AddFile(path, file)
		if err != nil {
			log.Fatal(err)
		}
	}

	err := res.Write(*out)

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

	log.Printf("Done. Wrote to %s", *out)

}
<commit_msg>cmd/resources: seperate flag parsing from default values<commit_after>// Unfancy resources embedding with Go.

package main

import (
	"flag"
	"log"
	"path/filepath"
	"strings"

	"github.com/omeid/go-resources"
)

var (
	pkg      = "main"
	varName  = "FS"
	tag      = ""
	declare  = false
	out      = ""
	trimPath = ""
	width    = resources.BlockWidth
	gofmt    = false
)

type nope struct{}

func main() {
	flag.StringVar(&pkg, "package", pkg, "`name` of the package to generate")
	flag.StringVar(&varName, "var", varName, "`name` of the variable to assign the virtual filesystem to")
	flag.StringVar(&tag, "tag", tag, "`tag` to use for the generated package (default no tag)")
	flag.BoolVar(&declare, "declare", declare, "whether to declare the -var (default false)")
	flag.StringVar(&out, "output", out, "`filename` to write the output to")
	flag.StringVar(&trimPath, "trim", trimPath, "path `prefix` to remove from the resulting file path in the virtual filesystem")
	flag.IntVar(&width, "width", width, "`number` of content bytes per line in generetated file")
	flag.BoolVar(&gofmt, "fmt", gofmt, "run output through gofmt, this is slow for huge files (default false)")
	flag.Parse()

	if out == "" {
		flag.PrintDefaults()
		log.Fatal("-output is required.")
	}

	config := resources.Config{
		Pkg:     pkg,
		Var:     varName,
		Tag:     tag,
		Declare: declare,
		Format:  gofmt,
	}
	resources.BlockWidth = width

	res := resources.New()
	res.Config = config

	files := make(map[string]nope)

	for _, g := range flag.Args() {
		matches, err := filepath.Glob(g)
		if err != nil {
			log.Fatal(err)
		}
		for _, m := range matches {
			files[m] = nope{}
		}
	}

	for file := range files {
		path := strings.TrimPrefix(file, trimPath)
		err := res.AddFile(path, file)
		if err != nil {
			log.Fatal(err)
		}
	}

	err := res.Write(*out)

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

	log.Printf("Done. Wrote to %s", *out)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"reflect"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/cmd/utils"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/ethereum/go-ethereum/rpc/codec"
	"github.com/ethereum/go-ethereum/rpc/comms"
	"github.com/gizak/termui"
)

var (
	monitorCommandAttachFlag = cli.StringFlag{
		Name:  "attach",
		Value: "ipc:" + common.DefaultIpcPath(),
		Usage: "API endpoint to attach to",
	}
	monitorCommandRowsFlag = cli.IntFlag{
		Name:  "rows",
		Value: 5,
		Usage: "Maximum rows in the chart grid",
	}
	monitorCommandRefreshFlag = cli.IntFlag{
		Name:  "refresh",
		Value: 3,
		Usage: "Refresh interval in seconds",
	}
	monitorCommand = cli.Command{
		Action: monitor,
		Name:   "monitor",
		Usage:  `Geth Monitor: node metrics monitoring and visualization`,
		Description: `
The Geth monitor is a tool to collect and visualize various internal metrics
gathered by the node, supporting different chart types as well as the capacity
to display multiple metrics simultaneously.
`,
		Flags: []cli.Flag{
			monitorCommandAttachFlag,
			monitorCommandRowsFlag,
			monitorCommandRefreshFlag,
		},
	}
)

// monitor starts a terminal UI based monitoring tool for the requested metrics.
func monitor(ctx *cli.Context) {
	var (
		client comms.EthereumClient
		err    error
	)
	// Attach to an Ethereum node over IPC or RPC
	endpoint := ctx.String(monitorCommandAttachFlag.Name)
	if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil {
		utils.Fatalf("Unable to attach to geth node: %v", err)
	}
	defer client.Close()

	xeth := rpc.NewXeth(client)

	// Retrieve all the available metrics and resolve the user pattens
	metrics, err := retrieveMetrics(xeth)
	if err != nil {
		utils.Fatalf("Failed to retrieve system metrics: %v", err)
	}
	monitored := resolveMetrics(metrics, ctx.Args())
	if len(monitored) == 0 {
		list := expandMetrics(metrics, "")
		sort.Strings(list)
		utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - "))
	}
	sort.Strings(monitored)
	if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 {
		utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - "))
	}
	// Create and configure the chart UI defaults
	if err := termui.Init(); err != nil {
		utils.Fatalf("Unable to initialize terminal UI: %v", err)
	}
	defer termui.Close()

	termui.UseTheme("helloworld")

	rows := len(monitored)
	if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max {
		rows = max
	}
	cols := (len(monitored) + rows - 1) / rows
	for i := 0; i < rows; i++ {
		termui.Body.AddRows(termui.NewRow())
	}
	// Create each individual data chart
	footer := termui.NewPar("")
	footer.HasBorder = true
	footer.Height = 3

	charts := make([]*termui.LineChart, len(monitored))
	data := make([][]float64, len(monitored))
	for i := 0; i < len(data); i++ {
		data[i] = make([]float64, 512)
	}
	for i, metric := range monitored {
		charts[i] = termui.NewLineChart()
		if runtime.GOOS == "windows" {
			charts[i].Mode = "dot"
		}
		charts[i].Data = make([]float64, 512)
		charts[i].DataLabels = []string{""}
		charts[i].Height = (termui.TermHeight() - footer.Height) / rows
		charts[i].AxesColor = termui.ColorWhite
		charts[i].PaddingBottom = -2

		charts[i].Border.Label = metric
		charts[i].Border.LabelFgColor = charts[i].Border.FgColor | termui.AttrBold
		charts[i].Border.FgColor = charts[i].Border.BgColor

		row := termui.Body.Rows[i%rows]
		row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i]))
	}
	termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer)))
	termui.Body.Align()
	termui.Render(termui.Body)

	refreshCharts(xeth, monitored, data, charts, ctx, footer)
	termui.Render(termui.Body)

	// Watch for various system events, and periodically refresh the charts
	refresh := time.Tick(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second)
	for {
		select {
		case event := <-termui.EventCh():
			if event.Type == termui.EventKey && event.Key == termui.KeyCtrlC {
				return
			}
			if event.Type == termui.EventResize {
				termui.Body.Width = termui.TermWidth()
				for _, chart := range charts {
					chart.Height = (termui.TermHeight() - footer.Height) / rows
				}
				termui.Body.Align()
				termui.Render(termui.Body)
			}
		case <-refresh:
			refreshCharts(xeth, monitored, data, charts, ctx, footer)
			termui.Render(termui.Body)
		}
	}
}

// retrieveMetrics contacts the attached geth node and retrieves the entire set
// of collected system metrics.
func retrieveMetrics(xeth *rpc.Xeth) (map[string]interface{}, error) {
	return xeth.Call("debug_metrics", []interface{}{true})
}

// resolveMetrics takes a list of input metric patterns, and resolves each to one
// or more canonical metric names.
func resolveMetrics(metrics map[string]interface{}, patterns []string) []string {
	res := []string{}
	for _, pattern := range patterns {
		res = append(res, resolveMetric(metrics, pattern, "")...)
	}
	return res
}

// resolveMetrics takes a single of input metric pattern, and resolves it to one
// or more canonical metric names.
func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string {
	results := []string{}

	// If a nested metric was requested, recurse optionally branching (via comma)
	parts := strings.SplitN(pattern, "/", 2)
	if len(parts) > 1 {
		for _, variation := range strings.Split(parts[0], ",") {
			if submetrics, ok := metrics[variation].(map[string]interface{}); !ok {
				utils.Fatalf("Failed to retrieve system metrics: %s", path+variation)
				return nil
			} else {
				results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...)
			}
		}
		return results
	}
	// Depending what the last link is, return or expand
	for _, variation := range strings.Split(pattern, ",") {
		switch metric := metrics[variation].(type) {
		case float64:
			// Final metric value found, return as singleton
			results = append(results, path+variation)

		case map[string]interface{}:
			results = append(results, expandMetrics(metric, path+variation+"/")...)

		default:
			utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric))
			return nil
		}
	}
	return results
}

// expandMetrics expands the entire tree of metrics into a flat list of paths.
func expandMetrics(metrics map[string]interface{}, path string) []string {
	// Iterate over all fields and expand individually
	list := []string{}
	for name, metric := range metrics {
		switch metric := metric.(type) {
		case float64:
			// Final metric value found, append to list
			list = append(list, path+name)

		case map[string]interface{}:
			// Tree of metrics found, expand recursively
			list = append(list, expandMetrics(metric, path+name+"/")...)

		default:
			utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric))
			return nil
		}
	}
	return list
}

// fetchMetric iterates over the metrics map and retrieves a specific one.
func fetchMetric(metrics map[string]interface{}, metric string) float64 {
	parts, found := strings.Split(metric, "/"), true
	for _, part := range parts[:len(parts)-1] {
		metrics, found = metrics[part].(map[string]interface{})
		if !found {
			return 0
		}
	}
	if v, ok := metrics[parts[len(parts)-1]].(float64); ok {
		return v
	}
	return 0
}

// refreshCharts retrieves a next batch of metrics, and inserts all the new
// values into the active datasets and charts
func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) {
	values, err := retrieveMetrics(xeth)
	for i, metric := range metrics {
		data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
		updateChart(metric, data[i], charts[i], err)
	}
	updateFooter(ctx, err, footer)
}

// updateChart inserts a dataset into a line chart, scaling appropriately as to
// not display weird labels, also updating the chart label accordingly.
func updateChart(metric string, data []float64, chart *termui.LineChart, err error) {
	dataUnits := []string{"", "K", "M", "G", "T", "E"}
	timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"}
	colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed}

	// Extract only part of the data that's actually visible
	data = data[:chart.Width*2]

	// Find the maximum value and scale under 1K
	high := data[0]
	for _, value := range data[1:] {
		high = math.Max(high, value)
	}
	unit, scale := 0, 1.0
	for high >= 1000 {
		high, unit, scale = high/1000, unit+1, scale*1000
	}
	// Update the chart's data points with the scaled values
	for i, value := range data {
		chart.Data[i] = value / scale
	}
	// Update the chart's label with the scale units
	chart.Border.Label = metric

	units := dataUnits
	if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") {
		units = timeUnits
	}
	if len(units[unit]) > 0 {
		chart.Border.Label += " [" + units[unit] + "]"
	}
	chart.LineColor = colors[unit] | termui.AttrBold
	if err != nil {
		chart.LineColor = termui.ColorRed | termui.AttrBold
	}
}

// updateFooter updates the footer contents based on any encountered errors.
func updateFooter(ctx *cli.Context, err error, footer *termui.Par) {
	// Generate the basic footer
	refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second
	footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh)
	footer.TextFgColor = termui.Theme().ParTextFg | termui.AttrBold

	// Append any encountered errors
	if err != nil {
		footer.Text = fmt.Sprintf("Error: %v.", err)
		footer.TextFgColor = termui.ColorRed | termui.AttrBold
	}
}
<commit_msg>cmd/geth: re-scale charts when changing unit magnitudes<commit_after>package main

import (
	"fmt"
	"math"
	"reflect"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/cmd/utils"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/ethereum/go-ethereum/rpc/codec"
	"github.com/ethereum/go-ethereum/rpc/comms"
	"github.com/gizak/termui"
)

var (
	monitorCommandAttachFlag = cli.StringFlag{
		Name:  "attach",
		Value: "ipc:" + common.DefaultIpcPath(),
		Usage: "API endpoint to attach to",
	}
	monitorCommandRowsFlag = cli.IntFlag{
		Name:  "rows",
		Value: 5,
		Usage: "Maximum rows in the chart grid",
	}
	monitorCommandRefreshFlag = cli.IntFlag{
		Name:  "refresh",
		Value: 3,
		Usage: "Refresh interval in seconds",
	}
	monitorCommand = cli.Command{
		Action: monitor,
		Name:   "monitor",
		Usage:  `Geth Monitor: node metrics monitoring and visualization`,
		Description: `
The Geth monitor is a tool to collect and visualize various internal metrics
gathered by the node, supporting different chart types as well as the capacity
to display multiple metrics simultaneously.
`,
		Flags: []cli.Flag{
			monitorCommandAttachFlag,
			monitorCommandRowsFlag,
			monitorCommandRefreshFlag,
		},
	}
)

// monitor starts a terminal UI based monitoring tool for the requested metrics.
func monitor(ctx *cli.Context) {
	var (
		client comms.EthereumClient
		err    error
	)
	// Attach to an Ethereum node over IPC or RPC
	endpoint := ctx.String(monitorCommandAttachFlag.Name)
	if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil {
		utils.Fatalf("Unable to attach to geth node: %v", err)
	}
	defer client.Close()

	xeth := rpc.NewXeth(client)

	// Retrieve all the available metrics and resolve the user pattens
	metrics, err := retrieveMetrics(xeth)
	if err != nil {
		utils.Fatalf("Failed to retrieve system metrics: %v", err)
	}
	monitored := resolveMetrics(metrics, ctx.Args())
	if len(monitored) == 0 {
		list := expandMetrics(metrics, "")
		sort.Strings(list)
		utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - "))
	}
	sort.Strings(monitored)
	if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 {
		utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - "))
	}
	// Create and configure the chart UI defaults
	if err := termui.Init(); err != nil {
		utils.Fatalf("Unable to initialize terminal UI: %v", err)
	}
	defer termui.Close()

	termui.UseTheme("helloworld")

	rows := len(monitored)
	if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max {
		rows = max
	}
	cols := (len(monitored) + rows - 1) / rows
	for i := 0; i < rows; i++ {
		termui.Body.AddRows(termui.NewRow())
	}
	// Create each individual data chart
	footer := termui.NewPar("")
	footer.HasBorder = true
	footer.Height = 3

	charts := make([]*termui.LineChart, len(monitored))
	units := make([]int, len(monitored))
	data := make([][]float64, len(monitored))
	for i := 0; i < len(data); i++ {
		data[i] = make([]float64, 512)
	}
	for i := 0; i < len(monitored); i++ {
		charts[i] = createChart((termui.TermHeight() - footer.Height) / rows)
		row := termui.Body.Rows[i%rows]
		row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i]))
	}
	termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer)))

	refreshCharts(xeth, monitored, data, units, charts, ctx, footer)
	termui.Body.Align()
	termui.Render(termui.Body)

	// Watch for various system events, and periodically refresh the charts
	refresh := time.Tick(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second)
	for {
		select {
		case event := <-termui.EventCh():
			if event.Type == termui.EventKey && event.Key == termui.KeyCtrlC {
				return
			}
			if event.Type == termui.EventResize {
				termui.Body.Width = termui.TermWidth()
				for _, chart := range charts {
					chart.Height = (termui.TermHeight() - footer.Height) / rows
				}
				termui.Body.Align()
				termui.Render(termui.Body)
			}
		case <-refresh:
			if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) {
				termui.Body.Align()
			}
			termui.Render(termui.Body)
		}
	}
}

// retrieveMetrics contacts the attached geth node and retrieves the entire set
// of collected system metrics.
func retrieveMetrics(xeth *rpc.Xeth) (map[string]interface{}, error) {
	return xeth.Call("debug_metrics", []interface{}{true})
}

// resolveMetrics takes a list of input metric patterns, and resolves each to one
// or more canonical metric names.
func resolveMetrics(metrics map[string]interface{}, patterns []string) []string {
	res := []string{}
	for _, pattern := range patterns {
		res = append(res, resolveMetric(metrics, pattern, "")...)
	}
	return res
}

// resolveMetrics takes a single of input metric pattern, and resolves it to one
// or more canonical metric names.
func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string {
	results := []string{}

	// If a nested metric was requested, recurse optionally branching (via comma)
	parts := strings.SplitN(pattern, "/", 2)
	if len(parts) > 1 {
		for _, variation := range strings.Split(parts[0], ",") {
			if submetrics, ok := metrics[variation].(map[string]interface{}); !ok {
				utils.Fatalf("Failed to retrieve system metrics: %s", path+variation)
				return nil
			} else {
				results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...)
			}
		}
		return results
	}
	// Depending what the last link is, return or expand
	for _, variation := range strings.Split(pattern, ",") {
		switch metric := metrics[variation].(type) {
		case float64:
			// Final metric value found, return as singleton
			results = append(results, path+variation)

		case map[string]interface{}:
			results = append(results, expandMetrics(metric, path+variation+"/")...)

		default:
			utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric))
			return nil
		}
	}
	return results
}

// expandMetrics expands the entire tree of metrics into a flat list of paths.
func expandMetrics(metrics map[string]interface{}, path string) []string {
	// Iterate over all fields and expand individually
	list := []string{}
	for name, metric := range metrics {
		switch metric := metric.(type) {
		case float64:
			// Final metric value found, append to list
			list = append(list, path+name)

		case map[string]interface{}:
			// Tree of metrics found, expand recursively
			list = append(list, expandMetrics(metric, path+name+"/")...)

		default:
			utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric))
			return nil
		}
	}
	return list
}

// fetchMetric iterates over the metrics map and retrieves a specific one.
func fetchMetric(metrics map[string]interface{}, metric string) float64 {
	parts, found := strings.Split(metric, "/"), true
	for _, part := range parts[:len(parts)-1] {
		metrics, found = metrics[part].(map[string]interface{})
		if !found {
			return 0
		}
	}
	if v, ok := metrics[parts[len(parts)-1]].(float64); ok {
		return v
	}
	return 0
}

// refreshCharts retrieves a next batch of metrics, and inserts all the new
// values into the active datasets and charts
func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) {
	values, err := retrieveMetrics(xeth)
	for i, metric := range metrics {
		data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
		if updateChart(metric, data[i], &units[i], charts[i], err) {
			realign = true
		}
	}
	updateFooter(ctx, err, footer)
	return
}

// updateChart inserts a dataset into a line chart, scaling appropriately as to
// not display weird labels, also updating the chart label accordingly.
func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) {
	dataUnits := []string{"", "K", "M", "G", "T", "E"}
	timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"}
	colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed}

	// Extract only part of the data that's actually visible
	data = data[:chart.Width*2]

	// Find the maximum value and scale under 1K
	high := data[0]
	for _, value := range data[1:] {
		high = math.Max(high, value)
	}
	unit, scale := 0, 1.0
	for high >= 1000 {
		high, unit, scale = high/1000, unit+1, scale*1000
	}
	// If the unit changes, re-create the chart (hack to set max height...)
	if unit != *base {
		realign, *base, *chart = true, unit, *createChart(chart.Height)
	}
	// Update the chart's data points with the scaled values
	for i, value := range data {
		chart.Data[i] = value / scale
	}
	// Update the chart's label with the scale units
	units := dataUnits
	if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") {
		units = timeUnits
	}
	chart.Border.Label = metric
	if len(units[unit]) > 0 {
		chart.Border.Label += " [" + units[unit] + "]"
	}
	chart.LineColor = colors[unit] | termui.AttrBold
	if err != nil {
		chart.LineColor = termui.ColorRed | termui.AttrBold
	}
	return
}

// createChart creates an empty line chart with the default configs.
func createChart(height int) *termui.LineChart {
	chart := termui.NewLineChart()
	if runtime.GOOS == "windows" {
		chart.Mode = "dot"
	}
	chart.Data = make([]float64, 512)
	chart.DataLabels = []string{""}
	chart.Height = height
	chart.AxesColor = termui.ColorWhite
	chart.PaddingBottom = -2

	chart.Border.LabelFgColor = chart.Border.FgColor | termui.AttrBold
	chart.Border.FgColor = chart.Border.BgColor

	return chart
}

// updateFooter updates the footer contents based on any encountered errors.
func updateFooter(ctx *cli.Context, err error, footer *termui.Par) {
	// Generate the basic footer
	refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second
	footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh)
	footer.TextFgColor = termui.Theme().ParTextFg | termui.AttrBold

	// Append any encountered errors
	if err != nil {
		footer.Text = fmt.Sprintf("Error: %v.", err)
		footer.TextFgColor = termui.ColorRed | termui.AttrBold
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package ra

import (
	"crypto/x509"
	"fmt"
	"math/big"
	"net/url"
	"regexp"
	"strconv"
	"time"

	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
	"github.com/letsencrypt/boulder/policy"
)

// All of the fields in RegistrationAuthorityImpl need to be
// populated, or there is a risk of panic.
type RegistrationAuthorityImpl struct {
	CA  core.CertificateAuthority
	VA  core.ValidationAuthority
	SA  core.StorageAuthority
	PA  core.PolicyAuthority
	log *blog.AuditLogger

	AuthzBase string
}

func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl {
	logger := blog.GetAuditLogger()
	logger.Notice("Registration Authority Starting")

	ra := RegistrationAuthorityImpl{log: logger}
	ra.PA = policy.NewPolicyAuthorityImpl()
	return ra
}

var allButLastPathSegment = regexp.MustCompile("^.*/")

func lastPathSegment(url core.AcmeURL) string {
	return allButLastPathSegment.ReplaceAllString(url.Path, "")
}

type certificateRequestEvent struct {
	ID                  string    `json:",omitempty"`
	Requester           int64     `json:",omitempty"`
	SerialNumber        *big.Int  `json:",omitempty"`
	RequestMethod       string    `json:",omitempty"`
	VerificationMethods []string  `json:",omitempty"`
	VerifiedFields      []string  `json:",omitempty"`
	CommonName          string    `json:",omitempty"`
	Names               []string  `json:",omitempty"`
	NotBefore           time.Time `json:",omitempty"`
	NotAfter            time.Time `json:",omitempty"`
	RequestTime         time.Time `json:",omitempty"`
	ResponseTime        time.Time `json:",omitempty"`
	Error               string    `json:",omitempty"`
}

func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) {
	reg = core.Registration{
		RecoveryToken: core.NewToken(),
		Key:           init.Key,
	}
	reg.MergeUpdate(init)

	// Store the authorization object, then return it
	reg, err = ra.SA.NewRegistration(reg)
	return
}

func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
	if regID <= 0 {
		err = fmt.Errorf("Invalid registration ID")
		return authz, err
	}

	identifier := request.Identifier

	// Check that the identifier is present and appropriate
	if err = ra.PA.WillingToIssue(identifier); err != nil {
		return authz, err
	}

	// Create validations
	// TODO: Assign URLs
	challenges, combinations := ra.PA.ChallengesFor(identifier)
	authID, err := ra.SA.NewPendingAuthorization()
	if err != nil {
		return authz, err
	}
	for i := range challenges {
		// Ignoring these errors because we construct the URLs to be correct
		challengeURI, _ := url.Parse(ra.AuthzBase + authID + "?challenge=" + strconv.Itoa(i))
		challenges[i].URI = core.AcmeURL(*challengeURI)

		if !challenges[i].IsSane(false) {
			err = fmt.Errorf("Challenge didn't pass sanity check: %+v", challenges[i])
			return authz, err
		}
	}

	// Create a new authorization object
	authz = core.Authorization{
		ID:             authID,
		Identifier:     identifier,
		RegistrationID: regID,
		Status:         core.StatusPending,
		Challenges:     challenges,
		Combinations:   combinations,
	}

	// Store the authorization object, then return it
	err = ra.SA.UpdatePendingAuthorization(authz)
	return authz, err
}

func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (core.Certificate, error) {
	emptyCert := core.Certificate{}
	var err error
	var logEventResult string

	// Assume the worst
	logEventResult = "error"

	// Construct the log event
	logEvent := certificateRequestEvent{
		ID:            core.NewToken(),
		Requester:     regID,
		RequestMethod: "online",
		RequestTime:   time.Now(),
	}

	// No matter what, log the request
	defer func() {
		// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
		ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent)
	}()

	if regID <= 0 {
		err = fmt.Errorf("Invalid registration ID")
		return emptyCert, err
	}

	// Verify the CSR
	// TODO: Verify that other aspects of the CSR are appropriate
	csr := req.CSR
	if err = core.VerifyCSR(csr); err != nil {
		logEvent.Error = err.Error()
		err = core.UnauthorizedError("Invalid signature on CSR")
		return emptyCert, err
	}

	logEvent.CommonName = csr.Subject.CommonName
	logEvent.Names = csr.DNSNames

	csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(append(csr.DNSNames, csr.Subject.CommonName))
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}
	if csrPreviousDenied {
		err = core.UnauthorizedError("CSR has already been revoked/denied")
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	registration, err := ra.SA.GetRegistration(regID)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	if core.KeyDigestEquals(csr.PublicKey, registration.Key) {
		err = core.MalformedRequestError("Certificate public key must be different than account key")
		return emptyCert, err
	}

	// Gather authorized domains from the referenced authorizations
	authorizedDomains := map[string]bool{}
	verificationMethodSet := map[string]bool{}
	earliestExpiry := time.Date(2100, 01, 01, 0, 0, 0, 0, time.UTC)
	now := time.Now()
	for _, url := range req.Authorizations {
		id := lastPathSegment(url)
		authz, err := ra.SA.GetAuthorization(id)
		if err != nil || // Couldn't find authorization
			authz.RegistrationID != registration.ID ||
			authz.Status != core.StatusValid || // Not finalized or not successful
			authz.Expires.Before(now) || // Expired
			authz.Identifier.Type != core.IdentifierDNS {
			// XXX: It may be good to fail here instead of ignoring invalid authorizations.
			//      However, it seems like this treatment is more in the spirit of Postel's
			//      law, and it hides information from attackers.
			continue
		}

		if authz.Expires.Before(earliestExpiry) {
			earliestExpiry = authz.Expires
		}

		for _, challenge := range authz.Challenges {
			if challenge.Status == core.StatusValid {
				verificationMethodSet[challenge.Type] = true
			}
		}

		authorizedDomains[authz.Identifier.Value] = true
	}
	verificationMethods := []string{}
	for method, _ := range verificationMethodSet {
		verificationMethods = append(verificationMethods, method)
	}
	logEvent.VerificationMethods = verificationMethods

	// Validate that authorization key is authorized for all domains
	names := csr.DNSNames
	if len(csr.Subject.CommonName) > 0 {
		names = append(names, csr.Subject.CommonName)
	}

	// Validate all domains
	for _, name := range names {
		if !authorizedDomains[name] {
			err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name))
			logEvent.Error = err.Error()
			return emptyCert, err
		}
	}

	// Mark that we verified the CN and SANs
	logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"}

	// Create the certificate and log the result
	var cert core.Certificate
	if cert, err = ra.CA.IssueCertificate(*csr, regID, earliestExpiry); err != nil {
		logEvent.Error = err.Error()
		return emptyCert, nil
	}

	cert.ParsedCertificate, err = x509.ParseCertificate([]byte(cert.DER))

	logEvent.SerialNumber = cert.ParsedCertificate.SerialNumber
	logEvent.CommonName = cert.ParsedCertificate.Subject.CommonName
	logEvent.NotBefore = cert.ParsedCertificate.NotBefore
	logEvent.NotAfter = cert.ParsedCertificate.NotAfter
	logEvent.ResponseTime = time.Now()

	logEventResult = "successful"
	return cert, nil
}

func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
	base.MergeUpdate(update)
	reg = base
	err = ra.SA.UpdateRegistration(base)
	return
}

func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) {
	// Copy information over that the client is allowed to supply
	authz = base
	if challengeIndex >= len(authz.Challenges) {
		err = core.MalformedRequestError("Invalid challenge index")
		return
	}
	authz.Challenges[challengeIndex] = authz.Challenges[challengeIndex].MergeResponse(response)

	// Store the updated version
	if err = ra.SA.UpdatePendingAuthorization(authz); err != nil {
		return
	}

	// Dispatch to the VA for service
	ra.VA.UpdateValidations(authz, challengeIndex)

	return
}

func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) error {
	serialString := core.SerialToString(cert.SerialNumber)
	err := ra.CA.RevokeCertificate(serialString, 0)

	// AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134
	if err != nil {
		ra.log.Audit(fmt.Sprintf("Revocation error - %s - %s", serialString, err))
	} else {
		ra.log.Audit(fmt.Sprintf("Revocation - %s", serialString))
	}

	return err
}

func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error {
	// Check to see whether the updated validations are sufficient
	// Current policy is to accept if any validation succeeded
	for _, val := range authz.Challenges {
		if val.Status == core.StatusValid {
			authz.Status = core.StatusValid
			break
		}
	}

	// If no validation succeeded, then the authorization is invalid
	// NOTE: This only works because we only ever do one validation
	if authz.Status != core.StatusValid {
		authz.Status = core.StatusInvalid
	} else {
		// TODO: Enable configuration of expiry time
		authz.Expires = time.Now().Add(365 * 24 * time.Hour)
	}

	// Finalize the authorization (error ignored)
	return ra.SA.FinalizeAuthorization(authz)
}
<commit_msg>Attempting to resolve Travis failure<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package ra

import (
	"crypto/x509"
	"fmt"
	"math/big"
	"net/url"
	"regexp"
	"strconv"
	"time"

	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
	"github.com/letsencrypt/boulder/policy"
)

// All of the fields in RegistrationAuthorityImpl need to be
// populated, or there is a risk of panic.
type RegistrationAuthorityImpl struct {
	CA  core.CertificateAuthority
	VA  core.ValidationAuthority
	SA  core.StorageAuthority
	PA  core.PolicyAuthority
	log *blog.AuditLogger

	AuthzBase string
}

func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl {
	logger := blog.GetAuditLogger()
	logger.Notice("Registration Authority Starting")

	ra := RegistrationAuthorityImpl{log: logger}
	ra.PA = policy.NewPolicyAuthorityImpl()
	return ra
}

var allButLastPathSegment = regexp.MustCompile("^.*/")

func lastPathSegment(url core.AcmeURL) string {
	return allButLastPathSegment.ReplaceAllString(url.Path, "")
}

type certificateRequestEvent struct {
	ID                  string    `json:",omitempty"`
	Requester           int64     `json:",omitempty"`
	SerialNumber        *big.Int  `json:",omitempty"`
	RequestMethod       string    `json:",omitempty"`
	VerificationMethods []string  `json:",omitempty"`
	VerifiedFields      []string  `json:",omitempty"`
	CommonName          string    `json:",omitempty"`
	Names               []string  `json:",omitempty"`
	NotBefore           time.Time `json:",omitempty"`
	NotAfter            time.Time `json:",omitempty"`
	RequestTime         time.Time `json:",omitempty"`
	ResponseTime        time.Time `json:",omitempty"`
	Error               string    `json:",omitempty"`
}

func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) {
	reg = core.Registration{
		RecoveryToken: core.NewToken(),
		Key:           init.Key,
	}
	reg.MergeUpdate(init)

	// Store the authorization object, then return it
	reg, err = ra.SA.NewRegistration(reg)
	return
}

func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
	if regID <= 0 {
		err = fmt.Errorf("Invalid registration ID")
		return authz, err
	}

	identifier := request.Identifier

	// Check that the identifier is present and appropriate
	if err = ra.PA.WillingToIssue(identifier); err != nil {
		return authz, err
	}

	// Create validations
	// TODO: Assign URLs
	challenges, combinations := ra.PA.ChallengesFor(identifier)
	authID, err := ra.SA.NewPendingAuthorization()
	if err != nil {
		return authz, err
	}
	for i := range challenges {
		// Ignoring these errors because we construct the URLs to be correct
		challengeURI, _ := url.Parse(ra.AuthzBase + authID + "?challenge=" + strconv.Itoa(i))
		challenges[i].URI = core.AcmeURL(*challengeURI)

		if !challenges[i].IsSane(false) {
			err = fmt.Errorf("Challenge didn't pass sanity check: %+v", challenges[i])
			return authz, err
		}
	}

	// Create a new authorization object
	authz = core.Authorization{
		ID:             authID,
		Identifier:     identifier,
		RegistrationID: regID,
		Status:         core.StatusPending,
		Challenges:     challenges,
		Combinations:   combinations,
	}

	// Store the authorization object, then return it
	err = ra.SA.UpdatePendingAuthorization(authz)
	return authz, err
}

func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (core.Certificate, error) {
	emptyCert := core.Certificate{}
	var err error
	var logEventResult string

	// Assume the worst
	logEventResult = "error"

	// Construct the log event
	logEvent := certificateRequestEvent{
		ID:            core.NewToken(),
		Requester:     regID,
		RequestMethod: "online",
		RequestTime:   time.Now(),
	}

	// No matter what, log the request
	defer func() {
		// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
		ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent)
	}()

	if regID <= 0 {
		err = fmt.Errorf("Invalid registration ID")
		return emptyCert, err
	}

	// Verify the CSR
	// TODO: Verify that other aspects of the CSR are appropriate
	csr := req.CSR
	if err = core.VerifyCSR(csr); err != nil {
		logEvent.Error = err.Error()
		err = core.UnauthorizedError("Invalid signature on CSR")
		return emptyCert, err
	}

	logEvent.CommonName = csr.Subject.CommonName
	logEvent.Names = csr.DNSNames

	csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(append(csr.DNSNames, csr.Subject.CommonName))
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}
	if csrPreviousDenied {
		err = core.UnauthorizedError("CSR has already been revoked/denied")
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	registration, err := ra.SA.GetRegistration(regID)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	if core.KeyDigestEquals(csr.PublicKey, registration.Key) {
		err = core.MalformedRequestError("Certificate public key must be different than account key")
		return emptyCert, err
	}

	// Gather authorized domains from the referenced authorizations
	authorizedDomains := map[string]bool{}
	verificationMethodSet := map[string]bool{}
	earliestExpiry := time.Date(2100, 01, 01, 0, 0, 0, 0, time.UTC)
	now := time.Now()
	for _, url := range req.Authorizations {
		id := lastPathSegment(url)
		authz, err := ra.SA.GetAuthorization(id)
		if err != nil || // Couldn't find authorization
			authz.RegistrationID != registration.ID ||
			authz.Status != core.StatusValid || // Not finalized or not successful
			authz.Expires.Before(now) || // Expired
			authz.Identifier.Type != core.IdentifierDNS {
			// XXX: It may be good to fail here instead of ignoring invalid authorizations.
			//      However, it seems like this treatment is more in the spirit of Postel's
			//      law, and it hides information from attackers.
			continue
		}

		if authz.Expires.Before(earliestExpiry) {
			earliestExpiry = authz.Expires
		}

		for _, challenge := range authz.Challenges {
			if challenge.Status == core.StatusValid {
				verificationMethodSet[challenge.Type] = true
			}
		}

		authorizedDomains[authz.Identifier.Value] = true
	}
	verificationMethods := []string{}
	for method, _ := range verificationMethodSet {
		verificationMethods = append(verificationMethods, method)
	}
	logEvent.VerificationMethods = verificationMethods

	// Validate that authorization key is authorized for all domains
	names := csr.DNSNames
	if len(csr.Subject.CommonName) > 0 {
		names = append(names, csr.Subject.CommonName)
	}

	// Validate all domains
	for _, name := range names {
		if !authorizedDomains[name] {
			err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name))
			logEvent.Error = err.Error()
			return emptyCert, err
		}
	}

	// Mark that we verified the CN and SANs
	logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"}

	// Create the certificate and log the result
	var cert core.Certificate
	if cert, err = ra.CA.IssueCertificate(*csr, regID, earliestExpiry); err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	cert.ParsedCertificate, err = x509.ParseCertificate([]byte(cert.DER))

	logEvent.SerialNumber = cert.ParsedCertificate.SerialNumber
	logEvent.CommonName = cert.ParsedCertificate.Subject.CommonName
	logEvent.NotBefore = cert.ParsedCertificate.NotBefore
	logEvent.NotAfter = cert.ParsedCertificate.NotAfter
	logEvent.ResponseTime = time.Now()

	logEventResult = "successful"
	return cert, nil
}

func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
	base.MergeUpdate(update)
	reg = base
	err = ra.SA.UpdateRegistration(base)
	return
}

func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) {
	// Copy information over that the client is allowed to supply
	authz = base
	if challengeIndex >= len(authz.Challenges) {
		err = core.MalformedRequestError("Invalid challenge index")
		return
	}
	authz.Challenges[challengeIndex] = authz.Challenges[challengeIndex].MergeResponse(response)

	// Store the updated version
	if err = ra.SA.UpdatePendingAuthorization(authz); err != nil {
		return
	}

	// Dispatch to the VA for service
	ra.VA.UpdateValidations(authz, challengeIndex)

	return
}

func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) error {
	serialString := core.SerialToString(cert.SerialNumber)
	err := ra.CA.RevokeCertificate(serialString, 0)

	// AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134
	if err != nil {
		ra.log.Audit(fmt.Sprintf("Revocation error - %s - %s", serialString, err))
	} else {
		ra.log.Audit(fmt.Sprintf("Revocation - %s", serialString))
	}

	return err
}

func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error {
	// Check to see whether the updated validations are sufficient
	// Current policy is to accept if any validation succeeded
	for _, val := range authz.Challenges {
		if val.Status == core.StatusValid {
			authz.Status = core.StatusValid
			break
		}
	}

	// If no validation succeeded, then the authorization is invalid
	// NOTE: This only works because we only ever do one validation
	if authz.Status != core.StatusValid {
		authz.Status = core.StatusInvalid
	} else {
		// TODO: Enable configuration of expiry time
		authz.Expires = time.Now().Add(365 * 24 * time.Hour)
	}

	// Finalize the authorization (error ignored)
	return ra.SA.FinalizeAuthorization(authz)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"

	"github.com/steder/gophernaut"
)

// Event is basically just an enum
type Event int

// Events that can be generated by our child processes
const (
	Start Event = iota
	Shutdown
	PiningForTheFjords
)

// TODO look into "go generate stringer -type Event"
func (e Event) String() string {
	return fmt.Sprintf("Event(%d)", e)
}

var hostnames = []string{
	fmt.Sprintf("http://127.0.0.1:%d", 8080),
	fmt.Sprintf("http://127.0.0.1:%d", 8081),
}

var executables = []string{
	fmt.Sprintf("python -m SimpleHTTPServer %d", 8080),
	fmt.Sprintf("python -m SimpleHTTPServer %d", 8081),
}

func copyToLog(dst *log.Logger, src io.Reader) {
	scanner := bufio.NewScanner(src)
	for scanner.Scan() {
		dst.Print(scanner.Text())
	}
}

func startProcess(control <-chan Event, events chan<- Event, executable string) {
	procLog := log.New(os.Stdout, fmt.Sprintf("gopher-worker(%s) ", executable), log.Ldate|log.Ltime)

	commandParts := strings.Split(executable, " ")
	command := exec.Command(commandParts[0], commandParts[1:]...)
	log.Printf("Command: %v\n", command)

	stdout, err := command.StdoutPipe()
	if err != nil {
		procLog.Fatalln("Unable to connect to stdout from command...")
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		procLog.Fatalln("Unable to connect to stderr from command...")
	}

	//go io.Copy(os.Stdout, stdout)
	//go io.Copy(os.Stderr, stderr)
	go copyToLog(procLog, stdout)
	go copyToLog(procLog, stderr)
	command.Start()

	for {
		_, ok := <-control
		if !ok {
			fmt.Println("Killing worker process after receiving close event.")
			command.Process.Kill()
			events <- Shutdown
			break
		}
	}
}

var requestCount = 0

func myHandler(w http.ResponseWriter, myReq *http.Request) {
	requestPath := myReq.URL.Path
	// TODO: multiprocess, pick one of n hostnames based on pool status
	hostname := hostnames[requestCount%len(hostnames)]
	requestCount++
	targetURL, _ := url.Parse(hostname)
	director := func(req *http.Request) {
		targetQuery := targetURL.RawQuery
		req.URL.Scheme = targetURL.Scheme
		// TODO: adjust request host to assign the request to the appropriate child process
		req.URL.Host = targetURL.Host

		// clean up but preserve trailing slash:
		trailing := strings.HasSuffix(req.URL.Path, "/")
		req.URL.Path = path.Join(targetURL.Path, req.URL.Path)
		if trailing && !strings.HasSuffix(req.URL.Path, "/") {
			req.URL.Path += "/"
		}

		// preserve query string:
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	proxy := &httputil.ReverseProxy{Director: director}

	staticHandler := http.StripPrefix("/static", http.FileServer(http.Dir("static")))
	adminTemplate := template.Must(template.ParseFiles("templates/admin.html"))
	adminHandler := func(w http.ResponseWriter, req *http.Request) {
		adminTemplate.Execute(w, nil)
	}

	switch {
	case requestPath == "/admin":
		adminHandler(w, myReq)
		return
	case strings.HasPrefix(requestPath, "/static"):
		staticHandler.ServeHTTP(w, myReq)
		return
	}
	proxy.ServeHTTP(w, myReq)
}

func main() {
	log.SetPrefix("gophernaut ")
	log.SetFlags(log.Ldate | log.Ltime)
	c := gophernaut.ReadConfig()
	log.Printf("Host %s and Port %d\n", c.Host, c.Port)

	controlChannel := make(chan Event)
	eventsChannel := make(chan Event)

	// Handle signals to try to do a graceful shutdown:
	receivedSignals := make(chan os.Signal, 1)
	signal.Notify(receivedSignals, os.Interrupt) // , syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for sig := range receivedSignals {
			fmt.Printf("Received signal, %s, shutting down workers...\n", sig)
			break
		}
		close(controlChannel)
		signal.Stop(receivedSignals)
	}()

	// Actually start some processes
	for _, executable := range executables {
		go startProcess(controlChannel, eventsChannel, executable)
	}

	// wait for child processes to exit before shutting down:
	processCount := len(executables)
	stoppedCount := 0
	go func() {
		for event := range eventsChannel {
			if event == Shutdown {
				stoppedCount++
			}
			if processCount == stoppedCount {
				fmt.Printf("%d workers stopped, shutting down.\n", processCount)
				os.Exit(1)
			}
		}
	}()

	log.Printf("Gophernaut is gopher launch!\n")
	// TODO: our own ReverseProxy implementation of at least, ServeHTTP so that we can
	// monitor the response codes to track successes and failures
	log.Fatal(http.ListenAndServe(":8483", http.HandlerFunc(myHandler)))
}
<commit_msg>A little less hard coding<commit_after>package main

import (
	"bufio"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"

	"github.com/steder/gophernaut"
)

// Event is basically just an enum
type Event int

// Events that can be generated by our child processes
const (
	Start Event = iota
	Shutdown
	PiningForTheFjords
)

// TODO look into "go generate stringer -type Event"
func (e Event) String() string {
	return fmt.Sprintf("Event(%d)", e)
}

var hostnames = []string{
	fmt.Sprintf("http://127.0.0.1:%d", 8080),
	fmt.Sprintf("http://127.0.0.1:%d", 8081),
}

var executables = []string{
	fmt.Sprintf("python -m SimpleHTTPServer %d", 8080),
	fmt.Sprintf("python -m SimpleHTTPServer %d", 8081),
}

func copyToLog(dst *log.Logger, src io.Reader) {
	scanner := bufio.NewScanner(src)
	for scanner.Scan() {
		dst.Print(scanner.Text())
	}
}

func startProcess(control <-chan Event, events chan<- Event, executable string) {
	procLog := log.New(os.Stdout, fmt.Sprintf("gopher-worker(%s) ", executable), log.Ldate|log.Ltime)

	commandParts := strings.Split(executable, " ")
	command := exec.Command(commandParts[0], commandParts[1:]...)
	log.Printf("Command: %v\n", command)

	stdout, err := command.StdoutPipe()
	if err != nil {
		procLog.Fatalln("Unable to connect to stdout from command...")
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		procLog.Fatalln("Unable to connect to stderr from command...")
	}

	//go io.Copy(os.Stdout, stdout)
	//go io.Copy(os.Stderr, stderr)
	go copyToLog(procLog, stdout)
	go copyToLog(procLog, stderr)
	command.Start()

	events <- Start
	for {
		_, ok := <-control
		if !ok {
			fmt.Println("Killing worker process after receiving close event.")
			command.Process.Kill()
			events <- Shutdown
			break
		}
	}
}

var requestCount = 0

func myHandler(w http.ResponseWriter, myReq *http.Request) {
	requestPath := myReq.URL.Path
	// TODO: multiprocess, pick one of n hostnames based on pool status
	hostname := hostnames[requestCount%len(hostnames)]
	requestCount++
	targetURL, _ := url.Parse(hostname)
	director := func(req *http.Request) {
		targetQuery := targetURL.RawQuery
		req.URL.Scheme = targetURL.Scheme
		// TODO: adjust request host to assign the request to the appropriate child process
		req.URL.Host = targetURL.Host

		// clean up but preserve trailing slash:
		trailing := strings.HasSuffix(req.URL.Path, "/")
		req.URL.Path = path.Join(targetURL.Path, req.URL.Path)
		if trailing && !strings.HasSuffix(req.URL.Path, "/") {
			req.URL.Path += "/"
		}

		// preserve query string:
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	proxy := &httputil.ReverseProxy{Director: director}

	staticHandler := http.StripPrefix("/static", http.FileServer(http.Dir("static")))
	adminTemplate := template.Must(template.ParseFiles("templates/admin.html"))
	adminHandler := func(w http.ResponseWriter, req *http.Request) {
		adminTemplate.Execute(w, nil)
	}

	switch {
	case requestPath == "/admin":
		adminHandler(w, myReq)
		return
	case strings.HasPrefix(requestPath, "/static"):
		staticHandler.ServeHTTP(w, myReq)
		return
	}
	proxy.ServeHTTP(w, myReq)
}

func main() {
	log.SetPrefix("gophernaut ")
	log.SetFlags(log.Ldate | log.Ltime)
	c := gophernaut.ReadConfig()
	log.Printf("Host %s and Port %d\n", c.Host, c.Port)

	controlChannel := make(chan Event)
	eventsChannel := make(chan Event)

	// Handle signals to try to do a graceful shutdown:
	receivedSignals := make(chan os.Signal, 1)
	signal.Notify(receivedSignals, os.Interrupt) // , syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for sig := range receivedSignals {
			fmt.Printf("Received signal, %s, shutting down workers...\n", sig)
			break
		}
		close(controlChannel)
		signal.Stop(receivedSignals)
	}()

	// Actually start some processes
	for _, executable := range executables {
		go startProcess(controlChannel, eventsChannel, executable)
	}

	// wait for child processes to exit before shutting down:

	processCount := 0
	stoppedCount := 0
	go func() {
		// TODO: turn this into a ProcessPool?
		for event := range eventsChannel {
			switch event {
			case Shutdown:
				stoppedCount++
			case Start:
				processCount++
			}
			if processCount == stoppedCount {
				fmt.Printf("%d workers stopped, shutting down.\n", processCount)
				os.Exit(1)
			}
		}
	}()

	log.Printf("Gophernaut is gopher launch!\n")
	// TODO: our own ReverseProxy implementation of at least, ServeHTTP so that we can
	// monitor the response codes to track successes and failures
	log.Fatal(http.ListenAndServe(":8483", http.HandlerFunc(myHandler)))
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"upspin.io/config"
	"upspin.io/flags"
	"upspin.io/upspin"
	"upspin.io/user"
)

func (s *State) signup(args ...string) {
	const help = `
Signup generates an Upspin configuration file and private/public key pair,
stores them locally, and sends a signup request to the public Upspin key server
at key.upspin.io. The server will respond by sending a confirmation email to
the given email address (or "username").

Signup writes a the configuration file to $HOME/upspin/config, holding the
username and the location of the directory and store servers. It writes the
public and private keys to $HOME/.ssh. These locations may be set using the
global -config and signup-specific -where flags.

The -dir and -store flags specify the network addresses of the Store and
Directory servers that the Upspin user will use. The -server flag may be used
to specify a single server that acts as both Store and Directory, in which case
the -dir and -store flags must not be set.

By default, signup creates new keys with the p256 cryptographic curve set.
The -curve and -secretseed flags allow the user to control the curve or to
recreate or reuse prior keys.

The -signuponly flag tells signup to skip the generation of the configuration
file and keys and only send the signup request to the key server.
`
	fs := flag.NewFlagSet("signup", flag.ExitOnError)
	var (
		force       = fs.Bool("force", false, "create a new user even if keys and config file exist")
		where       = fs.String("where", filepath.Join(os.Getenv("HOME"), ".ssh"), "`directory` to store keys")
		dirServer   = fs.String("dir", "", "Directory server `address`")
		storeServer = fs.String("store", "", "Store server `address`")
		bothServer  = fs.String("server", "", "Store and Directory server `address` (if combined)")
		signupOnly  = fs.Bool("signuponly", false, "only send signup request to key server; do not generate config or keys")
	)
	// Used only in keygen.
	fs.String("curve", "p256", "cryptographic curve `name`: p256, p384, or p521")
	fs.String("secretseed", "", "128 bit secret `seed` in proquint format")

	s.parseFlags(fs, args, help, "[-config=<file>] signup [flags] <username>")

	// Determine config file location.
	if !filepath.IsAbs(flags.Config) {
		// User must have a home dir in the local OS.
		homedir, err := config.Homedir()
		if err != nil {
			s.exit(err)
		}
		flags.Config = filepath.Join(homedir, flags.Config)
	}

	if *signupOnly {
		// Don't generate; just send the signup request to the key server.
		s.registerUser(flags.Config)
		return
	}

	// Check flags.
	if fs.NArg() != 1 {
		fs.Usage()
	}
	if *bothServer != "" {
		if *dirServer != "" || *storeServer != "" {
			s.failf("if -server provided -dir and -store must not be set")
			fs.Usage()
		}
		*dirServer = *bothServer
		*storeServer = *bothServer
	}
	if *dirServer == "" || *storeServer == "" {
		s.failf("-dir and -store must both be provided")
		fs.Usage()
	}

	// Parse -dir and -store flags as addresses and construct remote endpoints.
	dirEndpoint, err := parseAddress(*dirServer)
	if err != nil {
		s.exitf("error parsing -dir=%q: %v", dirServer, err)
	}
	storeEndpoint, err := parseAddress(*storeServer)
	if err != nil {
		s.exitf("error parsing -store=%q: %v", storeServer, err)
	}

	// Parse user name.
	uname, _, domain, err := user.Parse(upspin.UserName(fs.Arg(0)))
	if err != nil {
		s.exitf("invalid user name %q: %v", fs.Arg(0), err)
	}
	userName := upspin.UserName(uname + "@" + domain)

	env := os.Environ()
	wipeUpspinEnvironment()
	defer restoreEnvironment(env)

	// Verify if we have a config file.
	_, err = config.FromFile(flags.Config)
	if err == nil && !*force {
		s.exitf("%s already exists", flags.Config)
	}

	// Write the config file.
	var configContents bytes.Buffer
	err = configTemplate.Execute(&configContents, configData{
		UserName:  userName,
		Dir:       dirEndpoint,
		Store:     storeEndpoint,
		SecretDir: *where,
		Packing:   "ee",
	})
	if err != nil {
		s.exit(err)
	}
	err = ioutil.WriteFile(flags.Config, configContents.Bytes(), 0640)
	if err != nil {
		// Directory doesn't exist, perhaps.
		if !os.IsNotExist(err) {
			s.exitf("cannot create %s: %v", flags.Config, err)
		}
		dir := filepath.Dir(flags.Config)
		if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
			// Looks like the directory exists, so stop now and report original error.
			s.exitf("cannot create %s: %v", flags.Config, err)
		}
		if mkdirErr := os.Mkdir(dir, 0700); mkdirErr != nil {
			s.exitf("cannot make directory %s: %v", dir, mkdirErr)
		}
		err = ioutil.WriteFile(flags.Config, configContents.Bytes(), 0640)
		if err != nil {
			s.exit(err)
		}
	}
	fmt.Println("Configuration file written to:")
	fmt.Printf("\t%s\n\n", flags.Config)

	// Generate a new key.
	s.keygenCommand(fs)

	// Send the signup request to the key server.
	s.registerUser(flags.Config)
}

// registerUser reads the config file and sends its information to the key server.
func (s *State) registerUser(configFile string) {
	cfg, err := config.FromFile(configFile)
	if err != nil {
		s.exit(err)
	}

	// Make signup request.
	vals := url.Values{
		"name":  {string(cfg.UserName())},
		"dir":   {string(cfg.DirEndpoint().NetAddr)},
		"store": {string(cfg.StoreEndpoint().NetAddr)},
		"key":   {string(cfg.Factotum().PublicKey())},
	}
	signupURL := (&url.URL{
		Scheme:   "https",
		Host:     "key.upspin.io",
		Path:     "/signup",
		RawQuery: vals.Encode(),
	}).String()

	r, err := http.Post(signupURL, "text/plain", nil)
	if err != nil {
		s.exit(err)
	}
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		s.exit(err)
	}
	if r.StatusCode != http.StatusOK {
		s.exitf("key server error: %s", b)
	}
	fmt.Printf("A signup email has been sent to %q,\n", cfg.UserName())
	fmt.Println("please read it for further instructions.")
}

type configData struct {
	UserName   upspin.UserName
	Store, Dir *upspin.Endpoint
	SecretDir  string
	Packing    string
}

var configTemplate = template.Must(template.New("config").Parse(`
username: {{.UserName}}
secrets: {{.SecretDir}}
storeserver: {{.Store}}
dirserver: {{.Dir}}
packing: {{.Packing}}
`))

func parseAddress(a string) (*upspin.Endpoint, error) {
	host, port, err := net.SplitHostPort(a)
	if err != nil {
		var err2 error
		host, port, err2 = net.SplitHostPort(a + ":443")
		if err2 != nil {
			return nil, err
		}
	}
	return upspin.ParseEndpoint(fmt.Sprintf("remote,%s:%s", host, port))
}

func wipeUpspinEnvironment() {
	for _, env := range os.Environ() {
		if strings.HasPrefix(env, "upspin") {
			os.Setenv(env, "")
		}
	}
}

func restoreEnvironment(env []string) {
	for _, e := range env {
		kv := strings.Split(e, "=")
		if len(kv) != 2 {
			continue
		}
		os.Setenv(kv[0], kv[1])
	}
}
<commit_msg>cmd/upspin: fix typo in help text for signup command<commit_after>// Copyright 2016 The Upspin 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"upspin.io/config"
	"upspin.io/flags"
	"upspin.io/upspin"
	"upspin.io/user"
)

func (s *State) signup(args ...string) {
	const help = `
Signup generates an Upspin configuration file and private/public key pair,
stores them locally, and sends a signup request to the public Upspin key server
at key.upspin.io. The server will respond by sending a confirmation email to
the given email address (or "username").

Signup writes a configuration file to $HOME/upspin/config, holding the
username and the location of the directory and store servers. It writes the
public and private keys to $HOME/.ssh. These locations may be set using the
global -config and signup-specific -where flags.

The -dir and -store flags specify the network addresses of the Store and
Directory servers that the Upspin user will use. The -server flag may be used
to specify a single server that acts as both Store and Directory, in which case
the -dir and -store flags must not be set.

By default, signup creates new keys with the p256 cryptographic curve set.
The -curve and -secretseed flags allow the user to control the curve or to
recreate or reuse prior keys.

The -signuponly flag tells signup to skip the generation of the configuration
file and keys and only send the signup request to the key server.
`
	fs := flag.NewFlagSet("signup", flag.ExitOnError)
	var (
		force       = fs.Bool("force", false, "create a new user even if keys and config file exist")
		where       = fs.String("where", filepath.Join(os.Getenv("HOME"), ".ssh"), "`directory` to store keys")
		dirServer   = fs.String("dir", "", "Directory server `address`")
		storeServer = fs.String("store", "", "Store server `address`")
		bothServer  = fs.String("server", "", "Store and Directory server `address` (if combined)")
		signupOnly  = fs.Bool("signuponly", false, "only send signup request to key server; do not generate config or keys")
	)
	// Used only in keygen.
	fs.String("curve", "p256", "cryptographic curve `name`: p256, p384, or p521")
	fs.String("secretseed", "", "128 bit secret `seed` in proquint format")

	s.parseFlags(fs, args, help, "[-config=<file>] signup [flags] <username>")

	// Determine config file location.
	if !filepath.IsAbs(flags.Config) {
		// User must have a home dir in the local OS.
		homedir, err := config.Homedir()
		if err != nil {
			s.exit(err)
		}
		flags.Config = filepath.Join(homedir, flags.Config)
	}

	if *signupOnly {
		// Don't generate; just send the signup request to the key server.
		s.registerUser(flags.Config)
		return
	}

	// Check flags.
	if fs.NArg() != 1 {
		fs.Usage()
	}
	if *bothServer != "" {
		if *dirServer != "" || *storeServer != "" {
			s.failf("if -server provided -dir and -store must not be set")
			fs.Usage()
		}
		*dirServer = *bothServer
		*storeServer = *bothServer
	}
	if *dirServer == "" || *storeServer == "" {
		s.failf("-dir and -store must both be provided")
		fs.Usage()
	}

	// Parse -dir and -store flags as addresses and construct remote endpoints.
	dirEndpoint, err := parseAddress(*dirServer)
	if err != nil {
		s.exitf("error parsing -dir=%q: %v", dirServer, err)
	}
	storeEndpoint, err := parseAddress(*storeServer)
	if err != nil {
		s.exitf("error parsing -store=%q: %v", storeServer, err)
	}

	// Parse user name.
	uname, _, domain, err := user.Parse(upspin.UserName(fs.Arg(0)))
	if err != nil {
		s.exitf("invalid user name %q: %v", fs.Arg(0), err)
	}
	userName := upspin.UserName(uname + "@" + domain)

	env := os.Environ()
	wipeUpspinEnvironment()
	defer restoreEnvironment(env)

	// Verify if we have a config file.
	_, err = config.FromFile(flags.Config)
	if err == nil && !*force {
		s.exitf("%s already exists", flags.Config)
	}

	// Write the config file.
	var configContents bytes.Buffer
	err = configTemplate.Execute(&configContents, configData{
		UserName:  userName,
		Dir:       dirEndpoint,
		Store:     storeEndpoint,
		SecretDir: *where,
		Packing:   "ee",
	})
	if err != nil {
		s.exit(err)
	}
	err = ioutil.WriteFile(flags.Config, configContents.Bytes(), 0640)
	if err != nil {
		// Directory doesn't exist, perhaps.
		if !os.IsNotExist(err) {
			s.exitf("cannot create %s: %v", flags.Config, err)
		}
		dir := filepath.Dir(flags.Config)
		if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
			// Looks like the directory exists, so stop now and report original error.
			s.exitf("cannot create %s: %v", flags.Config, err)
		}
		if mkdirErr := os.Mkdir(dir, 0700); mkdirErr != nil {
			s.exitf("cannot make directory %s: %v", dir, mkdirErr)
		}
		err = ioutil.WriteFile(flags.Config, configContents.Bytes(), 0640)
		if err != nil {
			s.exit(err)
		}
	}
	fmt.Println("Configuration file written to:")
	fmt.Printf("\t%s\n\n", flags.Config)

	// Generate a new key.
	s.keygenCommand(fs)

	// Send the signup request to the key server.
	s.registerUser(flags.Config)
}

// registerUser reads the config file and sends its information to the key server.
func (s *State) registerUser(configFile string) {
	cfg, err := config.FromFile(configFile)
	if err != nil {
		s.exit(err)
	}

	// Make signup request.
	vals := url.Values{
		"name":  {string(cfg.UserName())},
		"dir":   {string(cfg.DirEndpoint().NetAddr)},
		"store": {string(cfg.StoreEndpoint().NetAddr)},
		"key":   {string(cfg.Factotum().PublicKey())},
	}
	signupURL := (&url.URL{
		Scheme:   "https",
		Host:     "key.upspin.io",
		Path:     "/signup",
		RawQuery: vals.Encode(),
	}).String()

	r, err := http.Post(signupURL, "text/plain", nil)
	if err != nil {
		s.exit(err)
	}
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		s.exit(err)
	}
	if r.StatusCode != http.StatusOK {
		s.exitf("key server error: %s", b)
	}
	fmt.Printf("A signup email has been sent to %q,\n", cfg.UserName())
	fmt.Println("please read it for further instructions.")
}

type configData struct {
	UserName   upspin.UserName
	Store, Dir *upspin.Endpoint
	SecretDir  string
	Packing    string
}

var configTemplate = template.Must(template.New("config").Parse(`
username: {{.UserName}}
secrets: {{.SecretDir}}
storeserver: {{.Store}}
dirserver: {{.Dir}}
packing: {{.Packing}}
`))

func parseAddress(a string) (*upspin.Endpoint, error) {
	host, port, err := net.SplitHostPort(a)
	if err != nil {
		var err2 error
		host, port, err2 = net.SplitHostPort(a + ":443")
		if err2 != nil {
			return nil, err
		}
	}
	return upspin.ParseEndpoint(fmt.Sprintf("remote,%s:%s", host, port))
}

func wipeUpspinEnvironment() {
	for _, env := range os.Environ() {
		if strings.HasPrefix(env, "upspin") {
			os.Setenv(env, "")
		}
	}
}

func restoreEnvironment(env []string) {
	for _, e := range env {
		kv := strings.Split(e, "=")
		if len(kv) != 2 {
			continue
		}
		os.Setenv(kv[0], kv[1])
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/akhenakh/gozim"
	"github.com/golang/groupcache/lru"
)

type ResponseType uint16

const (
	RedirectResponse ResponseType = 0xffff
	DataResponse                  = 0x0000
	NoResponse                    = 0x0404
)

type CachedResponse struct {
	ResponseType ResponseType
	Data         []byte
	MimeType     string
}

var (
	Z     *zim.ZimReader
	Cache *lru.Cache
)

func cacheLookup(url string) (*CachedResponse, bool) {
	if v, ok := Cache.Get(url); ok {
		c := v.(CachedResponse)
		return &c, ok
	}
	return nil, false
}

// dealing with
func handleCachedResponse(cr *CachedResponse, w http.ResponseWriter, r *http.Request) {
	if cr.ResponseType == RedirectResponse {
		fmt.Printf("302 from %s to %s\n", r.URL.Path, string(cr.Data))
		http.Redirect(w, r, string(cr.Data), http.StatusFound)
	} else if cr.ResponseType == NoResponse {
		fmt.Printf("404 %s\n", r.URL.Path)
		http.NotFound(w, r)
	} else if cr.ResponseType == DataResponse {
		fmt.Printf("200 %s\n", r.URL.Path)
		w.Header().Set("Content-Type", cr.MimeType)
		w.Write(cr.Data)
	}
}

// the handler receiving http request
func handler(w http.ResponseWriter, r *http.Request) {

	url := r.URL.Path[1:]

	if cr, iscached := cacheLookup(url); iscached {
		handleCachedResponse(cr, w, r)
		return

	} else {
		var a *zim.Article

		if url == "index.html" {
			a = Z.GetMainPage()
		} else {
			a = Z.GetPageNoIndex(url)
		}

		if a == nil {
			Cache.Add(r.URL.Path[1:], CachedResponse{ResponseType: NoResponse})
		} else if a.Mimetype == zim.RedirectEntry {
			Cache.Add(r.URL.Path[1:], CachedResponse{
				ResponseType: RedirectResponse,
				Data:         []byte(a.RedirectTo.FullURL())})
		} else {
			Cache.Add(r.URL.Path[1:], CachedResponse{
				ResponseType: DataResponse,
				Data:         a.Data(Z),
				MimeType:     Z.MimeTypes()[a.Mimetype],
			})
		}

		// look again in the cache for the same entry
		if cr, iscached := cacheLookup(url); iscached {
			handleCachedResponse(cr, w, r)
		}
	}
}

func main() {
	http.HandleFunc("/", handler)
	z, err := zim.NewReader("test.zim")
	Z = z
	if err != nil {
		panic(err)
	}

	// the need of a cache is absolute
	// a lots of urls will be called repeatedly, css, js ...
	Cache = lru.New(30)

	http.ListenAndServe(":8080", nil)

}
<commit_msg>redirect from / ...<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/akhenakh/gozim"
	"github.com/golang/groupcache/lru"
)

type ResponseType uint16

const (
	RedirectResponse ResponseType = 0xffff
	DataResponse                  = 0x0000
	NoResponse                    = 0x0404
)

type CachedResponse struct {
	ResponseType ResponseType
	Data         []byte
	MimeType     string
}

var (
	Z     *zim.ZimReader
	Cache *lru.Cache
)

func cacheLookup(url string) (*CachedResponse, bool) {
	if v, ok := Cache.Get(url); ok {
		c := v.(CachedResponse)
		return &c, ok
	}
	return nil, false
}

// dealing with cached response, responding directly
func handleCachedResponse(cr *CachedResponse, w http.ResponseWriter, r *http.Request) {
	if cr.ResponseType == RedirectResponse {
		fmt.Printf("302 from %s to %s\n", r.URL.Path, string(cr.Data))
		http.Redirect(w, r, "/"+string(cr.Data), http.StatusFound)
	} else if cr.ResponseType == NoResponse {
		fmt.Printf("404 %s\n", r.URL.Path)
		http.NotFound(w, r)
	} else if cr.ResponseType == DataResponse {
		fmt.Printf("200 %s\n", r.URL.Path)
		w.Header().Set("Content-Type", cr.MimeType)
		w.Write(cr.Data)
	}
}

// the handler receiving http request
func handler(w http.ResponseWriter, r *http.Request) {

	url := r.URL.Path[1:]

	if cr, iscached := cacheLookup(url); iscached {
		handleCachedResponse(cr, w, r)
		return

	} else {
		var a *zim.Article

		if url == "index.html" {
			a = Z.GetMainPage()
		} else {
			a = Z.GetPageNoIndex(url)
		}

		if a == nil {
			Cache.Add(r.URL.Path[1:], CachedResponse{ResponseType: NoResponse})
		} else if a.Mimetype == zim.RedirectEntry {
			Cache.Add(r.URL.Path[1:], CachedResponse{
				ResponseType: RedirectResponse,
				Data:         []byte(a.RedirectTo.FullURL())})
		} else {
			Cache.Add(r.URL.Path[1:], CachedResponse{
				ResponseType: DataResponse,
				Data:         a.Data(Z),
				MimeType:     Z.MimeTypes()[a.Mimetype],
			})
		}

		// look again in the cache for the same entry
		if cr, iscached := cacheLookup(url); iscached {
			handleCachedResponse(cr, w, r)
		}
	}
}

func main() {
	http.HandleFunc("/", handler)
	z, err := zim.NewReader("test.zim")
	Z = z
	if err != nil {
		panic(err)
	}

	// the need of a cache is absolute
	// a lots of urls will be called repeatedly, css, js ...
	Cache = lru.New(30)

	http.ListenAndServe(":8080", nil)

}
<|endoftext|>