<commit_before>// specgen generates Go code from the UPnP specification files.
//
// The specification is available for download from:
// http://upnp.org/resources/upnpresources.zip
package main

import (
	"archive/zip"
	"bytes"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/huin/goupnp"
	"github.com/huin/goupnp/scpd"
)

// flags
var (
	specFilename = flag.String("spec", "", "Path to the specification file.")
	outDir       = flag.String("out-dir", "", "Path to the output directory.")
	enableGofmt  = flag.Bool("gofmt", true, "Pass the output through gofmt. "+
		"Disable if debugging code output problems.")
)

var (
	deviceURNPrefix  = "urn:schemas-upnp-org:device:"
	serviceURNPrefix = "urn:schemas-upnp-org:service:"
)

func main() {
	flag.Parse()

	if *specFilename == "" {
		log.Fatal("--spec is required")
	}
	if *outDir == "" {
		log.Fatal("--out-dir is required")
	}

	specArchive, err := openZipfile(*specFilename)
	if err != nil {
		log.Fatalf("Error opening %s: %v", *specFilename)
	}
	defer specArchive.Close()

	dcpsCol := newDcpsCollection(map[string]string{
		"Internet Gateway_1": "internetgateway1",
		"Internet Gateway_2": "internetgateway2",
	})
	for _, f := range globFiles("standardizeddcps/*/*.zip", specArchive.Reader) {
		dirName := strings.TrimPrefix(f.Name, "standardizeddcps/")
		slashIndex := strings.Index(dirName, "/")
		if slashIndex == -1 {
			// Should not happen.
			log.Printf("Could not find / in %q", dirName)
			return
		}
		dirName = dirName[:slashIndex]

		dcp := dcpsCol.dcpsForDir(dirName)
		if dcp == nil {
			log.Printf("No alias defined for directory %q: skipping", dirName)
			continue
		}

		dcp.processZipFile(f)
	}

	for _, dcp := range dcpsCol.dcpsByAlias {
		if err := dcp.writePackage(*outDir); err != nil {
			log.Printf("Error writing package %q: %v", dcp.Name, err)
		}
	}
}

type dcpsCollection struct {
	dcpsAliasByDir map[string]string
	dcpsByAlias    map[string]*DCP
}

func newDcpsCollection(dcpsAliasByDir map[string]string) *dcpsCollection {
	c := &dcpsCollection{
		dcpsAliasByDir: dcpsAliasByDir,
		dcpsByAlias:    make(map[string]*DCP),
	}
	for _, alias := range dcpsAliasByDir {
		c.dcpsByAlias[alias] = newDCP(alias)
	}
	return c
}

func (c dcpsCollection) dcpsForDir(dirName string) *DCP {
	alias, ok := c.dcpsAliasByDir[dirName]
	if !ok {
		return nil
	}
	return c.dcpsByAlias[alias]
}

// DCP collects together information about a UPnP Device Control Protocol.
type DCP struct {
	Name         string
	DeviceTypes  map[string]*URNParts
	ServiceTypes map[string]*URNParts
	Services     []SCPDWithURN
}

func newDCP(name string) *DCP {
	return &DCP{
		Name:         name,
		DeviceTypes:  make(map[string]*URNParts),
		ServiceTypes: make(map[string]*URNParts),
	}
}

func (dcp *DCP) processZipFile(file *zip.File) {
	archive, err := openChildZip(file)
	if err != nil {
		log.Println("Error reading child zip file:", err)
		return
	}
	for _, deviceFile := range globFiles("*/device/*.xml", archive) {
		dcp.processDeviceFile(deviceFile)
	}
	for _, scpdFile := range globFiles("*/service/*.xml", archive) {
		dcp.processSCPDFile(scpdFile)
	}
}

func (dcp *DCP) processDeviceFile(file *zip.File) {
	var device goupnp.Device
	if err := unmarshalXmlFile(file, &device); err != nil {
		log.Printf("Error decoding device XML from file %q: %v", file.Name, err)
		return
	}
	device.VisitDevices(func(d *goupnp.Device) {
		t := strings.TrimSpace(d.DeviceType)
		if t != "" {
			u, err := extractURNParts(t, deviceURNPrefix)
			if err != nil {
				log.Println(err)
				return
			}
			dcp.DeviceTypes[t] = u
		}
	})
	device.VisitServices(func(s *goupnp.Service) {
		u, err := extractURNParts(s.ServiceType, serviceURNPrefix)
		if err != nil {
			log.Println(err)
			return
		}
		dcp.ServiceTypes[s.ServiceType] = u
	})
}

func (dcp *DCP) writePackage(outDir string) error {
	packageDirname := filepath.Join(outDir, dcp.Name)
	err := os.MkdirAll(packageDirname, os.ModePerm)
	if err != nil && !os.IsExist(err) {
		return err
	}
	packageFilename := filepath.Join(packageDirname, dcp.Name+".go")
	packageFile, err := os.Create(packageFilename)
	if err != nil {
		return err
	}
	var output io.WriteCloser = packageFile
	if *enableGofmt {
		if output, err = NewGofmtWriteCloser(output); err != nil {
			packageFile.Close()
			return err
		}
	}
	if err = packageTmpl.Execute(output, dcp); err != nil {
		output.Close()
		return err
	}
	return output.Close()
}

type GofmtWriteCloser struct {
	output io.WriteCloser
	stdin  io.WriteCloser
	gofmt  *exec.Cmd
}

func NewGofmtWriteCloser(output io.WriteCloser) (*GofmtWriteCloser, error) {
	gofmt := exec.Command("gofmt")
	gofmt.Stdout = output
	gofmt.Stderr = os.Stderr
	stdin, err := gofmt.StdinPipe()
	if err != nil {
		return nil, err
	}
	if err = gofmt.Start(); err != nil {
		return nil, err
	}
	return &GofmtWriteCloser{
		output: output,
		stdin:  stdin,
		gofmt:  gofmt,
	}, nil
}

func (gwc *GofmtWriteCloser) Write(p []byte) (int, error) {
	return gwc.stdin.Write(p)
}

func (gwc *GofmtWriteCloser) Close() error {
	gwc.stdin.Close()
	if err := gwc.output.Close(); err != nil {
		gwc.gofmt.Wait()
		return err
	}
	return gwc.gofmt.Wait()
}

func (dcp *DCP) processSCPDFile(file *zip.File) {
	scpd := new(scpd.SCPD)
	if err := unmarshalXmlFile(file, scpd); err != nil {
		log.Printf("Error decoding SCPD XML from file %q: %v", file.Name, err)
		return
	}
	scpd.Clean()
	urnParts, err := urnPartsFromSCPDFilename(file.Name)
	if err != nil {
		log.Printf("Could not recognize SCPD filename %q: %v", file.Name, err)
		return
	}
	dcp.Services = append(dcp.Services, SCPDWithURN{
		URNParts: urnParts,
		SCPD:     scpd,
	})
}

type SCPDWithURN struct {
	*URNParts
	SCPD *scpd.SCPD
}

func (s *SCPDWithURN) WrapArgument(arg scpd.Argument) (*argumentWrapper, error) {
	relVar := s.SCPD.GetStateVariable(arg.RelatedStateVariable)
	if relVar == nil {
		return nil, fmt.Errorf("no such state variable: %q, for argument %q", arg.RelatedStateVariable, arg.Name)
	}
	cnv, ok := typeConvs[relVar.DataType.Name]
	if !ok {
		return nil, fmt.Errorf("unknown data type: %q, for state variable %q, for argument %q", relVar.DataType.Type, arg.RelatedStateVariable, arg.Name)
	}
	return &argumentWrapper{
		Argument: arg,
		relVar:   relVar,
		conv:     cnv,
	}, nil
}

type argumentWrapper struct {
	scpd.Argument
	relVar *scpd.StateVariable
	conv   conv
}

func (arg *argumentWrapper) AsParameter() string {
	return fmt.Sprintf("%s %s", arg.Name, arg.conv.ExtType)
}

func (arg *argumentWrapper) Document() string {
	relVar := arg.relVar
	if rng := relVar.AllowedValueRange; rng != nil {
		var parts []string
		if rng.Minimum != "" {
			parts = append(parts, fmt.Sprintf("minimum=%s", rng.Minimum))
		}
		if rng.Maximum != "" {
			parts = append(parts, fmt.Sprintf("maximum=%s", rng.Maximum))
		}
		if rng.Step != "" {
			parts = append(parts, fmt.Sprintf("step=%s", rng.Step))
		}
		return "allowed value range: " + strings.Join(parts, ", ")
	}
	if len(relVar.AllowedValues) != 0 {
		return "allowed values: " + strings.Join(relVar.AllowedValues, ", ")
	}
	return ""
}

func (arg *argumentWrapper) Marshal() string {
	return fmt.Sprintf("soap.Marshal%s(%s)", arg.conv.FuncSuffix, arg.Name)
}

func (arg *argumentWrapper) Unmarshal(objVar string) string {
	return fmt.Sprintf("soap.Unmarshal%s(%s.%s)", arg.conv.FuncSuffix, objVar, arg.Name)
}

type conv struct {
	FuncSuffix string
	ExtType    string
}

// typeConvs maps from a SOAP type (e.g "fixed.14.4") to the function name
// suffix inside the soap module (e.g "Fixed14_4") and the Go type.
var typeConvs = map[string]conv{
	"ui1":         conv{"Ui1", "uint8"},
	"ui2":         conv{"Ui2", "uint16"},
	"ui4":         conv{"Ui4", "uint32"},
	"i1":          conv{"I1", "int8"},
	"i2":          conv{"I2", "int16"},
	"i4":          conv{"I4", "int32"},
	"int":         conv{"Int", "int64"},
	"r4":          conv{"R4", "float32"},
	"r8":          conv{"R8", "float64"},
	"number":      conv{"R8", "float64"}, // Alias for r8.
	"fixed.14.4":  conv{"Fixed14_4", "float64"},
	"float":       conv{"R8", "float64"},
	"char":        conv{"Char", "rune"},
	"string":      conv{"String", "string"},
	"date":        conv{"Date", "time.Time"},
	"dateTime":    conv{"DateTime", "time.Time"},
	"dateTime.tz": conv{"DateTimeTz", "time.Time"},
	"time":        conv{"TimeOfDay", "soap.TimeOfDay"},
	"time.tz":     conv{"TimeOfDayTz", "soap.TimeOfDay"},
	"boolean":     conv{"Boolean", "bool"},
	"bin.base64":  conv{"BinBase64", "[]byte"},
	"bin.hex":     conv{"BinHex", "[]byte"},
}

type closeableZipReader struct {
	io.Closer
	*zip.Reader
}

func openZipfile(filename string) (*closeableZipReader, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	fi, err := file.Stat()
	if err != nil {
		return nil, err
	}
	archive, err := zip.NewReader(file, fi.Size())
	if err != nil {
		return nil, err
	}
	return &closeableZipReader{
		Closer: file,
		Reader: archive,
	}, nil
}

// openChildZip opens a zip file within another zip file.
func openChildZip(file *zip.File) (*zip.Reader, error) {
	zipFile, err := file.Open()
	if err != nil {
		return nil, err
	}
	defer zipFile.Close()

	zipBytes, err := ioutil.ReadAll(zipFile)
	if err != nil {
		return nil, err
	}

	return zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
}

func globFiles(pattern string, archive *zip.Reader) []*zip.File {
	var files []*zip.File
	for _, f := range archive.File {
		if matched, err := path.Match(pattern, f.Name); err != nil {
			// This shouldn't happen - all patterns are hard-coded, errors in them
			// are a programming error.
			panic(err)
		} else if matched {
			files = append(files, f)
		}
	}
	return files
}

func unmarshalXmlFile(file *zip.File, data interface{}) error {
	r, err := file.Open()
	if err != nil {
		return err
	}
	decoder := xml.NewDecoder(r)
	r.Close()
	return decoder.Decode(data)
}

type URNParts struct {
	URN     string
	Name    string
	Version string
}

func (u *URNParts) Const() string {
	return fmt.Sprintf("URN_%s_%s", u.Name, u.Version)
}

// extractURNParts extracts the name and version from a URN string.
func extractURNParts(urn, expectedPrefix string) (*URNParts, error) {
	if !strings.HasPrefix(urn, expectedPrefix) {
		return nil, fmt.Errorf("%q does not have expected prefix %q", urn, expectedPrefix)
	}
	parts := strings.SplitN(strings.TrimPrefix(urn, expectedPrefix), ":", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("%q does not have a name and version", urn)
	}
	name, version := parts[0], parts[1]
	return &URNParts{urn, name, version}, nil
}

var scpdFilenameRe = regexp.MustCompile(
	`.*/([a-zA-Z0-9]+)([0-9]+)\.xml`)

func urnPartsFromSCPDFilename(filename string) (*URNParts, error) {
	parts := scpdFilenameRe.FindStringSubmatch(filename)
	if len(parts) != 3 {
		return nil, fmt.Errorf("SCPD filename %q does not have expected number of parts", filename)
	}
	name, version := parts[1], parts[2]
	return &URNParts{
		URN:     serviceURNPrefix + name + ":" + version,
		Name:    name,
		Version: version,
	}, nil
}
<commit_msg>Remove GofmtWriteCloser.<commit_after>// specgen generates Go code from the UPnP specification files.
//
// The specification is available for download from:
// http://upnp.org/resources/upnpresources.zip
package main

import (
	"archive/zip"
	"bytes"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/huin/goupnp"
	"github.com/huin/goupnp/scpd"
	"github.com/huin/goutil/codegen"
)

// flags
var (
	specFilename = flag.String("spec", "", "Path to the specification file.")
	outDir       = flag.String("out-dir", "", "Path to the output directory.")
	enableGofmt  = flag.Bool("gofmt", true, "Pass the output through gofmt. "+
		"Disable if debugging code output problems.")
)

var (
	deviceURNPrefix  = "urn:schemas-upnp-org:device:"
	serviceURNPrefix = "urn:schemas-upnp-org:service:"
)

func main() {
	flag.Parse()

	if *specFilename == "" {
		log.Fatal("--spec is required")
	}
	if *outDir == "" {
		log.Fatal("--out-dir is required")
	}

	specArchive, err := openZipfile(*specFilename)
	if err != nil {
		log.Fatalf("Error opening %s: %v", *specFilename)
	}
	defer specArchive.Close()

	dcpsCol := newDcpsCollection(map[string]string{
		"Internet Gateway_1": "internetgateway1",
		"Internet Gateway_2": "internetgateway2",
	})
	for _, f := range globFiles("standardizeddcps/*/*.zip", specArchive.Reader) {
		dirName := strings.TrimPrefix(f.Name, "standardizeddcps/")
		slashIndex := strings.Index(dirName, "/")
		if slashIndex == -1 {
			// Should not happen.
			log.Printf("Could not find / in %q", dirName)
			return
		}
		dirName = dirName[:slashIndex]

		dcp := dcpsCol.dcpsForDir(dirName)
		if dcp == nil {
			log.Printf("No alias defined for directory %q: skipping", dirName)
			continue
		}

		dcp.processZipFile(f)
	}

	for _, dcp := range dcpsCol.dcpsByAlias {
		if err := dcp.writePackage(*outDir); err != nil {
			log.Printf("Error writing package %q: %v", dcp.Name, err)
		}
	}
}

type dcpsCollection struct {
	dcpsAliasByDir map[string]string
	dcpsByAlias    map[string]*DCP
}

func newDcpsCollection(dcpsAliasByDir map[string]string) *dcpsCollection {
	c := &dcpsCollection{
		dcpsAliasByDir: dcpsAliasByDir,
		dcpsByAlias:    make(map[string]*DCP),
	}
	for _, alias := range dcpsAliasByDir {
		c.dcpsByAlias[alias] = newDCP(alias)
	}
	return c
}

func (c dcpsCollection) dcpsForDir(dirName string) *DCP {
	alias, ok := c.dcpsAliasByDir[dirName]
	if !ok {
		return nil
	}
	return c.dcpsByAlias[alias]
}

// DCP collects together information about a UPnP Device Control Protocol.
type DCP struct {
	Name         string
	DeviceTypes  map[string]*URNParts
	ServiceTypes map[string]*URNParts
	Services     []SCPDWithURN
}

func newDCP(name string) *DCP {
	return &DCP{
		Name:         name,
		DeviceTypes:  make(map[string]*URNParts),
		ServiceTypes: make(map[string]*URNParts),
	}
}

func (dcp *DCP) processZipFile(file *zip.File) {
	archive, err := openChildZip(file)
	if err != nil {
		log.Println("Error reading child zip file:", err)
		return
	}
	for _, deviceFile := range globFiles("*/device/*.xml", archive) {
		dcp.processDeviceFile(deviceFile)
	}
	for _, scpdFile := range globFiles("*/service/*.xml", archive) {
		dcp.processSCPDFile(scpdFile)
	}
}

func (dcp *DCP) processDeviceFile(file *zip.File) {
	var device goupnp.Device
	if err := unmarshalXmlFile(file, &device); err != nil {
		log.Printf("Error decoding device XML from file %q: %v", file.Name, err)
		return
	}
	device.VisitDevices(func(d *goupnp.Device) {
		t := strings.TrimSpace(d.DeviceType)
		if t != "" {
			u, err := extractURNParts(t, deviceURNPrefix)
			if err != nil {
				log.Println(err)
				return
			}
			dcp.DeviceTypes[t] = u
		}
	})
	device.VisitServices(func(s *goupnp.Service) {
		u, err := extractURNParts(s.ServiceType, serviceURNPrefix)
		if err != nil {
			log.Println(err)
			return
		}
		dcp.ServiceTypes[s.ServiceType] = u
	})
}

func (dcp *DCP) writePackage(outDir string) error {
	packageDirname := filepath.Join(outDir, dcp.Name)
	err := os.MkdirAll(packageDirname, os.ModePerm)
	if err != nil && !os.IsExist(err) {
		return err
	}
	packageFilename := filepath.Join(packageDirname, dcp.Name+".go")
	packageFile, err := os.Create(packageFilename)
	if err != nil {
		return err
	}
	var output io.WriteCloser = packageFile
	if *enableGofmt {
		if output, err = codegen.NewGofmtWriteCloser(output); err != nil {
			packageFile.Close()
			return err
		}
	}
	if err = packageTmpl.Execute(output, dcp); err != nil {
		output.Close()
		return err
	}
	return output.Close()
}

func (dcp *DCP) processSCPDFile(file *zip.File) {
	scpd := new(scpd.SCPD)
	if err := unmarshalXmlFile(file, scpd); err != nil {
		log.Printf("Error decoding SCPD XML from file %q: %v", file.Name, err)
		return
	}
	scpd.Clean()
	urnParts, err := urnPartsFromSCPDFilename(file.Name)
	if err != nil {
		log.Printf("Could not recognize SCPD filename %q: %v", file.Name, err)
		return
	}
	dcp.Services = append(dcp.Services, SCPDWithURN{
		URNParts: urnParts,
		SCPD:     scpd,
	})
}

type SCPDWithURN struct {
	*URNParts
	SCPD *scpd.SCPD
}

func (s *SCPDWithURN) WrapArgument(arg scpd.Argument) (*argumentWrapper, error) {
	relVar := s.SCPD.GetStateVariable(arg.RelatedStateVariable)
	if relVar == nil {
		return nil, fmt.Errorf("no such state variable: %q, for argument %q", arg.RelatedStateVariable, arg.Name)
	}
	cnv, ok := typeConvs[relVar.DataType.Name]
	if !ok {
		return nil, fmt.Errorf("unknown data type: %q, for state variable %q, for argument %q", relVar.DataType.Type, arg.RelatedStateVariable, arg.Name)
	}
	return &argumentWrapper{
		Argument: arg,
		relVar:   relVar,
		conv:     cnv,
	}, nil
}

type argumentWrapper struct {
	scpd.Argument
	relVar *scpd.StateVariable
	conv   conv
}

func (arg *argumentWrapper) AsParameter() string {
	return fmt.Sprintf("%s %s", arg.Name, arg.conv.ExtType)
}

func (arg *argumentWrapper) Document() string {
	relVar := arg.relVar
	if rng := relVar.AllowedValueRange; rng != nil {
		var parts []string
		if rng.Minimum != "" {
			parts = append(parts, fmt.Sprintf("minimum=%s", rng.Minimum))
		}
		if rng.Maximum != "" {
			parts = append(parts, fmt.Sprintf("maximum=%s", rng.Maximum))
		}
		if rng.Step != "" {
			parts = append(parts, fmt.Sprintf("step=%s", rng.Step))
		}
		return "allowed value range: " + strings.Join(parts, ", ")
	}
	if len(relVar.AllowedValues) != 0 {
		return "allowed values: " + strings.Join(relVar.AllowedValues, ", ")
	}
	return ""
}

func (arg *argumentWrapper) Marshal() string {
	return fmt.Sprintf("soap.Marshal%s(%s)", arg.conv.FuncSuffix, arg.Name)
}

func (arg *argumentWrapper) Unmarshal(objVar string) string {
	return fmt.Sprintf("soap.Unmarshal%s(%s.%s)", arg.conv.FuncSuffix, objVar, arg.Name)
}

type conv struct {
	FuncSuffix string
	ExtType    string
}

// typeConvs maps from a SOAP type (e.g "fixed.14.4") to the function name
// suffix inside the soap module (e.g "Fixed14_4") and the Go type.
var typeConvs = map[string]conv{
	"ui1":         conv{"Ui1", "uint8"},
	"ui2":         conv{"Ui2", "uint16"},
	"ui4":         conv{"Ui4", "uint32"},
	"i1":          conv{"I1", "int8"},
	"i2":          conv{"I2", "int16"},
	"i4":          conv{"I4", "int32"},
	"int":         conv{"Int", "int64"},
	"r4":          conv{"R4", "float32"},
	"r8":          conv{"R8", "float64"},
	"number":      conv{"R8", "float64"}, // Alias for r8.
	"fixed.14.4":  conv{"Fixed14_4", "float64"},
	"float":       conv{"R8", "float64"},
	"char":        conv{"Char", "rune"},
	"string":      conv{"String", "string"},
	"date":        conv{"Date", "time.Time"},
	"dateTime":    conv{"DateTime", "time.Time"},
	"dateTime.tz": conv{"DateTimeTz", "time.Time"},
	"time":        conv{"TimeOfDay", "soap.TimeOfDay"},
	"time.tz":     conv{"TimeOfDayTz", "soap.TimeOfDay"},
	"boolean":     conv{"Boolean", "bool"},
	"bin.base64":  conv{"BinBase64", "[]byte"},
	"bin.hex":     conv{"BinHex", "[]byte"},
}

type closeableZipReader struct {
	io.Closer
	*zip.Reader
}

func openZipfile(filename string) (*closeableZipReader, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	fi, err := file.Stat()
	if err != nil {
		return nil, err
	}
	archive, err := zip.NewReader(file, fi.Size())
	if err != nil {
		return nil, err
	}
	return &closeableZipReader{
		Closer: file,
		Reader: archive,
	}, nil
}

// openChildZip opens a zip file within another zip file.
func openChildZip(file *zip.File) (*zip.Reader, error) {
	zipFile, err := file.Open()
	if err != nil {
		return nil, err
	}
	defer zipFile.Close()

	zipBytes, err := ioutil.ReadAll(zipFile)
	if err != nil {
		return nil, err
	}

	return zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
}

func globFiles(pattern string, archive *zip.Reader) []*zip.File {
	var files []*zip.File
	for _, f := range archive.File {
		if matched, err := path.Match(pattern, f.Name); err != nil {
			// This shouldn't happen - all patterns are hard-coded, errors in them
			// are a programming error.
			panic(err)
		} else if matched {
			files = append(files, f)
		}
	}
	return files
}

func unmarshalXmlFile(file *zip.File, data interface{}) error {
	r, err := file.Open()
	if err != nil {
		return err
	}
	decoder := xml.NewDecoder(r)
	r.Close()
	return decoder.Decode(data)
}

type URNParts struct {
	URN     string
	Name    string
	Version string
}

func (u *URNParts) Const() string {
	return fmt.Sprintf("URN_%s_%s", u.Name, u.Version)
}

// extractURNParts extracts the name and version from a URN string.
func extractURNParts(urn, expectedPrefix string) (*URNParts, error) {
	if !strings.HasPrefix(urn, expectedPrefix) {
		return nil, fmt.Errorf("%q does not have expected prefix %q", urn, expectedPrefix)
	}
	parts := strings.SplitN(strings.TrimPrefix(urn, expectedPrefix), ":", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("%q does not have a name and version", urn)
	}
	name, version := parts[0], parts[1]
	return &URNParts{urn, name, version}, nil
}

var scpdFilenameRe = regexp.MustCompile(
	`.*/([a-zA-Z0-9]+)([0-9]+)\.xml`)

func urnPartsFromSCPDFilename(filename string) (*URNParts, error) {
	parts := scpdFilenameRe.FindStringSubmatch(filename)
	if len(parts) != 3 {
		return nil, fmt.Errorf("SCPD filename %q does not have expected number of parts", filename)
	}
	name, version := parts[1], parts[2]
	return &URNParts{
		URN:     serviceURNPrefix + name + ":" + version,
		Name:    name,
		Version: version,
	}, nil
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"errors"
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	"github.com/terraform-providers/terraform-provider-aws/aws"
	"github.com/terraform-providers/terraform-provider-google/google"
	api "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubernetes "k8s.io/client-go/kubernetes"
)

var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider

func init() {
	testAccProvider = Provider().(*schema.Provider)
	testAccProviders = map[string]terraform.ResourceProvider{
		"kubernetes": testAccProvider,
		"google":     google.Provider(),
		"aws":        aws.Provider(),
	}
}

func TestProvider(t *testing.T) {
	if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestProvider_impl(t *testing.T) {
	var _ terraform.ResourceProvider = Provider()
}

func TestProvider_configure(t *testing.T) {
	resetEnv := unsetEnv(t)
	defer resetEnv()

	os.Setenv("KUBECONFIG", "test-fixtures/kube-config.yaml")
	os.Setenv("KUBE_CTX", "gcp")

	c, err := config.NewRawConfig(map[string]interface{}{})
	if err != nil {
		t.Fatal(err)
	}
	rc := terraform.NewResourceConfig(c)
	p := Provider()
	err = p.Configure(rc)
	if err != nil {
		t.Fatal(err)
	}
}

func unsetEnv(t *testing.T) func() {
	e := getEnv()

	if err := os.Unsetenv("KUBECONFIG"); err != nil {
		t.Fatalf("Error unsetting env var KUBECONFIG: %s", err)
	}
	if err := os.Unsetenv("KUBE_CONFIG"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CONFIG: %s", err)
	}
	if err := os.Unsetenv("KUBE_CTX"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CTX: %s", err)
	}
	if err := os.Unsetenv("KUBE_CTX_AUTH_INFO"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CTX_AUTH_INFO: %s", err)
	}
	if err := os.Unsetenv("KUBE_CTX_CLUSTER"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CTX_CLUSTER: %s", err)
	}
	if err := os.Unsetenv("KUBE_HOST"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_HOST: %s", err)
	}
	if err := os.Unsetenv("KUBE_USER"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_USER: %s", err)
	}
	if err := os.Unsetenv("KUBE_PASSWORD"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_PASSWORD: %s", err)
	}
	if err := os.Unsetenv("KUBE_CLIENT_CERT_DATA"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CLIENT_CERT_DATA: %s", err)
	}
	if err := os.Unsetenv("KUBE_CLIENT_KEY_DATA"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CLIENT_KEY_DATA: %s", err)
	}
	if err := os.Unsetenv("KUBE_CLUSTER_CA_CERT_DATA"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CLUSTER_CA_CERT_DATA: %s", err)
	}

	return func() {
		if err := os.Setenv("KUBE_CONFIG", e.Config); err != nil {
			t.Fatalf("Error resetting env var KUBE_CONFIG: %s", err)
		}
		if err := os.Setenv("KUBECONFIG", e.Config); err != nil {
			t.Fatalf("Error resetting env var KUBECONFIG: %s", err)
		}
		if err := os.Setenv("KUBE_CTX", e.Config); err != nil {
			t.Fatalf("Error resetting env var KUBE_CTX: %s", err)
		}
		if err := os.Setenv("KUBE_CTX_AUTH_INFO", e.CtxAuthInfo); err != nil {
			t.Fatalf("Error resetting env var KUBE_CTX_AUTH_INFO: %s", err)
		}
		if err := os.Setenv("KUBE_CTX_CLUSTER", e.CtxCluster); err != nil {
			t.Fatalf("Error resetting env var KUBE_CTX_CLUSTER: %s", err)
		}
		if err := os.Setenv("KUBE_HOST", e.Host); err != nil {
			t.Fatalf("Error resetting env var KUBE_HOST: %s", err)
		}
		if err := os.Setenv("KUBE_USER", e.User); err != nil {
			t.Fatalf("Error resetting env var KUBE_USER: %s", err)
		}
		if err := os.Setenv("KUBE_PASSWORD", e.Password); err != nil {
			t.Fatalf("Error resetting env var KUBE_PASSWORD: %s", err)
		}
		if err := os.Setenv("KUBE_CLIENT_CERT_DATA", e.ClientCertData); err != nil {
			t.Fatalf("Error resetting env var KUBE_CLIENT_CERT_DATA: %s", err)
		}
		if err := os.Setenv("KUBE_CLIENT_KEY_DATA", e.ClientKeyData); err != nil {
			t.Fatalf("Error resetting env var KUBE_CLIENT_KEY_DATA: %s", err)
		}
		if err := os.Setenv("KUBE_CLUSTER_CA_CERT_DATA", e.ClusterCACertData); err != nil {
			t.Fatalf("Error resetting env var KUBE_CLUSTER_CA_CERT_DATA: %s", err)
		}
	}
}

func getEnv() *currentEnv {
	e := &currentEnv{
		Ctx:               os.Getenv("KUBE_CTX"),
		CtxAuthInfo:       os.Getenv("KUBE_CTX_AUTH_INFO"),
		CtxCluster:        os.Getenv("KUBE_CTX_CLUSTER"),
		Host:              os.Getenv("KUBE_HOST"),
		User:              os.Getenv("KUBE_USER"),
		Password:          os.Getenv("KUBE_PASSWORD"),
		ClientCertData:    os.Getenv("KUBE_CLIENT_CERT_DATA"),
		ClientKeyData:     os.Getenv("KUBE_CLIENT_KEY_DATA"),
		ClusterCACertData: os.Getenv("KUBE_CLUSTER_CA_CERT_DATA"),
	}
	if cfg := os.Getenv("KUBE_CONFIG"); cfg != "" {
		e.Config = cfg
	}
	if cfg := os.Getenv("KUBECONFIG"); cfg != "" {
		e.Config = cfg
	}
	return e
}

func testAccPreCheck(t *testing.T) {
	hasFileCfg := (os.Getenv("KUBE_CTX_AUTH_INFO") != "" && os.Getenv("KUBE_CTX_CLUSTER") != "") ||
		os.Getenv("KUBE_CTX") != "" ||
		os.Getenv("KUBECONFIG") != "" ||
		os.Getenv("KUBE_CONFIG") != ""
	hasStaticCfg := (os.Getenv("KUBE_HOST") != "" &&
		os.Getenv("KUBE_USER") != "" &&
		os.Getenv("KUBE_PASSWORD") != "" &&
		os.Getenv("KUBE_CLIENT_CERT_DATA") != "" &&
		os.Getenv("KUBE_CLIENT_KEY_DATA") != "" &&
		os.Getenv("KUBE_CLUSTER_CA_CERT_DATA") != "")

	if !hasFileCfg && !hasStaticCfg {
		t.Fatalf("File config (KUBE_CTX_AUTH_INFO and KUBE_CTX_CLUSTER) or static configuration"+
			" (%s) must be set for acceptance tests",
			strings.Join([]string{
				"KUBE_HOST",
				"KUBE_USER",
				"KUBE_PASSWORD",
				"KUBE_CLIENT_CERT_DATA",
				"KUBE_CLIENT_KEY_DATA",
				"KUBE_CLUSTER_CA_CERT_DATA",
			}, ", "))
	}

	err := testAccProvider.Configure(terraform.NewResourceConfig(nil))
	if err != nil {
		t.Fatal(err)
	}
}

func skipIfNoGoogleCloudSettingsFound(t *testing.T) {
	if os.Getenv("GOOGLE_PROJECT") == "" || os.Getenv("GOOGLE_REGION") == "" || os.Getenv("GOOGLE_ZONE") == "" {
		t.Skip("The environment variables GOOGLE_PROJECT, GOOGLE_REGION and GOOGLE_ZONE" +
			" must be set to run Google Cloud tests - skipping")
	}
}

func skipIfNoAwsSettingsFound(t *testing.T) {
	if os.Getenv("AWS_DEFAULT_REGION") == "" || os.Getenv("AWS_ZONE") == "" || os.Getenv("AWS_ACCESS_KEY_ID") == "" || os.Getenv("AWS_SECRET_ACCESS_KEY") == "" {
		t.Skip("The environment variables AWS_DEFAULT_REGION, AWS_ZONE, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY" +
			" must be set to run AWS tests - skipping")
	}
}

func skipIfNoLoadBalancersAvailable(t *testing.T) {
	// TODO: Support AWS ELBs
	isInGke, err := isRunningInGke()
	if err != nil {
		t.Fatal(err)
	}
	if !isInGke {
		t.Skip("The Kubernetes endpoint must come from an environment which supports " +
			"load balancer provisioning for this test to run - skipping")
	}
}

func skipIfNotRunningInGke(t *testing.T) {
	isInGke, err := isRunningInGke()
	if err != nil {
		t.Fatal(err)
	}
	if !isInGke {
		t.Skip("The Kubernetes endpoint must come from GKE for this test to run - skipping")
	}
}

func isRunningInMinikube() (bool, error) {
	node, err := getFirstNode()
	if err != nil {
		return false, err
	}

	labels := node.GetLabels()
	if v, ok := labels["kubernetes.io/hostname"]; ok && v == "minikube" {
		return true, nil
	}
	return false, nil
}

func isRunningInGke() (bool, error) {
	node, err := getFirstNode()
	if err != nil {
		return false, err
	}

	labels := node.GetLabels()
	if _, ok := labels["cloud.google.com/gke-nodepool"]; ok {
		return true, nil
	}
	return false, nil
}

func getFirstNode() (api.Node, error) {
	meta := testAccProvider.Meta()
	if meta == nil {
		return api.Node{}, errors.New("Provider not initialized, unable to get cluster node")
	}
	conn := meta.(*kubernetes.Clientset)
	resp, err := conn.CoreV1().Nodes().List(metav1.ListOptions{})
	if err != nil {
		return api.Node{}, err
	}

	if len(resp.Items) < 1 {
		return api.Node{}, errors.New("Expected at least 1 node, none found")
	}

	return resp.Items[0], nil
}

type currentEnv struct {
	Config            string
	Ctx               string
	CtxAuthInfo       string
	CtxCluster        string
	Host              string
	User              string
	Password          string
	ClientCertData    string
	ClientKeyData     string
	ClusterCACertData string
}
<commit_msg>Skip TestProvider_configure test when TF_ACC is set (#314)<commit_after>package kubernetes

import (
	"errors"
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	"github.com/terraform-providers/terraform-provider-aws/aws"
	"github.com/terraform-providers/terraform-provider-google/google"
	api "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubernetes "k8s.io/client-go/kubernetes"
)

var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider

func init() {
	testAccProvider = Provider().(*schema.Provider)
	testAccProviders = map[string]terraform.ResourceProvider{
		"kubernetes": testAccProvider,
		"google":     google.Provider(),
		"aws":        aws.Provider(),
	}
}

func TestProvider(t *testing.T) {
	if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestProvider_impl(t *testing.T) {
	var _ terraform.ResourceProvider = Provider()
}

func TestProvider_configure(t *testing.T) {
	if os.Getenv("TF_ACC") != "" {
		t.Skip("The environment variable TF_ACC is set, and this test prevents acceptance tests" +
			" from running as it alters environment variables - skipping")
	}

	resetEnv := unsetEnv(t)
	defer resetEnv()

	os.Setenv("KUBECONFIG", "test-fixtures/kube-config.yaml")
	os.Setenv("KUBE_CTX", "gcp")

	c, err := config.NewRawConfig(map[string]interface{}{})
	if err != nil {
		t.Fatal(err)
	}
	rc := terraform.NewResourceConfig(c)
	p := Provider()
	err = p.Configure(rc)
	if err != nil {
		t.Fatal(err)
	}
}

func unsetEnv(t *testing.T) func() {
	e := getEnv()

	if err := os.Unsetenv("KUBECONFIG"); err != nil {
		t.Fatalf("Error unsetting env var KUBECONFIG: %s", err)
	}
	if err := os.Unsetenv("KUBE_CONFIG"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CONFIG: %s", err)
	}
	if err := os.Unsetenv("KUBE_CTX"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CTX: %s", err)
	}
	if err := os.Unsetenv("KUBE_CTX_AUTH_INFO"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CTX_AUTH_INFO: %s", err)
	}
	if err := os.Unsetenv("KUBE_CTX_CLUSTER"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CTX_CLUSTER: %s", err)
	}
	if err := os.Unsetenv("KUBE_HOST"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_HOST: %s", err)
	}
	if err := os.Unsetenv("KUBE_USER"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_USER: %s", err)
	}
	if err := os.Unsetenv("KUBE_PASSWORD"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_PASSWORD: %s", err)
	}
	if err := os.Unsetenv("KUBE_CLIENT_CERT_DATA"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CLIENT_CERT_DATA: %s", err)
	}
	if err := os.Unsetenv("KUBE_CLIENT_KEY_DATA"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CLIENT_KEY_DATA: %s", err)
	}
	if err := os.Unsetenv("KUBE_CLUSTER_CA_CERT_DATA"); err != nil {
		t.Fatalf("Error unsetting env var KUBE_CLUSTER_CA_CERT_DATA: %s", err)
	}

	return func() {
		if err := os.Setenv("KUBE_CONFIG", e.Config); err != nil {
			t.Fatalf("Error resetting env var KUBE_CONFIG: %s", err)
		}
		if err := os.Setenv("KUBECONFIG", e.Config); err != nil {
			t.Fatalf("Error resetting env var KUBECONFIG: %s", err)
		}
		if err := os.Setenv("KUBE_CTX", e.Config); err != nil {
			t.Fatalf("Error resetting env var KUBE_CTX: %s", err)
		}
		if err := os.Setenv("KUBE_CTX_AUTH_INFO", e.CtxAuthInfo); err != nil {
			t.Fatalf("Error resetting env var KUBE_CTX_AUTH_INFO: %s", err)
		}
		if err := os.Setenv("KUBE_CTX_CLUSTER", e.CtxCluster); err != nil {
			t.Fatalf("Error resetting env var KUBE_CTX_CLUSTER: %s", err)
		}
		if err := os.Setenv("KUBE_HOST", e.Host); err != nil {
			t.Fatalf("Error resetting env var KUBE_HOST: %s", err)
		}
		if err := os.Setenv("KUBE_USER", e.User); err != nil {
			t.Fatalf("Error resetting env var KUBE_USER: %s", err)
		}
		if err := os.Setenv("KUBE_PASSWORD", e.Password); err != nil {
			t.Fatalf("Error resetting env var KUBE_PASSWORD: %s", err)
		}
		if err := os.Setenv("KUBE_CLIENT_CERT_DATA", e.ClientCertData); err != nil {
			t.Fatalf("Error resetting env var KUBE_CLIENT_CERT_DATA: %s", err)
		}
		if err := os.Setenv("KUBE_CLIENT_KEY_DATA", e.ClientKeyData); err != nil {
			t.Fatalf("Error resetting env var KUBE_CLIENT_KEY_DATA: %s", err)
		}
		if err := os.Setenv("KUBE_CLUSTER_CA_CERT_DATA", e.ClusterCACertData); err != nil {
			t.Fatalf("Error resetting env var KUBE_CLUSTER_CA_CERT_DATA: %s", err)
		}
	}
}

func getEnv() *currentEnv {
	e := &currentEnv{
		Ctx:               os.Getenv("KUBE_CTX"),
		CtxAuthInfo:       os.Getenv("KUBE_CTX_AUTH_INFO"),
		CtxCluster:        os.Getenv("KUBE_CTX_CLUSTER"),
		Host:              os.Getenv("KUBE_HOST"),
		User:              os.Getenv("KUBE_USER"),
		Password:          os.Getenv("KUBE_PASSWORD"),
		ClientCertData:    os.Getenv("KUBE_CLIENT_CERT_DATA"),
		ClientKeyData:     os.Getenv("KUBE_CLIENT_KEY_DATA"),
		ClusterCACertData: os.Getenv("KUBE_CLUSTER_CA_CERT_DATA"),
	}
	if cfg := os.Getenv("KUBE_CONFIG"); cfg != "" {
		e.Config = cfg
	}
	if cfg := os.Getenv("KUBECONFIG"); cfg != "" {
		e.Config = cfg
	}
	return e
}

func testAccPreCheck(t *testing.T) {
	hasFileCfg := (os.Getenv("KUBE_CTX_AUTH_INFO") != "" && os.Getenv("KUBE_CTX_CLUSTER") != "") ||
		os.Getenv("KUBE_CTX") != "" ||
		os.Getenv("KUBECONFIG") != "" ||
		os.Getenv("KUBE_CONFIG") != ""
	hasStaticCfg := (os.Getenv("KUBE_HOST") != "" &&
		os.Getenv("KUBE_USER") != "" &&
		os.Getenv("KUBE_PASSWORD") != "" &&
		os.Getenv("KUBE_CLIENT_CERT_DATA") != "" &&
		os.Getenv("KUBE_CLIENT_KEY_DATA") != "" &&
		os.Getenv("KUBE_CLUSTER_CA_CERT_DATA") != "")

	if !hasFileCfg && !hasStaticCfg {
		t.Fatalf("File config (KUBE_CTX_AUTH_INFO and KUBE_CTX_CLUSTER) or static configuration"+
			" (%s) must be set for acceptance tests",
			strings.Join([]string{
				"KUBE_HOST",
				"KUBE_USER",
				"KUBE_PASSWORD",
				"KUBE_CLIENT_CERT_DATA",
				"KUBE_CLIENT_KEY_DATA",
				"KUBE_CLUSTER_CA_CERT_DATA",
			}, ", "))
	}

	err := testAccProvider.Configure(terraform.NewResourceConfig(nil))
	if err != nil {
		t.Fatal(err)
	}
}

func skipIfNoGoogleCloudSettingsFound(t *testing.T) {
	if os.Getenv("GOOGLE_PROJECT") == "" || os.Getenv("GOOGLE_REGION") == "" || os.Getenv("GOOGLE_ZONE") == "" {
		t.Skip("The environment variables GOOGLE_PROJECT, GOOGLE_REGION and GOOGLE_ZONE" +
			" must be set to run Google Cloud tests - skipping")
	}
}

func skipIfNoAwsSettingsFound(t *testing.T) {
	if os.Getenv("AWS_DEFAULT_REGION") == "" || os.Getenv("AWS_ZONE") == "" || os.Getenv("AWS_ACCESS_KEY_ID") == "" || os.Getenv("AWS_SECRET_ACCESS_KEY") == "" {
		t.Skip("The environment variables AWS_DEFAULT_REGION, AWS_ZONE, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY" +
			" must be set to run AWS tests - skipping")
	}
}

func skipIfNoLoadBalancersAvailable(t *testing.T) {
	// TODO: Support AWS ELBs
	isInGke, err := isRunningInGke()
	if err != nil {
		t.Fatal(err)
	}
	if !isInGke {
		t.Skip("The Kubernetes endpoint must come from an environment which supports " +
			"load balancer provisioning for this test to run - skipping")
	}
}

func skipIfNotRunningInGke(t *testing.T) {
	isInGke, err := isRunningInGke()
	if err != nil {
		t.Fatal(err)
	}
	if !isInGke {
		t.Skip("The Kubernetes endpoint must come from GKE for this test to run - skipping")
	}
}

func isRunningInMinikube() (bool, error) {
	node, err := getFirstNode()
	if err != nil {
		return false, err
	}

	labels := node.GetLabels()
	if v, ok := labels["kubernetes.io/hostname"]; ok && v == "minikube" {
		return true, nil
	}
	return false, nil
}

func isRunningInGke() (bool, error) {
	node, err := getFirstNode()
	if err != nil {
		return false, err
	}

	labels := node.GetLabels()
	if _, ok := labels["cloud.google.com/gke-nodepool"]; ok {
		return true, nil
	}
	return false, nil
}

func getFirstNode() (api.Node, error) {
	meta := testAccProvider.Meta()
	if meta == nil {
		return api.Node{}, errors.New("Provider not initialized, unable to get cluster node")
	}
	conn := meta.(*kubernetes.Clientset)
	resp, err := conn.CoreV1().Nodes().List(metav1.ListOptions{})
	if err != nil {
		return api.Node{}, err
	}

	if len(resp.Items) < 1 {
		return api.Node{}, errors.New("Expected at least 1 node, none found")
	}

	return resp.Items[0], nil
}

type currentEnv struct {
	Config            string
	Ctx               string
	CtxAuthInfo       string
	CtxCluster        string
	Host              string
	User              string
	Password          string
	ClientCertData    string
	ClientKeyData     string
	ClusterCACertData string
}
<|endoftext|>


<commit_before>// Copyright (C) 2014-2015 Jakob Borg and Contributors (see the CONTRIBUTORS file).

package main

import (
	"crypto/tls"
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"strconv"
	"time"

	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/thejerf/suture"
)

const (
	minNegCache  = 60        // seconds
	maxNegCache  = 3600      // seconds
	maxDeviceAge = 7 * 86400 // one week, in seconds
)

var (
	Version    string
	BuildStamp string
	BuildUser  string
	BuildHost  string

	BuildDate   time.Time
	LongVersion string
)

func init() {
	stamp, _ := strconv.Atoi(BuildStamp)
	BuildDate = time.Unix(int64(stamp), 0)

	date := BuildDate.UTC().Format("2006-01-02 15:04:05 MST")
	LongVersion = fmt.Sprintf(`stdiscosrv %s (%s %s-%s) %s@%s %s`, Version, runtime.Version(), runtime.GOOS, runtime.GOARCH, BuildUser, BuildHost, date)
}

var (
	lruSize     = 10240
	limitAvg    = 5
	limitBurst  = 20
	globalStats stats
	statsFile   string
	backend     = "ql"
	dsn         = getEnvDefault("STDISCOSRV_DB_DSN", "memory://stdiscosrv")
	certFile    = "cert.pem"
	keyFile     = "key.pem"
	debug       = false
	useHTTP     = false
)

func main() {
	const (
		cleanIntv = 1 * time.Hour
		statsIntv = 5 * time.Minute
	)

	var listen string

	log.SetOutput(os.Stdout)
	log.SetFlags(0)

	flag.StringVar(&listen, "listen", ":8443", "Listen address")
	flag.IntVar(&lruSize, "limit-cache", lruSize, "Limiter cache entries")
	flag.IntVar(&limitAvg, "limit-avg", limitAvg, "Allowed average package rate, per 10 s")
	flag.IntVar(&limitBurst, "limit-burst", limitBurst, "Allowed burst size, packets")
	flag.StringVar(&statsFile, "stats-file", statsFile, "File to write periodic operation stats to")
	flag.StringVar(&backend, "db-backend", backend, "Database backend to use")
	flag.StringVar(&dsn, "db-dsn", dsn, "Database DSN")
	flag.StringVar(&certFile, "cert", certFile, "Certificate file")
	flag.StringVar(&keyFile, "key", keyFile, "Key file")
	flag.BoolVar(&debug, "debug", debug, "Debug")
	flag.BoolVar(&useHTTP, "http", useHTTP, "Listen on HTTP (behind an HTTPS proxy)")
	flag.Parse()

	log.Println(LongVersion)

	var cert tls.Certificate
	var err error
	if !useHTTP {
		cert, err = tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			log.Fatalln("Failed to load X509 key pair:", err)
		}

		devID := protocol.NewDeviceID(cert.Certificate[0])
		log.Println("Server device ID is", devID)
	}

	db, err := sql.Open(backend, dsn)
	if err != nil {
		log.Fatalln("sql.Open:", err)
	}
	prep, err := setup(backend, db)
	if err != nil {
		log.Fatalln("Setup:", err)
	}

	main := suture.NewSimple("main")

	main.Add(&querysrv{
		addr: listen,
		cert: cert,
		db:   db,
		prep: prep,
	})

	main.Add(&cleansrv{
		intv: cleanIntv,
		db:   db,
		prep: prep,
	})

	main.Add(&statssrv{
		intv: statsIntv,
		file: statsFile,
		db:   db,
	})

	globalStats.Reset()
	main.Serve()
}

func getEnvDefault(key, def string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return def
}

func next(intv time.Duration) time.Duration {
	t0 := time.Now()
	t1 := t0.Add(intv).Truncate(intv)
	return t1.Sub(t0)
}
<commit_msg>cmd/stdiscosrv: Generate keys if missing on startup (fixes #3511)<commit_after>// Copyright (C) 2014-2015 Jakob Borg and Contributors (see the CONTRIBUTORS file).

package main

import (
	"crypto/tls"
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"strconv"
	"time"

	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/tlsutil"
	"github.com/thejerf/suture"
)

const (
	minNegCache  = 60        // seconds
	maxNegCache  = 3600      // seconds
	maxDeviceAge = 7 * 86400 // one week, in seconds
)

var (
	Version    string
	BuildStamp string
	BuildUser  string
	BuildHost  string

	BuildDate   time.Time
	LongVersion string
)

func init() {
	stamp, _ := strconv.Atoi(BuildStamp)
	BuildDate = time.Unix(int64(stamp), 0)

	date := BuildDate.UTC().Format("2006-01-02 15:04:05 MST")
	LongVersion = fmt.Sprintf(`stdiscosrv %s (%s %s-%s) %s@%s %s`, Version, runtime.Version(), runtime.GOOS, runtime.GOARCH, BuildUser, BuildHost, date)
}

var (
	lruSize     = 10240
	limitAvg    = 5
	limitBurst  = 20
	globalStats stats
	statsFile   string
	backend     = "ql"
	dsn         = getEnvDefault("STDISCOSRV_DB_DSN", "memory://stdiscosrv")
	certFile    = "cert.pem"
	keyFile     = "key.pem"
	debug       = false
	useHTTP     = false
)

func main() {
	const (
		cleanIntv = 1 * time.Hour
		statsIntv = 5 * time.Minute
	)

	var listen string

	log.SetOutput(os.Stdout)
	log.SetFlags(0)

	flag.StringVar(&listen, "listen", ":8443", "Listen address")
	flag.IntVar(&lruSize, "limit-cache", lruSize, "Limiter cache entries")
	flag.IntVar(&limitAvg, "limit-avg", limitAvg, "Allowed average package rate, per 10 s")
	flag.IntVar(&limitBurst, "limit-burst", limitBurst, "Allowed burst size, packets")
	flag.StringVar(&statsFile, "stats-file", statsFile, "File to write periodic operation stats to")
	flag.StringVar(&backend, "db-backend", backend, "Database backend to use")
	flag.StringVar(&dsn, "db-dsn", dsn, "Database DSN")
	flag.StringVar(&certFile, "cert", certFile, "Certificate file")
	flag.StringVar(&keyFile, "key", keyFile, "Key file")
	flag.BoolVar(&debug, "debug", debug, "Debug")
	flag.BoolVar(&useHTTP, "http", useHTTP, "Listen on HTTP (behind an HTTPS proxy)")
	flag.Parse()

	log.Println(LongVersion)

	var cert tls.Certificate
	var err error
	if !useHTTP {
		cert, err = tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			log.Println("Failed to load keypair. Generating one, this might take a while...")
			cert, err = tlsutil.NewCertificate(certFile, keyFile, "stdiscosrv", 3072)
			if err != nil {
				log.Fatalln("Failed to generate X509 key pair:", err)
			}
		}

		devID := protocol.NewDeviceID(cert.Certificate[0])
		log.Println("Server device ID is", devID)
	}

	db, err := sql.Open(backend, dsn)
	if err != nil {
		log.Fatalln("sql.Open:", err)
	}
	prep, err := setup(backend, db)
	if err != nil {
		log.Fatalln("Setup:", err)
	}

	main := suture.NewSimple("main")

	main.Add(&querysrv{
		addr: listen,
		cert: cert,
		db:   db,
		prep: prep,
	})

	main.Add(&cleansrv{
		intv: cleanIntv,
		db:   db,
		prep: prep,
	})

	main.Add(&statssrv{
		intv: statsIntv,
		file: statsFile,
		db:   db,
	})

	globalStats.Reset()
	main.Serve()
}

func getEnvDefault(key, def string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return def
}

func next(intv time.Duration) time.Duration {
	t0 := time.Now()
	t1 := t0.Add(intv).Truncate(intv)
	return t1.Sub(t0)
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Audrius Butkevicius and Contributors.

package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/url"
	"time"
)

func poolHandler(pool string, uri *url.URL, mapping mapping) {
	if debug {
		log.Println("Joining", pool)
	}
	for {
		uriCopy := *uri
		uriCopy.Host = mapping.Address().String()

		var b bytes.Buffer
		json.NewEncoder(&b).Encode(struct {
			URL string `json:"url"`
		}{
			uriCopy.String(),
		})

		resp, err := httpClient.Post(pool, "application/json", &b)
		if err != nil {
			log.Println("Error joining pool", pool, err)
		} else if resp.StatusCode == 500 {
			bs, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Println("Failed to join", pool, "due to an internal server error. Could not read response:", err)
			} else {
				log.Println("Failed to join", pool, "due to an internal server error:", string(bs))
			}
			resp.Body.Close()
		} else if resp.StatusCode == 429 {
			log.Println(pool, "under load, will retry in a minute")
			time.Sleep(time.Minute)
			continue
		} else if resp.StatusCode == 401 {
			log.Println(pool, "failed to join due to IP address not matching external address. Aborting")
			return
		} else if resp.StatusCode == 200 {
			var x struct {
				EvictionIn time.Duration `json:"evictionIn"`
			}
			err := json.NewDecoder(resp.Body).Decode(&x)
			if err == nil {
				rejoin := x.EvictionIn - (x.EvictionIn / 5)
				log.Println("Joined", pool, "rejoining in", rejoin)
				time.Sleep(rejoin)
				continue
			} else {
				log.Println("Failed to deserialize response", err)
			}
		} else {
			log.Println(pool, "unknown response type from server", resp.StatusCode)
		}
		time.Sleep(time.Hour)
	}
}
<commit_msg>cmd/strelaysrv: Harmonize and improve log output (ref #6492)<commit_after>// Copyright (C) 2015 Audrius Butkevicius and Contributors.

package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

const (
	httpStatusEnhanceYourCalm = 429
)

func poolHandler(pool string, uri *url.URL, mapping mapping) {
	if debug {
		log.Println("Joining", pool)
	}
	for {
		uriCopy := *uri
		uriCopy.Host = mapping.Address().String()

		var b bytes.Buffer
		json.NewEncoder(&b).Encode(struct {
			URL string `json:"url"`
		}{
			uriCopy.String(),
		})

		resp, err := httpClient.Post(pool, "application/json", &b)
		if err != nil {
			log.Printf("Error joining pool %v: HTTP request: %v", pool, err)
			time.Sleep(time.Minute)
			continue
		}

		bs, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			log.Printf("Error joining pool %v: reading response: %v", pool, err)
			time.Sleep(time.Minute)
			continue
		}

		switch resp.StatusCode {
		case http.StatusOK:
			var x struct {
				EvictionIn time.Duration `json:"evictionIn"`
			}
			if err := json.NewDecoder(resp.Body).Decode(&x); err == nil {
				rejoin := x.EvictionIn - (x.EvictionIn / 5)
				log.Printf("Joined pool %s, rejoining in %v", pool, rejoin)
				time.Sleep(rejoin)
				continue
			} else {
				log.Printf("Joined pool %s, failed to deserialize response: %v", pool, err)
			}

		case http.StatusInternalServerError:
			log.Printf("Failed to join %v: server error", pool)
			log.Printf("Response data: %s", bs)
			time.Sleep(time.Minute)
			continue

		case http.StatusBadRequest:
			log.Printf("Failed to join %v: request or check error", pool)
			log.Printf("Response data: %s", bs)
			time.Sleep(time.Minute)
			continue

		case httpStatusEnhanceYourCalm:
			log.Printf("Failed to join %v: under load (rate limiting)", pool)
			time.Sleep(time.Minute)
			continue

		case http.StatusUnauthorized:
			log.Printf("Failed to join %v: IP address not matching external address", pool)
			log.Println("Aborting")
			return

		default:
			log.Printf("Failed to join %v: unexpected status code from server: %d", pool, resp.StatusCode)
			log.Printf("Response data: %s", bs)
			time.Sleep(time.Minute)
			continue
		}

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


<commit_before>package labeler

import (
	"log"
	"regexp"

	"github.com/google/go-github/github"
	"github.com/parkr/auto-reply/ctx"
)

var fixesIssueMatcher = regexp.MustCompile(`Fixes #(\d+)`)

var IssueHasPullRequestLabeler = func(context *ctx.Context, payload interface{}) error {
	event, ok := payload.(*github.PullRequestEvent)
	if !ok {
		return context.NewError("IssueHasPullRequestLabeler: not a pull request event")
	}

	if *event.Action != "opened" {
		return nil
	}

        description := *event.PullRequest.Body
	issueNum := issueFixed(description)

	var err error
	if issueNum != "" {
		log.Printf("detected a pull request that fixes issue %v", issueNum)
	}

	return err
}

func issueFixed(description string) string {
	issueSubmatches := fixesIssueMatcher.FindAllStringSubmatch(description, -1)
	if len(issueSubmatches) == 0 || len(issueSubmatches[0]) < 2 {
		return ""
	}

	return issueSubmatches[0][1]
}
<commit_msg>add label to issue fixed by pr<commit_after>package labeler

import (
	"log"
	"regexp"
	"strconv"

	"github.com/google/go-github/github"
	"github.com/parkr/auto-reply/ctx"
)

var fixesIssueMatcher = regexp.MustCompile(`Fixes #(\d+)`)

var IssueHasPullRequestLabeler = func(context *ctx.Context, payload interface{}) error {
	event, ok := payload.(*github.PullRequestEvent)
	if !ok {
		return context.NewError("IssueHasPullRequestLabeler: not a pull request event")
	}

	if *event.Action != "opened" {
		return nil
	}

	owner, repo, description := *event.Repo.Owner.Login, *event.Repo.Name, *event.PullRequest.Body

	issueNum := issueFixed(description)

	var err error
	if issueNum != -1 {
		err := AddLabels(context.GitHub, owner, repo, issueNum, []string{"has-pull-request"})
		if err != nil {
			log.Printf("error adding the has-pull-request label: %v", err)
		}
	}

	return err
}

func issueFixed(description string) int {
	issueSubmatches := fixesIssueMatcher.FindAllStringSubmatch(description, -1)
	if len(issueSubmatches) == 0 || len(issueSubmatches[0]) < 2 {
		return -1
	}

	issueNum, _ := strconv.Atoi(issueSubmatches[0][1])
	return issueNum
}
<|endoftext|>


<commit_before>package java

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/ezbuy/tgen/global"
	"github.com/samuel/go-thrift/parser"
)

func TestGenerate(t *testing.T) {
	return
	// 1 read thrift files from folder 'cases'
	// 2 generate & output
	// 3 read generated files, compared with corresponding files in folder 'test'

	casedir, _ := filepath.Abs("./../../example/java")

	// create output dir
	outdir, _ := filepath.Abs("./output")
	// if err := os.MkdirAll(outdir, 0755); err != nil {
	// 	t.Errorf("failed to create output directory %s", outdir)
	// }

	testdir, _ := filepath.Abs("./../../example/java/ref")

	gen := &JavaGen{}
	p := &parser.Parser{}

	visitfunc := func(path string, info os.FileInfo, err error) error {
		if strings.HasPrefix(filepath.Base(path), ".") || filepath.Ext(path) != ".thrift" {
			return nil
		}

		global.InputFile = path

		parsedThrift, _, err := p.ParseFile(path)
		if err != nil {
			t.Errorf("parse error: %s\n", err.Error())
		}

		gen.Generate(outdir, parsedThrift)

		for f, thrift := range parsedThrift {
			if f != global.InputFile {
				continue
			}

			ns := thrift.Namespaces["java"]
			p := strings.Replace(ns, ".", "/", -1)

			for _, m := range thrift.Structs {
				name := m.Name + ".java"

				// jsonrpc

				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}

			for _, s := range thrift.Services {
				name := s.Name + "Service.java"

				// jsonrpc
				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}
		}

		return nil
	}

	if err := filepath.Walk(casedir, visitfunc); err != nil {
		t.Errorf("walk error: %s\n", err.Error())
	}

	// do some clean
	os.RemoveAll(outdir)
}

func fileCompare(t *testing.T, src string, dest string) {
	if !pathexists(src) {
		t.Error("generate error\n")
	} else if !pathexists(dest) {
		t.Errorf("no test file found [%s]\n", dest)
	} else {
		// compare the output file with the case
		srcdata, srcerr := ioutil.ReadFile(src)
		destdata, desterr := ioutil.ReadFile(dest)

		if srcerr != nil || desterr != nil {
			t.Error("compare error [reading]")
		} else if string(srcdata) != string(destdata) {
			t.Errorf("mismatch: [%s, %s]", src, dest)
		} else {
			t.Log("PASS")
		}
	}
}

func pathexists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}

	if os.IsNotExist(err) {
		return false
	}

	return false
}
<commit_msg>modify go test<commit_after>package java

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/ezbuy/tgen/global"
	"github.com/samuel/go-thrift/parser"
)

func TestGenerate(t *testing.T) {
	return
	// 1 read thrift files from folder 'cases'
	// 2 generate & output
	// 3 read generated files, compared with corresponding files in folder 'test'

	casedir, _ := filepath.Abs("./../../example/java")

	// create output dir
	outdir, _ := filepath.Abs("./output")
	// if err := os.MkdirAll(outdir, 0755); err != nil {
	// 	t.Errorf("failed to create output directory %s", outdir)
	// }

	testdir, _ := filepath.Abs("./../../example/java/ref")

	gen := &JavaGen{}
	p := &parser.Parser{}

	visitfunc := func(path string, info os.FileInfo, err error) error {
		if strings.HasPrefix(filepath.Base(path), ".") || filepath.Ext(path) != ".thrift" {
			return nil
		}

		if !strings.HasSuffix(path, "ShipForMe.thrift") {
			return nil
		}

		global.InputFile = path

		parsedThrift, _, err := p.ParseFile(path)
		if err != nil {
			t.Errorf("parse error: %s\n", err.Error())
		}

		gen.Generate(outdir, parsedThrift)

		for f, thrift := range parsedThrift {
			if f != global.InputFile {
				continue
			}

			ns := thrift.Namespaces["java"]
			p := strings.Replace(ns, ".", "/", -1)

			for _, m := range thrift.Structs {
				name := m.Name + ".java"

				// jsonrpc

				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}

			for _, s := range thrift.Services {
				name := s.Name + "Service.java"

				// jsonrpc
				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}
		}

		return nil
	}

	if err := filepath.Walk(casedir, visitfunc); err != nil {
		t.Errorf("walk error: %s\n", err.Error())
	}

	// do some clean
	os.RemoveAll(outdir)
}

func fileCompare(t *testing.T, src string, dest string) {
	if !pathexists(src) {
		t.Error("generate error\n")
	} else if !pathexists(dest) {
		t.Errorf("no test file found [%s]\n", dest)
	} else {
		// compare the output file with the case
		srcdata, srcerr := ioutil.ReadFile(src)
		destdata, desterr := ioutil.ReadFile(dest)

		if srcerr != nil || desterr != nil {
			t.Error("compare error [reading]")
		} else if string(srcdata) != string(destdata) {
			t.Errorf("mismatch: [%s, %s]", src, dest)
		} else {
			t.Log("PASS")
		}
	}
}

func pathexists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}

	if os.IsNotExist(err) {
		return false
	}

	return false
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Gravitational, Inc.

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 mocku2f

/* Mock U2F device for testing.
 * This is not a complete implementation of U2F keys.
 * In particular, the key always returns a dummy key handle and doesn't differentiate between key handles
 */

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"math/big"
	"strings"
	"time"

	"github.com/tstranex/u2f"
	"github.com/gravitational/trace"
)

type Key struct {
	privatekey *ecdsa.PrivateKey
	cert []byte
	counter uint32
}

const keyHandle = "asdf"

func decodeBase64(s string) ([]byte, error) {
	for i := 0; i < len(s)%4; i++ {
		s += "="
	}
	return base64.URLEncoding.DecodeString(s)
}

func encodeBase64(buf []byte) string {
	s := base64.URLEncoding.EncodeToString(buf)
	return strings.TrimRight(s, "=")
}

func selfSignPublicKey(keyToSign *ecdsa.PublicKey) (cert []byte, err error) {
	caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return []byte{}, err
	}
	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			Organization: []string{"Test CA"},
		},
		NotBefore: time.Now(),
		NotAfter: time.Now().Add(time.Hour),
		KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}
	cert, err = x509.CreateCertificate(rand.Reader, &template, &template, keyToSign, caPrivateKey)
	if err != nil {
		return []byte{}, err
	}
	return cert, nil
}

func Create() (*Key, error) {
	privatekey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}

	cert, err := selfSignPublicKey(&privatekey.PublicKey)
	if err != nil {
		return nil, err
	}

	return &Key{
		privatekey: privatekey,
		cert: cert,
		counter: 1,
	}, nil
}

func (muk *Key) RegisterResponse(req *u2f.RegisterRequest) (*u2f.RegisterResponse, error) {
	appIDHash := sha256.Sum256([]byte(req.AppID))

	clientData := u2f.ClientData{
		Typ: "navigator.id.finishEnrollment",
		Challenge: req.Challenge,
		Origin: req.AppID,
	}
	clientDataJson, err := json.Marshal(clientData)
	if err != nil {
		return nil, err
	}
	clientDataHash := sha256.Sum256(clientDataJson)

	marshalledPublickey := elliptic.Marshal(elliptic.P256(), muk.privatekey.PublicKey.X, muk.privatekey.PublicKey.Y)

	var dataToSign []byte
	dataToSign = append(dataToSign[:], []byte{ 0 }[:]...)
	dataToSign = append(dataToSign[:], appIDHash[:]...)
	dataToSign = append(dataToSign[:], clientDataHash[:]...)
	dataToSign = append(dataToSign[:], keyHandle[:]...)
	dataToSign = append(dataToSign[:], marshalledPublickey[:]...)

	dataHash := sha256.Sum256(dataToSign)

	// Despite taking a hash function, this actually does not hash the input.
	sig, err := muk.privatekey.Sign(rand.Reader, dataHash[:], crypto.SHA256)

	var regData []byte
	regData  = append(regData, []byte{ 5 }[:]...) // fixed by specification
	regData = append(regData, marshalledPublickey[:]...)
	regData = append(regData, []byte{ byte(len(keyHandle)) }[:]...)
	regData = append(regData, keyHandle[:]...)
	regData = append(regData, muk.cert[:]...)
	regData = append(regData, sig[:]...)

	return &u2f.RegisterResponse{
		RegistrationData: encodeBase64(regData),
		ClientData: encodeBase64(clientDataJson),
	}, nil
}

func (muk *Key) SignResponse(req *u2f.SignRequest) (*u2f.SignResponse, error) {
	appIDHash := sha256.Sum256([]byte(req.AppID))

	counterBytes := make([]byte, 4)
	binary.BigEndian.PutUint32(counterBytes, muk.counter)
	muk.counter += 1

	clientData := u2f.ClientData{
		Typ: "navigator.id.getAssertion",
		Challenge: req.Challenge,
		Origin: req.AppID,
	}
	clientDataJson, err := json.Marshal(clientData)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	clientDataHash := sha256.Sum256(clientDataJson)

	var dataToSign []byte
	dataToSign = append(dataToSign, appIDHash[:]...)
	dataToSign = append(dataToSign, []byte{ 1 }[:]...) // user presence
	dataToSign = append(dataToSign, counterBytes[:]...)
	dataToSign = append(dataToSign, clientDataHash[:]...)

	dataHash := sha256.Sum256(dataToSign)

	// Despite taking a hash function, this actually does not hash the input.
	sig, err := muk.privatekey.Sign(rand.Reader, dataHash[:], crypto.SHA256)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	var signData []byte
	signData = append(signData, []byte{ 1 }[:]...) // user presence
	signData = append(signData, counterBytes[:]...)
	signData = append(signData, sig[:]...)

	return &u2f.SignResponse{
		KeyHandle: req.KeyHandle,
		SignatureData: encodeBase64(signData),
		ClientData: encodeBase64(clientDataJson),
	}, nil
}

func (muk *Key) SetCounter(counter uint32) {
	muk.counter = counter
}
<commit_msg>mocku2f: add support for different key handles<commit_after>/*
Copyright 2015 Gravitational, Inc.

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 mocku2f

/* Mock U2F device for testing.
 * This is not a complete implementation of U2F keys.
 * In particular, the key only supports a single key handle that is specified upon creation
 */

import (
	"bytes"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"math/big"
	"strings"
	"time"

	"github.com/tstranex/u2f"
	"github.com/gravitational/trace"
)

type Key struct {
	keyHandle []byte
	privatekey *ecdsa.PrivateKey
	cert []byte
	counter uint32
}

// The "websafe-base64 encoding" in the U2F specifications removes the padding
func decodeBase64(s string) ([]byte, error) {
	for i := 0; i < len(s)%4; i++ {
		s += "="
	}
	return base64.URLEncoding.DecodeString(s)
}

func encodeBase64(buf []byte) string {
	s := base64.URLEncoding.EncodeToString(buf)
	return strings.TrimRight(s, "=")
}

func selfSignPublicKey(keyToSign *ecdsa.PublicKey) (cert []byte, err error) {
	caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return []byte{}, err
	}
	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			Organization: []string{"Test CA"},
		},
		NotBefore: time.Now(),
		NotAfter: time.Now().Add(time.Hour),
		KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}
	cert, err = x509.CreateCertificate(rand.Reader, &template, &template, keyToSign, caPrivateKey)
	if err != nil {
		return []byte{}, err
	}
	return cert, nil
}

func Create() (*Key, error) {
	keyHandle := make([]byte, 128)
	_, err := rand.Read(keyHandle)
	if err != nil {
		return nil, err
	}
	return CreateWithKeyHandle(keyHandle)
}

func CreateWithKeyHandle(keyHandle []byte) (*Key, error) {
	if len(keyHandle) > 255 {
		return nil, trace.BadParameter("keyHandle length exceeds limit")
	}

	privatekey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}

	cert, err := selfSignPublicKey(&privatekey.PublicKey)
	if err != nil {
		return nil, err
	}

	return &Key{
		keyHandle: keyHandle,
		privatekey: privatekey,
		cert: cert,
		counter: 1,
	}, nil
}

func (muk *Key) RegisterResponse(req *u2f.RegisterRequest) (*u2f.RegisterResponse, error) {
	appIDHash := sha256.Sum256([]byte(req.AppID))

	clientData := u2f.ClientData{
		Typ: "navigator.id.finishEnrollment",
		Challenge: req.Challenge,
		Origin: req.AppID,
	}
	clientDataJson, err := json.Marshal(clientData)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	clientDataHash := sha256.Sum256(clientDataJson)

	marshalledPublickey := elliptic.Marshal(elliptic.P256(), muk.privatekey.PublicKey.X, muk.privatekey.PublicKey.Y)

	var dataToSign []byte
	dataToSign = append(dataToSign[:], []byte{ 0 }[:]...)
	dataToSign = append(dataToSign[:], appIDHash[:]...)
	dataToSign = append(dataToSign[:], clientDataHash[:]...)
	dataToSign = append(dataToSign[:], muk.keyHandle[:]...)
	dataToSign = append(dataToSign[:], marshalledPublickey[:]...)

	dataHash := sha256.Sum256(dataToSign)

	// Despite taking a hash function, this actually does not hash the input.
	sig, err := muk.privatekey.Sign(rand.Reader, dataHash[:], crypto.SHA256)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	var regData []byte
	regData  = append(regData, []byte{ 5 }[:]...) // fixed by specification
	regData = append(regData, marshalledPublickey[:]...)
	regData = append(regData, []byte{ byte(len(muk.keyHandle)) }[:]...)
	regData = append(regData, muk.keyHandle[:]...)
	regData = append(regData, muk.cert[:]...)
	regData = append(regData, sig[:]...)

	return &u2f.RegisterResponse{
		RegistrationData: encodeBase64(regData),
		ClientData: encodeBase64(clientDataJson),
	}, nil
}

func (muk *Key) SignResponse(req *u2f.SignRequest) (*u2f.SignResponse, error) {
	rawKeyHandle, err := decodeBase64(req.KeyHandle)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	if !bytes.Equal(rawKeyHandle, muk.keyHandle) {
		return nil, trace.CompareFailed("wrong keyHandle")
	}

	appIDHash := sha256.Sum256([]byte(req.AppID))

	counterBytes := make([]byte, 4)
	binary.BigEndian.PutUint32(counterBytes, muk.counter)
	muk.counter += 1

	clientData := u2f.ClientData{
		Typ: "navigator.id.getAssertion",
		Challenge: req.Challenge,
		Origin: req.AppID,
	}
	clientDataJson, err := json.Marshal(clientData)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	clientDataHash := sha256.Sum256(clientDataJson)

	var dataToSign []byte
	dataToSign = append(dataToSign, appIDHash[:]...)
	dataToSign = append(dataToSign, []byte{ 1 }[:]...) // user presence
	dataToSign = append(dataToSign, counterBytes[:]...)
	dataToSign = append(dataToSign, clientDataHash[:]...)

	dataHash := sha256.Sum256(dataToSign)

	// Despite taking a hash function, this actually does not hash the input.
	sig, err := muk.privatekey.Sign(rand.Reader, dataHash[:], crypto.SHA256)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	var signData []byte
	signData = append(signData, []byte{ 1 }[:]...) // user presence
	signData = append(signData, counterBytes[:]...)
	signData = append(signData, sig[:]...)

	return &u2f.SignResponse{
		KeyHandle: req.KeyHandle,
		SignatureData: encodeBase64(signData),
		ClientData: encodeBase64(clientDataJson),
	}, nil
}

func (muk *Key) SetCounter(counter uint32) {
	muk.counter = counter
}

<|endoftext|>


<commit_before>/*
Copyright 2021 Gravitational, Inc.

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 webauthn

import (
	"crypto/ecdsa"
	"crypto/x509"

	"github.com/duo-labs/webauthn/protocol/webauthncose"
	"github.com/fxamacker/cbor/v2"
	"github.com/gravitational/teleport/api/types"
	"github.com/gravitational/trace"

	wan "github.com/duo-labs/webauthn/webauthn"
	log "github.com/sirupsen/logrus"
)

// curveP256CBOR is the constant for the P-256 curve in CBOR.
// https://datatracker.ietf.org/doc/html/rfc8152#section-13.1
const curveP256CBOR = 1

func deviceToCredential(dev *types.MFADevice, idOnly bool) (wan.Credential, bool) {
	switch dev := dev.Device.(type) {
	case *types.MFADevice_U2F:
		var pubKeyCBOR []byte
		if !idOnly {
			var err error
			pubKeyCBOR, err = u2fDERKeyToCBOR(dev.U2F.PubKey)
			if err != nil {
				log.Warnf("WebAuthn: failed to convert U2F device key to CBOR: %v", err)
				return wan.Credential{}, false
			}
		}
		return wan.Credential{
			ID:        dev.U2F.KeyHandle,
			PublicKey: pubKeyCBOR,
			Authenticator: wan.Authenticator{
				SignCount: dev.U2F.Counter,
			},
		}, true
	case *types.MFADevice_Webauthn:
		var pubKeyCBOR []byte
		if !idOnly {
			pubKeyCBOR = dev.Webauthn.PublicKeyCbor
		}
		return wan.Credential{
			ID:              dev.Webauthn.CredentialId,
			PublicKey:       pubKeyCBOR,
			AttestationType: dev.Webauthn.AttestationType,
			Authenticator: wan.Authenticator{
				AAGUID:    dev.Webauthn.Aaguid,
				SignCount: dev.Webauthn.SignatureCounter,
			},
		}, true
	default:
		return wan.Credential{}, false
	}
}

func u2fDERKeyToCBOR(der []byte) ([]byte, error) {
	pubKeyI, err := x509.ParsePKIXPublicKey(der)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	// U2F device keys are guaranteed to be ECDSA/P256
	// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#h3_registration-response-message-success.
	pubKey, ok := pubKeyI.(*ecdsa.PublicKey)
	if !ok {
		return nil, trace.Wrap(err)
	}
	return U2FKeyToCBOR(pubKey)
}

// U2FKeyToCBOR transforms a DER-encoded U2F into its CBOR counterpart.
func U2FKeyToCBOR(pubKey *ecdsa.PublicKey) ([]byte, error) {
	// X and Y coordinates must be exactly 32 bytes.
	xBytes := make([]byte, 32)
	yBytes := make([]byte, 32)
	pubKey.X.FillBytes(xBytes)
	pubKey.Y.FillBytes(yBytes)

	pubKeyCBOR, err := cbor.Marshal(&webauthncose.EC2PublicKeyData{
		PublicKeyData: webauthncose.PublicKeyData{
			KeyType:   int64(webauthncose.EllipticKey),
			Algorithm: int64(webauthncose.AlgES256),
		},
		Curve:  curveP256CBOR,
		XCoord: xBytes,
		YCoord: yBytes,
	})
	return pubKeyCBOR, trace.Wrap(err)
}
<commit_msg>Correctly handle U2F public key typecast failure (#15689)<commit_after>/*
Copyright 2021 Gravitational, Inc.

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 webauthn

import (
	"crypto/ecdsa"
	"crypto/x509"

	"github.com/duo-labs/webauthn/protocol/webauthncose"
	"github.com/fxamacker/cbor/v2"
	"github.com/gravitational/teleport/api/types"
	"github.com/gravitational/trace"

	wan "github.com/duo-labs/webauthn/webauthn"
	log "github.com/sirupsen/logrus"
)

// curveP256CBOR is the constant for the P-256 curve in CBOR.
// https://datatracker.ietf.org/doc/html/rfc8152#section-13.1
const curveP256CBOR = 1

func deviceToCredential(dev *types.MFADevice, idOnly bool) (wan.Credential, bool) {
	switch dev := dev.Device.(type) {
	case *types.MFADevice_U2F:
		var pubKeyCBOR []byte
		if !idOnly {
			var err error
			pubKeyCBOR, err = u2fDERKeyToCBOR(dev.U2F.PubKey)
			if err != nil {
				log.Warnf("WebAuthn: failed to convert U2F device key to CBOR: %v", err)
				return wan.Credential{}, false
			}
		}
		return wan.Credential{
			ID:        dev.U2F.KeyHandle,
			PublicKey: pubKeyCBOR,
			Authenticator: wan.Authenticator{
				SignCount: dev.U2F.Counter,
			},
		}, true
	case *types.MFADevice_Webauthn:
		var pubKeyCBOR []byte
		if !idOnly {
			pubKeyCBOR = dev.Webauthn.PublicKeyCbor
		}
		return wan.Credential{
			ID:              dev.Webauthn.CredentialId,
			PublicKey:       pubKeyCBOR,
			AttestationType: dev.Webauthn.AttestationType,
			Authenticator: wan.Authenticator{
				AAGUID:    dev.Webauthn.Aaguid,
				SignCount: dev.Webauthn.SignatureCounter,
			},
		}, true
	default:
		return wan.Credential{}, false
	}
}

func u2fDERKeyToCBOR(der []byte) ([]byte, error) {
	pubKeyI, err := x509.ParsePKIXPublicKey(der)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	// U2F device keys are guaranteed to be ECDSA/P256
	// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#h3_registration-response-message-success.
	pubKey, ok := pubKeyI.(*ecdsa.PublicKey)
	if !ok {
		return nil, trace.BadParameter("U2F public key has an unexpected type: %T", pubKeyI)
	}
	return U2FKeyToCBOR(pubKey)
}

// U2FKeyToCBOR transforms a DER-encoded U2F into its CBOR counterpart.
func U2FKeyToCBOR(pubKey *ecdsa.PublicKey) ([]byte, error) {
	// X and Y coordinates must be exactly 32 bytes.
	xBytes := make([]byte, 32)
	yBytes := make([]byte, 32)
	pubKey.X.FillBytes(xBytes)
	pubKey.Y.FillBytes(yBytes)

	pubKeyCBOR, err := cbor.Marshal(&webauthncose.EC2PublicKeyData{
		PublicKeyData: webauthncose.PublicKeyData{
			KeyType:   int64(webauthncose.EllipticKey),
			Algorithm: int64(webauthncose.AlgES256),
		},
		Curve:  curveP256CBOR,
		XCoord: xBytes,
		YCoord: yBytes,
	})
	return pubKeyCBOR, trace.Wrap(err)
}
<|endoftext|>


<commit_before>// +build js,wasm

package websocket

import (
	"bufio"
	"context"
	"testing"
	"time"

	"github.com/libp2p/go-libp2p-core/sec/insecure"
	mplex "github.com/libp2p/go-libp2p-mplex"
	tptu "github.com/libp2p/go-libp2p-transport-upgrader"
	ma "github.com/multiformats/go-multiaddr"
)

func TestInBrowser(t *testing.T) {
	tpt := New(&tptu.Upgrader{
		Secure: insecure.New("browserPeer"),
		Muxer:  new(mplex.Transport),
	})
	addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5555/ws")
	if err != nil {
		t.Fatal("could not parse multiaddress:" + err.Error())
	}
	conn, err := tpt.Dial(context.Background(), addr, "serverPeer")
	if err != nil {
		t.Fatal("could not dial server:" + err.Error())
	}
	defer conn.Close()

	stream, err := conn.AcceptStream()
	if err != nil {
		t.Fatal("could not accept stream:" + err.Error())
	}
	defer stream.Close()

	buf := bufio.NewReader(stream)
	msg, err := buf.ReadString('\n')
	if err != nil {
		t.Fatal("could not read ping message:" + err.Error())
	}
	expected := "ping\n"
	if msg != expected {
		t.Fatalf("Received wrong message. Expected %q but got %q", expected, msg)
	}

	_, err = stream.Write([]byte("pong\n"))
	if err != nil {
		t.Fatal("could not write pong message:" + err.Error())
	}

	// TODO(albrow): This hack is necessary in order to give the reader time to
	// finish. We should find some way to remove it.
	time.Sleep(1 * time.Second)
}
<commit_msg>Expand comment about time.Sleep hack<commit_after>// +build js,wasm

package websocket

import (
	"bufio"
	"context"
	"testing"
	"time"

	"github.com/libp2p/go-libp2p-core/sec/insecure"
	mplex "github.com/libp2p/go-libp2p-mplex"
	tptu "github.com/libp2p/go-libp2p-transport-upgrader"
	ma "github.com/multiformats/go-multiaddr"
)

func TestInBrowser(t *testing.T) {
	tpt := New(&tptu.Upgrader{
		Secure: insecure.New("browserPeer"),
		Muxer:  new(mplex.Transport),
	})
	addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5555/ws")
	if err != nil {
		t.Fatal("could not parse multiaddress:" + err.Error())
	}
	conn, err := tpt.Dial(context.Background(), addr, "serverPeer")
	if err != nil {
		t.Fatal("could not dial server:" + err.Error())
	}
	defer conn.Close()

	stream, err := conn.AcceptStream()
	if err != nil {
		t.Fatal("could not accept stream:" + err.Error())
	}
	defer stream.Close()

	buf := bufio.NewReader(stream)
	msg, err := buf.ReadString('\n')
	if err != nil {
		t.Fatal("could not read ping message:" + err.Error())
	}
	expected := "ping\n"
	if msg != expected {
		t.Fatalf("Received wrong message. Expected %q but got %q", expected, msg)
	}

	_, err = stream.Write([]byte("pong\n"))
	if err != nil {
		t.Fatal("could not write pong message:" + err.Error())
	}

	// TODO(albrow): This hack is necessary in order to give the reader time to
	// finish. As soon as this test function returns, the browser window is
	// closed, which means there is no time for the other end of the connection to
	// read the "pong" message. We should find some way to remove this hack if
	// possible.
	time.Sleep(1 * time.Second)
}
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/newrelic/go-agent"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/controller/flash_message"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/rs/cors"
	"github.com/uber-go/zap"
)

var _ = fmt.Print

func PanicHandler(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				var err error
				switch errorType := r.(type) {
				case string:
					err = fmt.Errorf(errorType)
				case error:
					err = errorType
				default:
					err = fmt.Errorf("Unknown error type: %v", errorType)
				}
				if err != nil {
					controller.InternalServerError(w, errors.InternalWrapf(err, "panic ocurred"))
					return
				}
			}
		}()
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func AccessLogger(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		writerProxy := controller.WrapWriter(w)
		h.ServeHTTP(writerProxy, r)
		func() {
			end := time.Now()
			status := writerProxy.Status()
			if status == 0 {
				status = http.StatusOK
			}
			remoteAddr := r.RemoteAddr
			if remoteAddr != "" {
				remoteAddr = (strings.Split(remoteAddr, ":"))[0]
			}

			// 180.76.15.26 - - [31/Jul/2016:13:18:07 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
			logger.AccessLogger.Info(
				"",
				zap.String("date", start.Format(time.RFC3339)),
				zap.String("method", r.Method),
				zap.String("url", r.URL.String()),
				zap.Int("status", status),
				zap.Int("bytes", writerProxy.BytesWritten()),
				zap.String("remoteAddr", remoteAddr),
				zap.String("userAgent", r.Header.Get("User-Agent")),
				zap.String("referer", r.Referer()),
				zap.Duration("elapsed", end.Sub(start)/time.Millisecond),
			)
		}()
	}
	return http.HandlerFunc(fn)
}

func NewRelic(h http.Handler) http.Handler {
	key := os.Getenv("NEW_RELIC_LICENSE_KEY")
	if key == "" {
		return h
	}

	c := newrelic.NewConfig("lekcije", key)
	app, err := newrelic.NewApplication(c)
	if err != nil {
		logger.AppLogger.Error("Failed to newrelic.NewApplication()", zap.Error(err))
		return h
	}
	fn := func(w http.ResponseWriter, r *http.Request) {
		tx := app.StartTransaction(r.URL.Path, w, r)
		defer tx.End()
		h.ServeHTTP(tx, r)
	}
	return http.HandlerFunc(fn)
}

func SetDBAndRedisToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		fmt.Printf("%s %s\n", r.Method, r.RequestURI)

		db, c, err := model.OpenDBAndSetToContext(ctx, os.Getenv("DB_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer db.Close()

		redisClient, c, err := model.OpenRedisAndSetToContext(c, os.Getenv("REDIS_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer redisClient.Close()

		_, c = flash_message.NewStoreRedisAndSetToContext(c, redisClient)

		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetLoggedInUserToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.ApiTokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			fmt.Printf("loggedInUser = %+v\n", user)
			h.ServeHTTP(w, r)
			return
		}
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func LoginRequiredFilter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if !strings.HasPrefix(r.RequestURI, "/me") {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.ApiTokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			fmt.Printf("loggedInUser = %+v\n", user)
			h.ServeHTTP(w, r)
			return
		}
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func CORS(h http.Handler) http.Handler {
	origins := []string{}
	if strings.HasPrefix(config.StaticURL(), "http") {
		origins = append(origins, strings.TrimRight(config.StaticURL(), "/static"))
	}
	c := cors.New(cors.Options{
		AllowedOrigins: origins,
		//Debug:          true,
	})
	fn := func(w http.ResponseWriter, r *http.Request) {
		c.HandlerFunc(w, r)
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<commit_msg>Remove useless if<commit_after>package middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/newrelic/go-agent"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/controller/flash_message"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/rs/cors"
	"github.com/uber-go/zap"
)

var _ = fmt.Print

func PanicHandler(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				var err error
				switch errorType := r.(type) {
				case string:
					err = fmt.Errorf(errorType)
				case error:
					err = errorType
				default:
					err = fmt.Errorf("Unknown error type: %v", errorType)
				}
				controller.InternalServerError(w, errors.InternalWrapf(err, "panic ocurred"))
				return
			}
		}()
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func AccessLogger(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		writerProxy := controller.WrapWriter(w)
		h.ServeHTTP(writerProxy, r)
		func() {
			end := time.Now()
			status := writerProxy.Status()
			if status == 0 {
				status = http.StatusOK
			}
			remoteAddr := r.RemoteAddr
			if remoteAddr != "" {
				remoteAddr = (strings.Split(remoteAddr, ":"))[0]
			}

			// 180.76.15.26 - - [31/Jul/2016:13:18:07 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
			logger.AccessLogger.Info(
				"",
				zap.String("date", start.Format(time.RFC3339)),
				zap.String("method", r.Method),
				zap.String("url", r.URL.String()),
				zap.Int("status", status),
				zap.Int("bytes", writerProxy.BytesWritten()),
				zap.String("remoteAddr", remoteAddr),
				zap.String("userAgent", r.Header.Get("User-Agent")),
				zap.String("referer", r.Referer()),
				zap.Duration("elapsed", end.Sub(start)/time.Millisecond),
			)
		}()
	}
	return http.HandlerFunc(fn)
}

func NewRelic(h http.Handler) http.Handler {
	key := os.Getenv("NEW_RELIC_LICENSE_KEY")
	if key == "" {
		return h
	}

	c := newrelic.NewConfig("lekcije", key)
	app, err := newrelic.NewApplication(c)
	if err != nil {
		logger.AppLogger.Error("Failed to newrelic.NewApplication()", zap.Error(err))
		return h
	}
	fn := func(w http.ResponseWriter, r *http.Request) {
		tx := app.StartTransaction(r.URL.Path, w, r)
		defer tx.End()
		h.ServeHTTP(tx, r)
	}
	return http.HandlerFunc(fn)
}

func SetDBAndRedisToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		fmt.Printf("%s %s\n", r.Method, r.RequestURI)

		db, c, err := model.OpenDBAndSetToContext(ctx, os.Getenv("DB_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer db.Close()

		redisClient, c, err := model.OpenRedisAndSetToContext(c, os.Getenv("REDIS_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer redisClient.Close()

		_, c = flash_message.NewStoreRedisAndSetToContext(c, redisClient)

		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetLoggedInUserToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.ApiTokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			fmt.Printf("loggedInUser = %+v\n", user)
			h.ServeHTTP(w, r)
			return
		}
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func LoginRequiredFilter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if !strings.HasPrefix(r.RequestURI, "/me") {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.ApiTokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			fmt.Printf("loggedInUser = %+v\n", user)
			h.ServeHTTP(w, r)
			return
		}
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func CORS(h http.Handler) http.Handler {
	origins := []string{}
	if strings.HasPrefix(config.StaticURL(), "http") {
		origins = append(origins, strings.TrimRight(config.StaticURL(), "/static"))
	}
	c := cors.New(cors.Options{
		AllowedOrigins: origins,
		//Debug:          true,
	})
	fn := func(w http.ResponseWriter, r *http.Request) {
		c.HandlerFunc(w, r)
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<|endoftext|>