<commit_before>package main

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sort"
	"sync"
	"time"

	"github.com/UniversityRadioYork/baps3-go"
)

type GetResponse struct {
	Status string
	Value  interface{}
}

func GetOk(value interface{}) *GetResponse {
	r := new(GetResponse)
	r.Status = "ok"
	r.Value = value
	return r
}

type bfConnector struct {
	conn   *baps3.Connector
	name   string
	wg     *sync.WaitGroup
	logger *log.Logger

	// Cache of BAPS3 service internal state
	features map[Feature]struct{}
	state    string
	time     time.Duration
	file     string

	reqCh chan httpRequest
	resCh <-chan baps3.Message

	// TODO(CaptainHayashi): move this away from baps3.Message to
	// something generic.
	updateCh chan<- baps3.Message
}

func initBfConnector(name string, updateCh chan baps3.Message, waitGroup *sync.WaitGroup, logger *log.Logger) (c *bfConnector) {
	resCh := make(chan baps3.Message)

	c = new(bfConnector)
	c.resCh = resCh
	c.conn = baps3.InitConnector(name, resCh, waitGroup, logger)
	c.name = name
	c.wg = waitGroup
	c.logger = logger
	c.reqCh = make(chan httpRequest)
	c.updateCh = updateCh

	c.features = make(map[Feature]struct{})
	return
}

func (c *bfConnector) Run() {
	defer c.wg.Done()
	defer close(c.conn.ReqCh)

	go c.conn.Run()

	fmt.Printf("connector %s now listening for requests\n", c.name)

	for {
		select {
		case rq, ok := <-c.reqCh:
			if !ok {
				return
			}

			// TODO(CaptainHayashi): probably make this more robust
			resource := strings.Replace(rq.resource, "/"+c.name, "", 1)
			fmt.Printf("connector %s response %s\n", c.name, resource)

			// TODO(CaptainHayashi): other methods
			rq.resCh <- c.get(resource)
		case res := <-c.resCh:
			var err error
			switch res.Word() {
			case baps3.RsFeatures:
				err = c.updateFeaturesFromMessage(res)
			case baps3.RsFile:
				err = c.updateFileFromMessage(res)
			case baps3.RsState:
				err = c.updateStateFromMessage(res)
			case baps3.RsTime:
				err = c.updateTimeFromMessage(res)
			}
			if err != nil {
				fmt.Println(err)
			}
			c.updateCh <- res
		}
	}

	return
}

// hasFeature returns whether the connected server advertises the given feature.
func (c *bfConnector) hasFeature(f Feature) bool {
	// TODO(CaptainHayashi): Actually check this
	return true
}

func splitResource(resource string) []string {
	res := strings.Split(strings.Trim(resource, "/"), "/")

	// The empty resource is returned as {""}: let's fix that
	if len(res) == 1 && res[0] == "" {
		res = []string{}
	}

	return res
}

func (c *bfConnector) get(resource string) interface{} {
	// TODO(CaptainHayashi): HTTP status codes

	resourcePath := splitResource(resource)

	if len(resourcePath) == 0 {
		return GetOk(c.rootGet())
	}

	var r interface{}

	switch resourcePath[0] {
	case "control":
		r = c.control(resourcePath[1:])
	case "player":
		r = c.player(resourcePath[1:])
		//case "playlist":
		//	r = c.playlist(resourcePath[1:])
	}

	if r == nil {
		// TODO(CaptainHayashi): more errors
		return GetResponse{
			Status: "what",
			Value:  "resource not found: " + resource,
		}
	}

	return GetOk(r)
}

// control is the main handler for the /control resource.
func (c *bfConnector) control(resourcePath []string) interface{} {
	if len(resourcePath) == 0 {
		return c.controlGet()
	}

	if len(resourcePath) == 1 {
		switch resourcePath[0] {
		case "features":
			return c.featuresGet()
		case "state":
			return c.stateGet()
		}
	}

	return nil
}

// player is the main handler for the /player resource.
func (c *bfConnector) player(resourcePath []string) interface{} {
	if len(resourcePath) == 0 {
		return c.playerGet()
	}

	if len(resourcePath) == 1 {
		switch resourcePath[0] {
		case "time":
			return c.timeGet()
		case "file":
			return c.fileGet()
		}
	}

	return nil
}

// GET value for /
func (c *bfConnector) rootGet() interface{} {
	return struct {
		Control  interface{} `json:"control,omitempty"`
		Player   interface{} `json:"player,omitempty"`
		Playlist interface{} `json:"playlist,omitempty"`
	}{
		c.controlGet(),
		c.playerGet(),
		[]string{},
	}
}

// GET value for /control
func (c *bfConnector) controlGet() interface{} {
	return struct {
		Features []string `json:"features"`
		State string `json:"state"`
	}{
		c.featuresGet(),
		c.stateGet(),
	}
}

// GET value for /control/features
func (c *bfConnector) featuresGet() []string {
	fstrings := []string{}

	for k := range c.features {
		fstrings = append(fstrings, k.String())
	}

	sort.Strings(fstrings)
	return fstrings
}

// GET value for /control/state
func (c *bfConnector) stateGet() string {
	return c.state
}

// GET value for /player
func (c *bfConnector) playerGet() interface{} {
	// TODO(CaptainHayashi): Probably a spec change, but the fact that this
	// resource is guarded by more than one feature is iffy.  Do we need a
	// Player feature?
	if !(c.hasFeature(FtFileLoad) || c.hasFeature(FtTimeReport)) {
		return nil
	}

	return struct {
		Time interface{} `json:"time"`
		File interface{} `json:"file,omitempty"`
	}{
		c.timeGet(),
		c.fileGet(),
	}
}

// GET value for /player/time
func (c *bfConnector) timeGet() interface{} {
	if !c.hasFeature(FtTimeReport) {
		return nil
	}

	// Time is reported in _micro_seconds
	return c.time.Nanoseconds() / 1000
}

// GET value for /player/file
func (c *bfConnector) fileGet() interface{} {
	if !c.hasFeature(FtFileLoad) {
		return nil
	}

	return c.file
}

func (c *bfConnector) updateFeaturesFromMessage(res baps3.Message) (err error) {
	feats := make(map[Feature]struct{})

	for i := 0; ; i++ {
		if fstring, e := res.Arg(i); e == nil {
			feat := LookupFeature(fstring)
			if feat == FtUnknown {
				err = fmt.Errorf("unknown feature: %q", fstring)
				break
			}
			feats[feat] = struct{}{}
		} else {
			// e != nil means we've run out of arguments.
			break
		}
	}

	c.features = feats
	return
}

func (c *bfConnector) updateFileFromMessage(res baps3.Message) (err error) {
	file, err := res.Arg(0)
	if err != nil {
		c.file = ""
		return
	}

	c.file = file

	return
}

func (c *bfConnector) updateStateFromMessage(res baps3.Message) (err error) {
	state, err := res.Arg(0)
	if err != nil {
		c.state = "???"
		return
	}

	c.state = state

	return
}

func (c *bfConnector) updateTimeFromMessage(res baps3.Message) (err error) {
	usecs, err := res.Arg(0)
	if err != nil {
		return
	}

	usec, err := strconv.Atoi(usecs)
	if err != nil {
		return
	}

	c.time = time.Duration(usec) * time.Microsecond

	return
}
<commit_msg>Actually check the features cache now.<commit_after>package main

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sort"
	"sync"
	"time"

	"github.com/UniversityRadioYork/baps3-go"
)

type GetResponse struct {
	Status string
	Value  interface{}
}

func GetOk(value interface{}) *GetResponse {
	r := new(GetResponse)
	r.Status = "ok"
	r.Value = value
	return r
}

type bfConnector struct {
	conn   *baps3.Connector
	name   string
	wg     *sync.WaitGroup
	logger *log.Logger

	// Cache of BAPS3 service internal state
	features map[Feature]struct{}
	state    string
	time     time.Duration
	file     string

	reqCh chan httpRequest
	resCh <-chan baps3.Message

	// TODO(CaptainHayashi): move this away from baps3.Message to
	// something generic.
	updateCh chan<- baps3.Message
}

func initBfConnector(name string, updateCh chan baps3.Message, waitGroup *sync.WaitGroup, logger *log.Logger) (c *bfConnector) {
	resCh := make(chan baps3.Message)

	c = new(bfConnector)
	c.resCh = resCh
	c.conn = baps3.InitConnector(name, resCh, waitGroup, logger)
	c.name = name
	c.wg = waitGroup
	c.logger = logger
	c.reqCh = make(chan httpRequest)
	c.updateCh = updateCh

	c.features = make(map[Feature]struct{})
	return
}

func (c *bfConnector) Run() {
	defer c.wg.Done()
	defer close(c.conn.ReqCh)

	go c.conn.Run()

	fmt.Printf("connector %s now listening for requests\n", c.name)

	for {
		select {
		case rq, ok := <-c.reqCh:
			if !ok {
				return
			}

			// TODO(CaptainHayashi): probably make this more robust
			resource := strings.Replace(rq.resource, "/"+c.name, "", 1)
			fmt.Printf("connector %s response %s\n", c.name, resource)

			// TODO(CaptainHayashi): other methods
			rq.resCh <- c.get(resource)
		case res := <-c.resCh:
			var err error
			switch res.Word() {
			case baps3.RsFeatures:
				err = c.updateFeaturesFromMessage(res)
			case baps3.RsFile:
				err = c.updateFileFromMessage(res)
			case baps3.RsState:
				err = c.updateStateFromMessage(res)
			case baps3.RsTime:
				err = c.updateTimeFromMessage(res)
			}
			if err != nil {
				fmt.Println(err)
			}
			c.updateCh <- res
		}
	}

	return
}

// hasFeature returns whether the connected server advertises the given feature.
func (c *bfConnector) hasFeature(f Feature) bool {
	_, ok := c.features[f]
	return ok
}

func splitResource(resource string) []string {
	res := strings.Split(strings.Trim(resource, "/"), "/")

	// The empty resource is returned as {""}: let's fix that
	if len(res) == 1 && res[0] == "" {
		res = []string{}
	}

	return res
}

func (c *bfConnector) get(resource string) interface{} {
	// TODO(CaptainHayashi): HTTP status codes

	resourcePath := splitResource(resource)

	if len(resourcePath) == 0 {
		return GetOk(c.rootGet())
	}

	var r interface{}

	switch resourcePath[0] {
	case "control":
		r = c.control(resourcePath[1:])
	case "player":
		r = c.player(resourcePath[1:])
		//case "playlist":
		//	r = c.playlist(resourcePath[1:])
	}

	if r == nil {
		// TODO(CaptainHayashi): more errors
		return GetResponse{
			Status: "what",
			Value:  "resource not found: " + resource,
		}
	}

	return GetOk(r)
}

// control is the main handler for the /control resource.
func (c *bfConnector) control(resourcePath []string) interface{} {
	if len(resourcePath) == 0 {
		return c.controlGet()
	}

	if len(resourcePath) == 1 {
		switch resourcePath[0] {
		case "features":
			return c.featuresGet()
		case "state":
			return c.stateGet()
		}
	}

	return nil
}

// player is the main handler for the /player resource.
func (c *bfConnector) player(resourcePath []string) interface{} {
	if len(resourcePath) == 0 {
		return c.playerGet()
	}

	if len(resourcePath) == 1 {
		switch resourcePath[0] {
		case "time":
			return c.timeGet()
		case "file":
			return c.fileGet()
		}
	}

	return nil
}

// GET value for /
func (c *bfConnector) rootGet() interface{} {
	return struct {
		Control  interface{} `json:"control,omitempty"`
		Player   interface{} `json:"player,omitempty"`
		Playlist interface{} `json:"playlist,omitempty"`
	}{
		c.controlGet(),
		c.playerGet(),
		[]string{},
	}
}

// GET value for /control
func (c *bfConnector) controlGet() interface{} {
	return struct {
		Features []string `json:"features"`
		State string `json:"state"`
	}{
		c.featuresGet(),
		c.stateGet(),
	}
}

// GET value for /control/features
func (c *bfConnector) featuresGet() []string {
	fstrings := []string{}

	for k := range c.features {
		fstrings = append(fstrings, k.String())
	}

	sort.Strings(fstrings)
	return fstrings
}

// GET value for /control/state
func (c *bfConnector) stateGet() string {
	return c.state
}

// GET value for /player
func (c *bfConnector) playerGet() interface{} {
	// TODO(CaptainHayashi): Probably a spec change, but the fact that this
	// resource is guarded by more than one feature is iffy.  Do we need a
	// Player feature?
	if !(c.hasFeature(FtFileLoad) || c.hasFeature(FtTimeReport)) {
		return nil
	}

	return struct {
		Time interface{} `json:"time"`
		File interface{} `json:"file,omitempty"`
	}{
		c.timeGet(),
		c.fileGet(),
	}
}

// GET value for /player/time
func (c *bfConnector) timeGet() interface{} {
	if !c.hasFeature(FtTimeReport) {
		return nil
	}

	// Time is reported in _micro_seconds
	return c.time.Nanoseconds() / 1000
}

// GET value for /player/file
func (c *bfConnector) fileGet() interface{} {
	if !c.hasFeature(FtFileLoad) {
		return nil
	}

	return c.file
}

func (c *bfConnector) updateFeaturesFromMessage(res baps3.Message) (err error) {
	feats := make(map[Feature]struct{})

	for i := 0; ; i++ {
		if fstring, e := res.Arg(i); e == nil {
			feat := LookupFeature(fstring)
			if feat == FtUnknown {
				err = fmt.Errorf("unknown feature: %q", fstring)
				break
			}
			feats[feat] = struct{}{}
		} else {
			// e != nil means we've run out of arguments.
			break
		}
	}

	c.features = feats
	return
}

func (c *bfConnector) updateFileFromMessage(res baps3.Message) (err error) {
	file, err := res.Arg(0)
	if err != nil {
		c.file = ""
		return
	}

	c.file = file

	return
}

func (c *bfConnector) updateStateFromMessage(res baps3.Message) (err error) {
	state, err := res.Arg(0)
	if err != nil {
		c.state = "???"
		return
	}

	c.state = state

	return
}

func (c *bfConnector) updateTimeFromMessage(res baps3.Message) (err error) {
	usecs, err := res.Arg(0)
	if err != nil {
		return
	}

	usec, err := strconv.Atoi(usecs)
	if err != nil {
		return
	}

	c.time = time.Duration(usec) * time.Microsecond

	return
}
<|endoftext|>


<commit_before>// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/

package glfw

// GLFW Version
const (
	VersionMajor    = 2
	VersionMinor    = 7
	VersionRevision = 0
)

// Key and button state/action definitions
const (
	KeyRelease = iota
	KeyPress
)

// Keyboard key definitions: 8-bit ISO-8859-1 (Latin 1) encoding is used
// for printable keys (such as A-Z, 0-9 etc), and values above 256
// represent Special (non-printable) keys (e.g. F1, Page Up etc).
const (
	KeyUnknown = -1
	KeySpace   = 32
	KeySpecial = 256
)

const (
	_ = (KeySpecial + iota)
	KeyEsc
	KeyF1
	KeyF2
	KeyF3
	KeyF4
	KeyF5
	KeyF6
	KeyF7
	KeyF8
	KeyF9
	KeyF10
	KeyF11
	KeyF12
	KeyF13
	KeyF14
	KeyF15
	KeyF16
	KeyF17
	KeyF18
	KeyF19
	KeyF20
	KeyF21
	KeyF22
	KeyF23
	KeyF24
	KeyF25
	KeyUp
	KeyDown
	KeyLeft
	KeyRight
	KeyLshift
	KeyRshift
	KeyLctrl
	KeyRctrl
	KeyLalt
	KeyRalt
	KeyTab
	KeyEnter
	KeyBackspace
	KeyInsert
	KeyDel
	KeyPageup
	KeyPagedown
	KeyHome
	KeyEnd
	KeyKP0
	KeyKP1
	KeyKP2
	KeyKP3
	KeyKP4
	KeyKP5
	KeyKP6
	KeyKP7
	KeyKP8
	KeyKP9
	KeyKPDidivde
	KeyKPMultiply
	KeyKPSubtract
	KeyKPAdd
	KeyKPDecimal
	KeyKPEqual
	KeyKPEnter
	KeyKPNumlock
	KeyCapslock
	KeyScrolllock
	KeyPause
	KeyLsuper
	KeyRsuper
	KeyMenu
	KeyLast = KeyMenu
)

// Mouse button definitions
const (
	Mouse1 = iota
	Mouse2
	Mouse3
	Mouse4
	Mouse5
	Mouse6
	Mouse7
	Mouse8
	MouseLast   = Mouse8
	MouseLeft   = Mouse1
	MouseRight  = Mouse2
	MouseMiddle = Mouse3
)

// Joystick identifiers
const (
	Joy1 = iota
	Joy2
	Joy3
	Joy4
	Joy5
	Joy6
	Joy7
	Joy8
	Joy9
	Joy10
	Joy11
	Joy12
	Joy13
	Joy14
	Joy15
	Joy16
	JoyLast = Joy16
)

// glfwOpenWindow modes
const (
	_ = 0x00010000 + iota
	Windowed
	Fullscreen
)

// glfwGetWindowParam / glfwOpenWindowHint tokens
const (
	_ = 0x00020000 + iota
	Opened
	Active
	Iconified
	Accelerated
	RedBits
	GreenBits
	BlueBits
	AlphaBits
	DepthBits
	StencilBits

	// The following constants are used for both glfwGetWindowParam
	// and glfwOpenWindowHint
	RefreshRate
	AccumRedBits
	AccumGreenBits
	AccumBlueBits
	AccumAlphaBits
	AuxBuffers
	Stereo
	WindowNoResize
	FsaaSamples
	OpenGLVersionMajor
	OpenGLVersionMinor
	OpenGLForwardCompat
	OpenGLDebugContext
	OpenGLProfile
)

// GLFW_OPENGL_PROFILE tokens
const (
	_ = 0x00050000 + iota
	OpenGLCoreProfile
	OpenGLCompatProfile
)

// glfwEnable/glfwDisable tokens
const (
	_ = 0x00030000 + iota
	MouseCursor
	StickyKeys
	StickyMouseButtons
	SystemKeys
	KeyRepeat
	AutoPollEvents
)

// glfwWaitThread wait modes
const (
	_ = 0x00040000 + iota
	Wait
	NoWait
)

// glfwGetJoystickParam tokens
const (
	_ = 0x00050000 + iota
	Present
	Axes
	Buttons
)

// glfwReadImage/glfwLoadTexture2D flags
const (
	NoRescaleBit = 1 << iota // Only for glfwReadImage
	OriginUlBit
	BuildMipmapsBit // Only for glfwLoadTexture2D
	AlphaMapBit
)

// Time spans longer than this (seconds) are considered to be infinity
const Infinity = 100000.0
<commit_msg>Fixes issue #22: Typo in constant name: KeyKPDidivde -> KeyKPDivide<commit_after>// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/

package glfw

// GLFW Version
const (
	VersionMajor    = 2
	VersionMinor    = 7
	VersionRevision = 0
)

// Key and button state/action definitions
const (
	KeyRelease = iota
	KeyPress
)

// Keyboard key definitions: 8-bit ISO-8859-1 (Latin 1) encoding is used
// for printable keys (such as A-Z, 0-9 etc), and values above 256
// represent Special (non-printable) keys (e.g. F1, Page Up etc).
const (
	KeyUnknown = -1
	KeySpace   = 32
	KeySpecial = 256
)

const (
	_ = (KeySpecial + iota)
	KeyEsc
	KeyF1
	KeyF2
	KeyF3
	KeyF4
	KeyF5
	KeyF6
	KeyF7
	KeyF8
	KeyF9
	KeyF10
	KeyF11
	KeyF12
	KeyF13
	KeyF14
	KeyF15
	KeyF16
	KeyF17
	KeyF18
	KeyF19
	KeyF20
	KeyF21
	KeyF22
	KeyF23
	KeyF24
	KeyF25
	KeyUp
	KeyDown
	KeyLeft
	KeyRight
	KeyLshift
	KeyRshift
	KeyLctrl
	KeyRctrl
	KeyLalt
	KeyRalt
	KeyTab
	KeyEnter
	KeyBackspace
	KeyInsert
	KeyDel
	KeyPageup
	KeyPagedown
	KeyHome
	KeyEnd
	KeyKP0
	KeyKP1
	KeyKP2
	KeyKP3
	KeyKP4
	KeyKP5
	KeyKP6
	KeyKP7
	KeyKP8
	KeyKP9
	KeyKPDivide
	KeyKPMultiply
	KeyKPSubtract
	KeyKPAdd
	KeyKPDecimal
	KeyKPEqual
	KeyKPEnter
	KeyKPNumlock
	KeyCapslock
	KeyScrolllock
	KeyPause
	KeyLsuper
	KeyRsuper
	KeyMenu
	KeyLast = KeyMenu
)

// Mouse button definitions
const (
	Mouse1 = iota
	Mouse2
	Mouse3
	Mouse4
	Mouse5
	Mouse6
	Mouse7
	Mouse8
	MouseLast   = Mouse8
	MouseLeft   = Mouse1
	MouseRight  = Mouse2
	MouseMiddle = Mouse3
)

// Joystick identifiers
const (
	Joy1 = iota
	Joy2
	Joy3
	Joy4
	Joy5
	Joy6
	Joy7
	Joy8
	Joy9
	Joy10
	Joy11
	Joy12
	Joy13
	Joy14
	Joy15
	Joy16
	JoyLast = Joy16
)

// glfwOpenWindow modes
const (
	_ = 0x00010000 + iota
	Windowed
	Fullscreen
)

// glfwGetWindowParam / glfwOpenWindowHint tokens
const (
	_ = 0x00020000 + iota
	Opened
	Active
	Iconified
	Accelerated
	RedBits
	GreenBits
	BlueBits
	AlphaBits
	DepthBits
	StencilBits

	// The following constants are used for both glfwGetWindowParam
	// and glfwOpenWindowHint
	RefreshRate
	AccumRedBits
	AccumGreenBits
	AccumBlueBits
	AccumAlphaBits
	AuxBuffers
	Stereo
	WindowNoResize
	FsaaSamples
	OpenGLVersionMajor
	OpenGLVersionMinor
	OpenGLForwardCompat
	OpenGLDebugContext
	OpenGLProfile
)

// GLFW_OPENGL_PROFILE tokens
const (
	_ = 0x00050000 + iota
	OpenGLCoreProfile
	OpenGLCompatProfile
)

// glfwEnable/glfwDisable tokens
const (
	_ = 0x00030000 + iota
	MouseCursor
	StickyKeys
	StickyMouseButtons
	SystemKeys
	KeyRepeat
	AutoPollEvents
)

// glfwWaitThread wait modes
const (
	_ = 0x00040000 + iota
	Wait
	NoWait
)

// glfwGetJoystickParam tokens
const (
	_ = 0x00050000 + iota
	Present
	Axes
	Buttons
)

// glfwReadImage/glfwLoadTexture2D flags
const (
	NoRescaleBit = 1 << iota // Only for glfwReadImage
	OriginUlBit
	BuildMipmapsBit // Only for glfwLoadTexture2D
	AlphaMapBit
)

// Time spans longer than this (seconds) are considered to be infinity
const Infinity = 100000.0
<|endoftext|>


<commit_before>package maas

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
)

type ConfigSuite struct{}

var _ = Suite(new(ConfigSuite))

// copyAttrs copies values from src into dest.  If src contains a key that was
// already in dest, its value in dest will still be updated to the one from
// src.
func copyAttrs(src, dest map[string]interface{}) {
	for k, v := range src {
		dest[k] = v
	}
}

// newConfig creates a MAAS environment config from attributes.
func newConfig(values map[string]interface{}) (*maasEnvironConfig, error) {
	defaults := map[string]interface{}{
		"name":            "testenv",
		"type":            "maas",
		"admin-secret":    "ssshhhhhh",
		"authorized-keys": "I-am-not-a-real-key",
		"agent-version":   version.CurrentNumber().String(),
		// These are not needed by MAAS, but juju-core breaks without them. Needs
		// fixing there.
		"ca-cert":        testing.CACert,
		"ca-private-key": testing.CAKey,
	}
	cfg := make(map[string]interface{})
	copyAttrs(defaults, cfg)
	copyAttrs(values, cfg)
	env, err := environs.NewFromAttrs(cfg)
	if err != nil {
		return nil, err
	}
	return env.(*maasEnviron).ecfg(), nil
}

func (ConfigSuite) TestParsesMAASSettings(c *C) {
	server := "http://maas.example.com/maas/"
	oauth := "consumer-key:resource-token:resource-secret"
	ecfg, err := newConfig(map[string]interface{}{
		"maas-server": server,
		"maas-oauth":  oauth,
	})
	c.Assert(err, IsNil)
	c.Check(ecfg.MAASServer(), Equals, server)
	c.Check(ecfg.MAASOAuth(), DeepEquals, oauth)
}

func (ConfigSuite) TestChecksWellFormedMaasServer(c *C) {
	_, err := newConfig(map[string]interface{}{
		"maas-server": "This should have been a URL.",
		"maas-oauth":  "consumer-key:resource-token:resource-secret",
	})
	c.Assert(err, NotNil)
	c.Check(err, ErrorMatches, ".*malformed maas-server.*")
}

func (ConfigSuite) TestChecksWellFormedMaasOAuth(c *C) {
	_, err := newConfig(map[string]interface{}{
		"maas-server": "http://maas.example.com/maas/",
		"maas-oauth":  "This should have been a 3-part token.",
	})
	c.Assert(err, NotNil)
	c.Check(err, ErrorMatches, ".*malformed maas-oauth.*")
}
<commit_msg>Test for desired behaviour: maasEnvironProvider.Validate() calls environs.config.Validate().  Tests: 1 failure.<commit_after>package maas

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
)

type ConfigSuite struct{}

var _ = Suite(new(ConfigSuite))

// copyAttrs copies values from src into dest.  If src contains a key that was
// already in dest, its value in dest will still be updated to the one from
// src.
func copyAttrs(src, dest map[string]interface{}) {
	for k, v := range src {
		dest[k] = v
	}
}

// newConfig creates a MAAS environment config from attributes.
func newConfig(values map[string]interface{}) (*maasEnvironConfig, error) {
	defaults := map[string]interface{}{
		"name":            "testenv",
		"type":            "maas",
		"admin-secret":    "ssshhhhhh",
		"authorized-keys": "I-am-not-a-real-key",
		"agent-version":   version.CurrentNumber().String(),
		// These are not needed by MAAS, but juju-core breaks without them. Needs
		// fixing there.
		"ca-cert":        testing.CACert,
		"ca-private-key": testing.CAKey,
	}
	cfg := make(map[string]interface{})
	copyAttrs(defaults, cfg)
	copyAttrs(values, cfg)
	env, err := environs.NewFromAttrs(cfg)
	if err != nil {
		return nil, err
	}
	return env.(*maasEnviron).ecfg(), nil
}

func (ConfigSuite) TestParsesMAASSettings(c *C) {
	server := "http://maas.example.com/maas/"
	oauth := "consumer-key:resource-token:resource-secret"
	ecfg, err := newConfig(map[string]interface{}{
		"maas-server": server,
		"maas-oauth":  oauth,
	})
	c.Assert(err, IsNil)
	c.Check(ecfg.MAASServer(), Equals, server)
	c.Check(ecfg.MAASOAuth(), DeepEquals, oauth)
}

func (ConfigSuite) TestChecksWellFormedMaasServer(c *C) {
	_, err := newConfig(map[string]interface{}{
		"maas-server": "This should have been a URL.",
		"maas-oauth":  "consumer-key:resource-token:resource-secret",
	})
	c.Assert(err, NotNil)
	c.Check(err, ErrorMatches, ".*malformed maas-server.*")
}

func (ConfigSuite) TestChecksWellFormedMaasOAuth(c *C) {
	_, err := newConfig(map[string]interface{}{
		"maas-server": "http://maas.example.com/maas/",
		"maas-oauth":  "This should have been a 3-part token.",
	})
	c.Assert(err, NotNil)
	c.Check(err, ErrorMatches, ".*malformed maas-oauth.*")
}

func (ConfigSuite) TestValidateUpcallsEnvironsConfigValidate(c *C) {
	// The base Validate() function will not allow an environment to
	// change its name.  Trigger that error so as to prove that the
	// environment provider's Validate() calls the base Validate().
	baseAttrs := map[string]interface{}{
		"maas-server": "http://maas.example.com/maas/",
		"maas-oauth": "consumer-key:resource-token:resource-secret",
	}
	oldCfg, err := newConfig(baseAttrs)
	c.Assert(err, IsNil)
	newCfg, err := newConfig(baseAttrs)
	c.Assert(err, IsNil)
	newName := oldCfg.Name() + "-but-different"
	newCfg.Apply(map[string]interface{}{"name": newName})
	_, err = maasEnvironProvider{}.Validate(newCfg.Config, oldCfg.Config)
	c.Assert(err, NotNil)
	c.Check(err, ErrorMatches, ".*cannot change name.*")
}
<|endoftext|>


<commit_before>package restful

// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

import (
	"bytes"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"

	"github.com/emicklei/go-restful/log"
)

// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
// The requests are further dispatched to routes of WebServices using a RouteSelector
type Container struct {
	webServices            []*WebService
	ServeMux               *http.ServeMux
	isRegisteredOnRoot     bool
	containerFilters       []FilterFunction
	doNotRecover           bool // default is false
	recoverHandleFunc      RecoverHandleFunction
	serviceErrorHandleFunc ServiceErrorHandleFunction
	router                 RouteSelector // default is a RouterJSR311, CurlyRouter is the faster alternative
	contentEncodingEnabled bool          // default is false
}

// NewContainer creates a new Container using a new ServeMux and default router (RouterJSR311)
func NewContainer() *Container {
	return &Container{
		webServices:            []*WebService{},
		ServeMux:               http.NewServeMux(),
		isRegisteredOnRoot:     false,
		containerFilters:       []FilterFunction{},
		doNotRecover:           false,
		recoverHandleFunc:      logStackOnRecover,
		serviceErrorHandleFunc: writeServiceError,
		router:                 RouterJSR311{},
		contentEncodingEnabled: false}
}

// RecoverHandleFunction declares functions that can be used to handle a panic situation.
// The first argument is what recover() returns. The second must be used to communicate an error response.
type RecoverHandleFunction func(interface{}, http.ResponseWriter)

// RecoverHandler changes the default function (logStackOnRecover) to be called
// when a panic is detected. DoNotRecover must be have its default value (=false).
func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
	c.recoverHandleFunc = handler
}

// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
// The first argument is the service error, the second is the request that resulted in the error and
// the third must be used to communicate an error response.
type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)

// ServiceErrorHandler changes the default function (writeServiceError) to be called
// when a ServiceError is detected.
func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
	c.serviceErrorHandleFunc = handler
}

// DoNotRecover controls whether panics will be caught to return HTTP 500.
// If set to true, Route functions are responsible for handling any error situation.
// Default value is false = recover from panics. This has performance implications.
func (c *Container) DoNotRecover(doNot bool) {
	c.doNotRecover = doNot
}

// Router changes the default Router (currently RouterJSR311)
func (c *Container) Router(aRouter RouteSelector) {
	c.router = aRouter
}

// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
func (c *Container) EnableContentEncoding(enabled bool) {
	c.contentEncodingEnabled = enabled
}

// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
func (c *Container) Add(service *WebService) *Container {
	// If registered on root then no additional specific mapping is needed
	if !c.isRegisteredOnRoot {
		pattern := c.fixedPrefixPath(service.RootPath())
		// check if root path registration is needed
		if "/" == pattern || "" == pattern {
			c.ServeMux.HandleFunc("/", c.dispatch)
			c.isRegisteredOnRoot = true
		} else {
			// detect if registration already exists
			alreadyMapped := false
			for _, each := range c.webServices {
				if each.RootPath() == service.RootPath() {
					alreadyMapped = true
					break
				}
			}
			if !alreadyMapped {
				c.ServeMux.HandleFunc(pattern, c.dispatch)
				if !strings.HasSuffix(pattern, "/") {
					c.ServeMux.HandleFunc(pattern+"/", c.dispatch)
				}
			}
		}
	}
	// cannot have duplicate root paths
	for _, each := range c.webServices {
		if each.RootPath() == service.RootPath() {
			log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
			os.Exit(1)
		}
	}
	// if rootPath was not set then lazy initialize it
	if len(service.rootPath) == 0 {
		service.Path("/")
	}
	c.webServices = append(c.webServices, service)
	return c
}

// logStackOnRecover is the default RecoverHandleFunction and is called
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
// Default implementation logs the stacktrace and writes the stacktrace on the response.
// This may be a security issue as it exposes sourcecode information.
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason))
	for i := 2; ; i += 1 {
		_, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		buffer.WriteString(fmt.Sprintf("    %s:%d\r\n", file, line))
	}
	log.Print(buffer.String())
	httpWriter.WriteHeader(http.StatusInternalServerError)
	httpWriter.Write(buffer.Bytes())
}

// writeServiceError is the default ServiceErrorHandleFunction and is called
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
	resp.WriteErrorString(err.Code, err.Message)
}

// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
	// Instal panic recovery unless told otherwise
	if !c.doNotRecover { // catch all for 500 response
		defer func() {
			if r := recover(); r != nil {
				c.recoverHandleFunc(r, httpWriter)
				return
			}
		}()
	}
	// Install closing the request body (if any)
	defer func() {
		if nil != httpRequest.Body {
			httpRequest.Body.Close()
		}
	}()

	// Detect if compression is needed
	// assume without compression, test for override
	writer := httpWriter
	if c.contentEncodingEnabled {
		doCompress, encoding := wantsCompressedResponse(httpRequest)
		if doCompress {
			var err error
			writer, err = NewCompressingResponseWriter(httpWriter, encoding)
			if err != nil {
				log.Print("[restful] unable to install compressor: ", err)
				httpWriter.WriteHeader(http.StatusInternalServerError)
				return
			}
			defer func() {
				writer.(*CompressingResponseWriter).Close()
			}()
		}
	}
	// Find best match Route ; err is non nil if no match was found
	webService, route, err := c.router.SelectRoute(
		c.webServices,
		httpRequest)
	if err != nil {
		// a non-200 response has already been written
		// run container filters anyway ; they should not touch the response...
		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			switch err.(type) {
			case ServiceError:
				ser := err.(ServiceError)
				c.serviceErrorHandleFunc(ser, req, resp)
			}
			// TODO
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
		return
	}
	wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest)
	// pass through filters (if any)
	if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
		// compose filter chain
		allFilters := []FilterFunction{}
		allFilters = append(allFilters, c.containerFilters...)
		allFilters = append(allFilters, webService.filters...)
		allFilters = append(allFilters, route.Filters...)
		chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
			// handle request by route after passing all filters
			route.Function(wrappedRequest, wrappedResponse)
		}}
		chain.ProcessFilter(wrappedRequest, wrappedResponse)
	} else {
		// no filters, handle request by route
		route.Function(wrappedRequest, wrappedResponse)
	}
}

// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
func (c Container) fixedPrefixPath(pathspec string) string {
	varBegin := strings.Index(pathspec, "{")
	if -1 == varBegin {
		return pathspec
	}
	return pathspec[:varBegin]
}

// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
	c.ServeMux.ServeHTTP(httpwriter, httpRequest)
}

// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c Container) Handle(pattern string, handler http.Handler) {
	c.ServeMux.Handle(pattern, handler)
}

// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// If a handler already exists for pattern, HandleWithFilter panics.
func (c Container) HandleWithFilter(pattern string, handler http.Handler) {
	f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
		if len(c.containerFilters) == 0 {
			handler.ServeHTTP(httpResponse, httpRequest)
			return
		}

		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			handler.ServeHTTP(httpResponse, httpRequest)
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
	}

	c.Handle(pattern, http.HandlerFunc(f))
}

// Filter appends a container FilterFunction. These are called before dispatching
// a http.Request to a WebService from the container
func (c *Container) Filter(filter FilterFunction) {
	c.containerFilters = append(c.containerFilters, filter)
}

// RegisteredWebServices returns the collections of added WebServices
func (c Container) RegisteredWebServices() []*WebService {
	return c.webServices
}

// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
func (c Container) computeAllowedMethods(req *Request) []string {
	// Go through all RegisteredWebServices() and all its Routes to collect the options
	methods := []string{}
	requestPath := req.Request.URL.Path
	for _, ws := range c.RegisteredWebServices() {
		matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
		if matches != nil {
			finalMatch := matches[len(matches)-1]
			for _, rt := range ws.Routes() {
				matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
				if matches != nil {
					lastMatch := matches[len(matches)-1]
					if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
						methods = append(methods, rt.Method)
					}
				}
			}
		}
	}
	// methods = append(methods, "OPTIONS")  not sure about this
	return methods
}

// newBasicRequestResponse creates a pair of Request,Response from its http versions.
// It is basic because no parameter or (produces) content-type information is given.
func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
	resp := NewResponse(httpWriter)
	resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
	return NewRequest(httpRequest), resp
}
<commit_msg>Fix panic response unsent if CompressingResponseWriter is used<commit_after>package restful

// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

import (
	"bytes"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"

	"github.com/emicklei/go-restful/log"
)

// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
// The requests are further dispatched to routes of WebServices using a RouteSelector
type Container struct {
	webServices            []*WebService
	ServeMux               *http.ServeMux
	isRegisteredOnRoot     bool
	containerFilters       []FilterFunction
	doNotRecover           bool // default is false
	recoverHandleFunc      RecoverHandleFunction
	serviceErrorHandleFunc ServiceErrorHandleFunction
	router                 RouteSelector // default is a RouterJSR311, CurlyRouter is the faster alternative
	contentEncodingEnabled bool          // default is false
}

// NewContainer creates a new Container using a new ServeMux and default router (RouterJSR311)
func NewContainer() *Container {
	return &Container{
		webServices:            []*WebService{},
		ServeMux:               http.NewServeMux(),
		isRegisteredOnRoot:     false,
		containerFilters:       []FilterFunction{},
		doNotRecover:           false,
		recoverHandleFunc:      logStackOnRecover,
		serviceErrorHandleFunc: writeServiceError,
		router:                 RouterJSR311{},
		contentEncodingEnabled: false}
}

// RecoverHandleFunction declares functions that can be used to handle a panic situation.
// The first argument is what recover() returns. The second must be used to communicate an error response.
type RecoverHandleFunction func(interface{}, http.ResponseWriter)

// RecoverHandler changes the default function (logStackOnRecover) to be called
// when a panic is detected. DoNotRecover must be have its default value (=false).
func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
	c.recoverHandleFunc = handler
}

// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
// The first argument is the service error, the second is the request that resulted in the error and
// the third must be used to communicate an error response.
type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)

// ServiceErrorHandler changes the default function (writeServiceError) to be called
// when a ServiceError is detected.
func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
	c.serviceErrorHandleFunc = handler
}

// DoNotRecover controls whether panics will be caught to return HTTP 500.
// If set to true, Route functions are responsible for handling any error situation.
// Default value is false = recover from panics. This has performance implications.
func (c *Container) DoNotRecover(doNot bool) {
	c.doNotRecover = doNot
}

// Router changes the default Router (currently RouterJSR311)
func (c *Container) Router(aRouter RouteSelector) {
	c.router = aRouter
}

// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
func (c *Container) EnableContentEncoding(enabled bool) {
	c.contentEncodingEnabled = enabled
}

// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
func (c *Container) Add(service *WebService) *Container {
	// If registered on root then no additional specific mapping is needed
	if !c.isRegisteredOnRoot {
		pattern := c.fixedPrefixPath(service.RootPath())
		// check if root path registration is needed
		if "/" == pattern || "" == pattern {
			c.ServeMux.HandleFunc("/", c.dispatch)
			c.isRegisteredOnRoot = true
		} else {
			// detect if registration already exists
			alreadyMapped := false
			for _, each := range c.webServices {
				if each.RootPath() == service.RootPath() {
					alreadyMapped = true
					break
				}
			}
			if !alreadyMapped {
				c.ServeMux.HandleFunc(pattern, c.dispatch)
				if !strings.HasSuffix(pattern, "/") {
					c.ServeMux.HandleFunc(pattern+"/", c.dispatch)
				}
			}
		}
	}
	// cannot have duplicate root paths
	for _, each := range c.webServices {
		if each.RootPath() == service.RootPath() {
			log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
			os.Exit(1)
		}
	}
	// if rootPath was not set then lazy initialize it
	if len(service.rootPath) == 0 {
		service.Path("/")
	}
	c.webServices = append(c.webServices, service)
	return c
}

// logStackOnRecover is the default RecoverHandleFunction and is called
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
// Default implementation logs the stacktrace and writes the stacktrace on the response.
// This may be a security issue as it exposes sourcecode information.
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason))
	for i := 2; ; i += 1 {
		_, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		buffer.WriteString(fmt.Sprintf("    %s:%d\r\n", file, line))
	}
	log.Print(buffer.String())
	httpWriter.WriteHeader(http.StatusInternalServerError)
	httpWriter.Write(buffer.Bytes())
}

// writeServiceError is the default ServiceErrorHandleFunction and is called
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
	resp.WriteErrorString(err.Code, err.Message)
}

// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
	writer := httpWriter

	// CompressingResponseWriter should be closed after all operations are done
	defer func() {
		if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
			compressWriter.Close()
		}
	}()

	// Instal panic recovery unless told otherwise
	if !c.doNotRecover { // catch all for 500 response
		defer func() {
			if r := recover(); r != nil {
				c.recoverHandleFunc(r, writer)
				return
			}
		}()
	}
	// Install closing the request body (if any)
	defer func() {
		if nil != httpRequest.Body {
			httpRequest.Body.Close()
		}
	}()

	// Detect if compression is needed
	// assume without compression, test for override
	if c.contentEncodingEnabled {
		doCompress, encoding := wantsCompressedResponse(httpRequest)
		if doCompress {
			var err error
			writer, err = NewCompressingResponseWriter(httpWriter, encoding)
			if err != nil {
				log.Print("[restful] unable to install compressor: ", err)
				httpWriter.WriteHeader(http.StatusInternalServerError)
				return
			}
		}
	}
	// Find best match Route ; err is non nil if no match was found
	webService, route, err := c.router.SelectRoute(
		c.webServices,
		httpRequest)
	if err != nil {
		// a non-200 response has already been written
		// run container filters anyway ; they should not touch the response...
		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			switch err.(type) {
			case ServiceError:
				ser := err.(ServiceError)
				c.serviceErrorHandleFunc(ser, req, resp)
			}
			// TODO
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
		return
	}
	wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest)
	// pass through filters (if any)
	if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
		// compose filter chain
		allFilters := []FilterFunction{}
		allFilters = append(allFilters, c.containerFilters...)
		allFilters = append(allFilters, webService.filters...)
		allFilters = append(allFilters, route.Filters...)
		chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
			// handle request by route after passing all filters
			route.Function(wrappedRequest, wrappedResponse)
		}}
		chain.ProcessFilter(wrappedRequest, wrappedResponse)
	} else {
		// no filters, handle request by route
		route.Function(wrappedRequest, wrappedResponse)
	}
}

// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
func (c Container) fixedPrefixPath(pathspec string) string {
	varBegin := strings.Index(pathspec, "{")
	if -1 == varBegin {
		return pathspec
	}
	return pathspec[:varBegin]
}

// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
	c.ServeMux.ServeHTTP(httpwriter, httpRequest)
}

// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c Container) Handle(pattern string, handler http.Handler) {
	c.ServeMux.Handle(pattern, handler)
}

// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// If a handler already exists for pattern, HandleWithFilter panics.
func (c Container) HandleWithFilter(pattern string, handler http.Handler) {
	f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
		if len(c.containerFilters) == 0 {
			handler.ServeHTTP(httpResponse, httpRequest)
			return
		}

		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			handler.ServeHTTP(httpResponse, httpRequest)
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
	}

	c.Handle(pattern, http.HandlerFunc(f))
}

// Filter appends a container FilterFunction. These are called before dispatching
// a http.Request to a WebService from the container
func (c *Container) Filter(filter FilterFunction) {
	c.containerFilters = append(c.containerFilters, filter)
}

// RegisteredWebServices returns the collections of added WebServices
func (c Container) RegisteredWebServices() []*WebService {
	return c.webServices
}

// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
func (c Container) computeAllowedMethods(req *Request) []string {
	// Go through all RegisteredWebServices() and all its Routes to collect the options
	methods := []string{}
	requestPath := req.Request.URL.Path
	for _, ws := range c.RegisteredWebServices() {
		matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
		if matches != nil {
			finalMatch := matches[len(matches)-1]
			for _, rt := range ws.Routes() {
				matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
				if matches != nil {
					lastMatch := matches[len(matches)-1]
					if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
						methods = append(methods, rt.Method)
					}
				}
			}
		}
	}
	// methods = append(methods, "OPTIONS")  not sure about this
	return methods
}

// newBasicRequestResponse creates a pair of Request,Response from its http versions.
// It is basic because no parameter or (produces) content-type information is given.
func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
	resp := NewResponse(httpWriter)
	resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
	return NewRequest(httpRequest), resp
}
<|endoftext|>


<commit_before>package core

import (
	"github.com/akutz/gofig"
)

func init() {
	initDrivers()
	gofig.Register(globalRegistration())
	gofig.Register(driverRegistration())
}

func globalRegistration() *gofig.Registration {
	r := gofig.NewRegistration("Global")
	r.Yaml(`
rexray:
    host: tcp://:7979
    logLevel: warn
`)
	r.Key(gofig.String, "h", "tcp://:7979",
		"The REX-Ray host", "rexray.host")
	r.Key(gofig.String, "l", "warn",
		"The log level (error, warn, info, debug)", "rexray.logLevel")
	return r
}

func driverRegistration() *gofig.Registration {
	r := gofig.NewRegistration("Driver")
	r.Yaml(`
rexray:
    osDrivers:
    - linux
    storageDrivers:
    - libstorage
    volumeDrivers:
    - docker
`)
	r.Key(gofig.String, "", "linux",
		"The OS drivers to consider", "rexray.osDrivers")
	r.Key(gofig.String, "", "",
		"The storage drivers to consider", "rexray.storageDrivers")
	r.Key(gofig.String, "", "docker",
		"The volume drivers to consider", "rexray.volumeDrivers")
	return r
}
<commit_msg>Fix for not setting Gofig global/user dirs<commit_after>package core

import (
	"fmt"

	"github.com/akutz/gofig"

	"github.com/emccode/rexray/util"
)

func init() {
	initDrivers()

	gofig.SetGlobalConfigPath(util.EtcDirPath())
	gofig.SetUserConfigPath(fmt.Sprintf("%s/.rexray", util.HomeDir()))

	gofig.Register(globalRegistration())
	gofig.Register(driverRegistration())
}

func globalRegistration() *gofig.Registration {
	r := gofig.NewRegistration("Global")
	r.Yaml(`
rexray:
    host: tcp://:7979
    logLevel: warn
`)
	r.Key(gofig.String, "h", "tcp://:7979",
		"The REX-Ray host", "rexray.host")
	r.Key(gofig.String, "l", "warn",
		"The log level (error, warn, info, debug)", "rexray.logLevel")
	return r
}

func driverRegistration() *gofig.Registration {
	r := gofig.NewRegistration("Driver")
	r.Yaml(`
rexray:
    osDrivers:
    - linux
    storageDrivers:
    - libstorage
    volumeDrivers:
    - docker
`)
	r.Key(gofig.String, "", "linux",
		"The OS drivers to consider", "rexray.osDrivers")
	r.Key(gofig.String, "", "",
		"The storage drivers to consider", "rexray.storageDrivers")
	r.Key(gofig.String, "", "docker",
		"The volume drivers to consider", "rexray.volumeDrivers")
	return r
}
<|endoftext|>


<commit_before>package core

import (
	"errors"
	"fmt"
	"net/http"
	"path"
	"sync"
	"time"

	"gx/ipfs/QmSY3nkMNLzh9GdbFKK5tT7YMfLpf52iUZ8ZRkr29MJaa5/go-libp2p-kad-dht"
	libp2p "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto"
	ma "gx/ipfs/QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL/go-multiaddr"
	cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid"
	peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer"
	routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing"

	"github.com/OpenBazaar/multiwallet"
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/net"
	rep "github.com/OpenBazaar/openbazaar-go/net/repointer"
	ret "github.com/OpenBazaar/openbazaar-go/net/retriever"
	"github.com/OpenBazaar/openbazaar-go/pb"
	"github.com/OpenBazaar/openbazaar-go/repo"
	sto "github.com/OpenBazaar/openbazaar-go/storage"
	"github.com/btcsuite/btcutil/hdkeychain"
	"github.com/ipfs/go-ipfs/core"
	logging "github.com/op/go-logging"
	"golang.org/x/net/context"
	"golang.org/x/net/proxy"
)

const (
	// VERSION - current version
	VERSION = "0.14.3"
	// USERAGENT - user-agent header string
	USERAGENT = "/openbazaar-go:" + VERSION + "/"
)

var log = logging.MustGetLogger("core")

// Node - ob node
var Node *OpenBazaarNode

var inflightPublishRequests int

// OpenBazaarNode - represent ob node which encapsulates ipfsnode, wallet etc
type OpenBazaarNode struct {
	// IPFS node object
	IpfsNode *core.IpfsNode

	// An implementation of the custom DHT used by OpenBazaar
	DHT *dht.IpfsDHT

	// The roothash of the node directory inside the openbazaar repo.
	// This directory hash is published on IPNS at our peer ID making
	// the directory publicly viewable on the network.
	RootHash string

	// The path to the openbazaar repo in the file system
	RepoPath string

	// The OpenBazaar network service for direct communication between peers
	Service net.NetworkService

	// Database for storing node specific data
	Datastore repo.Datastore

	// Websocket channel used for pushing data to the UI
	Broadcast chan repo.Notifier

	// A map of cryptocurrency wallets
	Multiwallet multiwallet.MultiWallet

	// Storage for our outgoing messages
	MessageStorage sto.OfflineMessagingStorage

	// A service that periodically checks the dht for outstanding messages
	MessageRetriever *ret.MessageRetriever

	// OfflineMessageFailoverTimeout is the duration until the protocol
	// will stop looking for the peer to send a direct message and failover to
	// sending an offline message
	OfflineMessageFailoverTimeout time.Duration

	// A service that periodically republishes active pointers
	PointerRepublisher *rep.PointerRepublisher

	// Optional nodes to push user data to
	PushNodes []peer.ID

	// The user-agent for this node
	UserAgent string

	// A dialer for Tor if available
	TorDialer proxy.Dialer

	// Manage blocked peers
	BanManager *net.BanManager

	// Allow other nodes to push data to this node for storage
	AcceptStoreRequests bool

	// RecordAgingNotifier is a worker that walks the cases datastore to
	// notify the user as disputes age past certain thresholds
	RecordAgingNotifier *recordAgingNotifier

	// Generic pubsub interface
	Pubsub ipfs.Pubsub

	// The master private key derived from the mnemonic
	MasterPrivateKey *hdkeychain.ExtendedKey

	// The number of DHT records to collect before returning. The larger the number
	// the slower the query but the less likely we will get an old record.
	IPNSQuorumSize uint

	TestnetEnable        bool
	RegressionTestEnable bool

	PublishLock sync.Mutex
	seedLock    sync.Mutex

	InitalPublishComplete bool

	// InboundMsgScanner is a worker that scans the messages
	// table and tries to retry a failed order message
	InboundMsgScanner *inboundMessageScanner
}

// TestNetworkEnabled indicates whether the node is operating with test parameters
func (n *OpenBazaarNode) TestNetworkEnabled() bool { return n.TestnetEnable }

// RegressionNetworkEnabled indicates whether the node is operating with regression parameters
func (n *OpenBazaarNode) RegressionNetworkEnabled() bool { return n.RegressionTestEnable }

// SeedNode - publish to IPNS
func (n *OpenBazaarNode) SeedNode() error {
	n.seedLock.Lock()
	err := ipfs.UnPinDir(n.IpfsNode, n.RootHash)
	if err != nil {
		log.Errorf("unpinning old root: %s", err.Error())
	}
	var aerr error
	var rootHash string
	// There's an IPFS bug on Windows that might be related to the Windows indexer that could cause this to fail
	// If we fail the first time, let's retry a couple times before giving up.
	for i := 0; i < 3; i++ {
		rootHash, aerr = ipfs.AddDirectory(n.IpfsNode, path.Join(n.RepoPath, "root"))
		if aerr == nil {
			break
		}
		time.Sleep(time.Millisecond * 500)
	}
	if aerr != nil {
		n.seedLock.Unlock()
		return aerr
	}
	n.RootHash = rootHash

	n.seedLock.Unlock()
	n.InitalPublishComplete = true
	go n.publish(rootHash)
	return nil
}

func (n *OpenBazaarNode) publish(hash string) {
	// Multiple publishes may have been queued
	// We only need to publish the most recent
	n.PublishLock.Lock()
	defer n.PublishLock.Unlock()
	if hash != n.RootHash {
		return
	}

	if inflightPublishRequests == 0 {
		n.Broadcast <- repo.StatusNotification{Status: "publishing"}
	}

	err := n.sendToPushNodes(hash)
	if err != nil {
		log.Error(err)
		return
	}

	go func() {
		// Update search endpoint with published hash
		peerId, _ := n.GetNodeID()
		endpoint := fmt.Sprintf("https://search.ob1.io/update/%s/%s", peerId.PeerID, hash)
		log.Infof("Publishing new rootHash to: %s\n", endpoint)

		var client *http.Client
		if n.TorDialer != nil {
			tbTransport := &http.Transport{Dial: n.TorDialer.Dial}
			client = &http.Client{Transport: tbTransport, Timeout: time.Second * 30}
		} else {
			client = &http.Client{Timeout: time.Second * 30}
		}

		resp, err := client.Get(endpoint)
		if err != nil {
			log.Errorf("Search update did not succeed. %v\n", err)
		}
		log.Debugf("%s response: %v", endpoint, resp)
	}()

	inflightPublishRequests++
	err = ipfs.Publish(n.IpfsNode, hash)

	inflightPublishRequests--
	if inflightPublishRequests == 0 {
		if err != nil {
			log.Error(err)
			n.Broadcast <- repo.StatusNotification{Status: "error publishing"}
		} else {
			n.Broadcast <- repo.StatusNotification{Status: "publish complete"}
		}
	}
}

func (n *OpenBazaarNode) sendToPushNodes(hash string) error {
	id, err := cid.Decode(hash)
	if err != nil {
		return err
	}

	var graph []cid.Cid
	if len(n.PushNodes) > 0 {
		graph, err = ipfs.FetchGraph(n.IpfsNode, &id)
		if err != nil {
			return err
		}
		pointers, err := n.Datastore.Pointers().GetByPurpose(ipfs.MESSAGE)
		if err != nil {
			return err
		}
		// Check if we're seeding any outgoing messages and add their CIDs to the graph
		for _, p := range pointers {
			if len(p.Value.Addrs) > 0 {
				s, err := p.Value.Addrs[0].ValueForProtocol(ma.P_IPFS)
				if err != nil {
					continue
				}
				c, err := cid.Decode(s)
				if err != nil {
					continue
				}
				graph = append(graph, c)
			}
		}
	}
	for _, p := range n.PushNodes {
		go n.retryableSeedStoreToPeer(p, hash, graph)
	}

	return nil
}

func (n *OpenBazaarNode) retryableSeedStoreToPeer(pid peer.ID, graphHash string, graph []cid.Cid) {
	var retryTimeout = 2 * time.Second
	for {
		if graphHash != n.RootHash {
			log.Errorf("root hash has changed, aborting push to %s", pid.Pretty())
			return
		}
		err := n.SendStore(pid.Pretty(), graph)
		if err != nil {
			if retryTimeout > 8*time.Second {
				log.Errorf("error pushing to peer %s: %s", pid.Pretty(), err.Error())
				return
			}
			log.Errorf("error pushing to peer %s...backing off: %s", pid.Pretty(), err.Error())
			time.Sleep(retryTimeout)
			retryTimeout *= 2
			continue
		}
		return
	}
}

// SetUpRepublisher - periodic publishing to IPNS
func (n *OpenBazaarNode) SetUpRepublisher(interval time.Duration) {
	if interval == 0 {
		return
	}
	ticker := time.NewTicker(interval)
	go func() {
		for range ticker.C {
			err := n.UpdateFollow()
			if err != nil {
				log.Error(err)
			}
			err = n.SeedNode()
			if err != nil {
				log.Error(err)
			}
		}
	}()
}

/*EncryptMessage This is a placeholder until the libsignal is operational.
  For now we will just encrypt outgoing offline messages with the long lived identity key.
  Optionally you may provide a public key, to avoid doing an IPFS lookup */
func (n *OpenBazaarNode) EncryptMessage(peerID peer.ID, peerKey *libp2p.PubKey, message []byte) (ct []byte, rerr error) {
	ctx, cancel := context.WithTimeout(context.Background(), n.OfflineMessageFailoverTimeout)
	defer cancel()
	if peerKey == nil {
		var (
			pubKey libp2p.PubKey
			store  = n.IpfsNode.Repo.Datastore()
		)
		keyval, err := ipfs.GetCachedPubkey(store, peerID.Pretty())
		if err != nil {
			pubKey, err = routing.GetPublicKey(n.IpfsNode.Routing, ctx, peerID)
			if err != nil {
				log.Errorf("Failed to find public key for %s", peerID.Pretty())
				return nil, err
			}
		} else {
			pubKey, err = libp2p.UnmarshalPublicKey(keyval)
			if err != nil {
				log.Errorf("Failed to find public key for %s", peerID.Pretty())
				return nil, err
			}
		}
		peerKey = &pubKey
	}
	if peerID.MatchesPublicKey(*peerKey) {
		ciphertext, err := net.Encrypt(*peerKey, message)
		if err != nil {
			return nil, err
		}
		return ciphertext, nil
	}
	log.Errorf("peer public key and id do not match for peer: %s", peerID.Pretty())
	return nil, errors.New("peer public key and id do not match")
}

// IPFSIdentityString - IPFS identifier
func (n *OpenBazaarNode) IPFSIdentityString() string {
	return n.IpfsNode.Identity.Pretty()
}

// GetNodeID returns the protobuf representing the node's identity and crypto
// keys with the peer ID
func (n *OpenBazaarNode) GetNodeID() (*pb.ID, error) {
	var id = new(pb.ID)
	id.PeerID = n.IpfsNode.Identity.Pretty()

	if p, err := n.GetProfile(); err == nil {
		id.Handle = p.Handle
	}

	p := new(pb.ID_Pubkeys)
	pubkey, err := n.IpfsNode.PrivateKey.GetPublic().Bytes()
	if err != nil {
		return nil, fmt.Errorf("ipfs pubkey bytes: %s", err.Error())
	}
	p.Identity = pubkey
	coinPubkey, err := n.MasterPrivateKey.ECPubKey()
	if err != nil {
		return nil, fmt.Errorf("master pubkey: %s", err.Error())
	}
	p.Bitcoin = coinPubkey.SerializeCompressed()
	id.Pubkeys = p

	coinPrivKey, err := n.MasterPrivateKey.ECPrivKey()
	if err != nil {
		return nil, fmt.Errorf("master privkey: %s", err.Error())
	}
	coinSig, err := coinPrivKey.Sign([]byte(id.PeerID))
	if err != nil {
		return nil, fmt.Errorf("sign id: %s", err.Error())
	}
	id.BitcoinSig = coinSig.Serialize()

	return id, nil
}

// Sign returns a signature for the payload signed by the IPFS private key
func (n *OpenBazaarNode) Sign(payload []byte) ([]byte, error) {
	return n.IpfsNode.PrivateKey.Sign(payload)
}
<commit_msg>Bump version<commit_after>package core

import (
	"errors"
	"fmt"
	"net/http"
	"path"
	"sync"
	"time"

	"gx/ipfs/QmSY3nkMNLzh9GdbFKK5tT7YMfLpf52iUZ8ZRkr29MJaa5/go-libp2p-kad-dht"
	libp2p "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto"
	ma "gx/ipfs/QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL/go-multiaddr"
	cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid"
	peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer"
	routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing"

	"github.com/OpenBazaar/multiwallet"
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/net"
	rep "github.com/OpenBazaar/openbazaar-go/net/repointer"
	ret "github.com/OpenBazaar/openbazaar-go/net/retriever"
	"github.com/OpenBazaar/openbazaar-go/pb"
	"github.com/OpenBazaar/openbazaar-go/repo"
	sto "github.com/OpenBazaar/openbazaar-go/storage"
	"github.com/btcsuite/btcutil/hdkeychain"
	"github.com/ipfs/go-ipfs/core"
	logging "github.com/op/go-logging"
	"golang.org/x/net/context"
	"golang.org/x/net/proxy"
)

const (
	// VERSION - current version
	VERSION = "0.14.r-rc1"
	// USERAGENT - user-agent header string
	USERAGENT = "/openbazaar-go:" + VERSION + "/"
)

var log = logging.MustGetLogger("core")

// Node - ob node
var Node *OpenBazaarNode

var inflightPublishRequests int

// OpenBazaarNode - represent ob node which encapsulates ipfsnode, wallet etc
type OpenBazaarNode struct {
	// IPFS node object
	IpfsNode *core.IpfsNode

	// An implementation of the custom DHT used by OpenBazaar
	DHT *dht.IpfsDHT

	// The roothash of the node directory inside the openbazaar repo.
	// This directory hash is published on IPNS at our peer ID making
	// the directory publicly viewable on the network.
	RootHash string

	// The path to the openbazaar repo in the file system
	RepoPath string

	// The OpenBazaar network service for direct communication between peers
	Service net.NetworkService

	// Database for storing node specific data
	Datastore repo.Datastore

	// Websocket channel used for pushing data to the UI
	Broadcast chan repo.Notifier

	// A map of cryptocurrency wallets
	Multiwallet multiwallet.MultiWallet

	// Storage for our outgoing messages
	MessageStorage sto.OfflineMessagingStorage

	// A service that periodically checks the dht for outstanding messages
	MessageRetriever *ret.MessageRetriever

	// OfflineMessageFailoverTimeout is the duration until the protocol
	// will stop looking for the peer to send a direct message and failover to
	// sending an offline message
	OfflineMessageFailoverTimeout time.Duration

	// A service that periodically republishes active pointers
	PointerRepublisher *rep.PointerRepublisher

	// Optional nodes to push user data to
	PushNodes []peer.ID

	// The user-agent for this node
	UserAgent string

	// A dialer for Tor if available
	TorDialer proxy.Dialer

	// Manage blocked peers
	BanManager *net.BanManager

	// Allow other nodes to push data to this node for storage
	AcceptStoreRequests bool

	// RecordAgingNotifier is a worker that walks the cases datastore to
	// notify the user as disputes age past certain thresholds
	RecordAgingNotifier *recordAgingNotifier

	// Generic pubsub interface
	Pubsub ipfs.Pubsub

	// The master private key derived from the mnemonic
	MasterPrivateKey *hdkeychain.ExtendedKey

	// The number of DHT records to collect before returning. The larger the number
	// the slower the query but the less likely we will get an old record.
	IPNSQuorumSize uint

	TestnetEnable        bool
	RegressionTestEnable bool

	PublishLock sync.Mutex
	seedLock    sync.Mutex

	InitalPublishComplete bool

	// InboundMsgScanner is a worker that scans the messages
	// table and tries to retry a failed order message
	InboundMsgScanner *inboundMessageScanner
}

// TestNetworkEnabled indicates whether the node is operating with test parameters
func (n *OpenBazaarNode) TestNetworkEnabled() bool { return n.TestnetEnable }

// RegressionNetworkEnabled indicates whether the node is operating with regression parameters
func (n *OpenBazaarNode) RegressionNetworkEnabled() bool { return n.RegressionTestEnable }

// SeedNode - publish to IPNS
func (n *OpenBazaarNode) SeedNode() error {
	n.seedLock.Lock()
	err := ipfs.UnPinDir(n.IpfsNode, n.RootHash)
	if err != nil {
		log.Errorf("unpinning old root: %s", err.Error())
	}
	var aerr error
	var rootHash string
	// There's an IPFS bug on Windows that might be related to the Windows indexer that could cause this to fail
	// If we fail the first time, let's retry a couple times before giving up.
	for i := 0; i < 3; i++ {
		rootHash, aerr = ipfs.AddDirectory(n.IpfsNode, path.Join(n.RepoPath, "root"))
		if aerr == nil {
			break
		}
		time.Sleep(time.Millisecond * 500)
	}
	if aerr != nil {
		n.seedLock.Unlock()
		return aerr
	}
	n.RootHash = rootHash

	n.seedLock.Unlock()
	n.InitalPublishComplete = true
	go n.publish(rootHash)
	return nil
}

func (n *OpenBazaarNode) publish(hash string) {
	// Multiple publishes may have been queued
	// We only need to publish the most recent
	n.PublishLock.Lock()
	defer n.PublishLock.Unlock()
	if hash != n.RootHash {
		return
	}

	if inflightPublishRequests == 0 {
		n.Broadcast <- repo.StatusNotification{Status: "publishing"}
	}

	err := n.sendToPushNodes(hash)
	if err != nil {
		log.Error(err)
		return
	}

	go func() {
		// Update search endpoint with published hash
		peerId, _ := n.GetNodeID()
		endpoint := fmt.Sprintf("https://search.ob1.io/update/%s/%s", peerId.PeerID, hash)
		log.Infof("Publishing new rootHash to: %s\n", endpoint)

		var client *http.Client
		if n.TorDialer != nil {
			tbTransport := &http.Transport{Dial: n.TorDialer.Dial}
			client = &http.Client{Transport: tbTransport, Timeout: time.Second * 30}
		} else {
			client = &http.Client{Timeout: time.Second * 30}
		}

		resp, err := client.Get(endpoint)
		if err != nil {
			log.Errorf("Search update did not succeed. %v\n", err)
		}
		log.Debugf("%s response: %v", endpoint, resp)
	}()

	inflightPublishRequests++
	err = ipfs.Publish(n.IpfsNode, hash)

	inflightPublishRequests--
	if inflightPublishRequests == 0 {
		if err != nil {
			log.Error(err)
			n.Broadcast <- repo.StatusNotification{Status: "error publishing"}
		} else {
			n.Broadcast <- repo.StatusNotification{Status: "publish complete"}
		}
	}
}

func (n *OpenBazaarNode) sendToPushNodes(hash string) error {
	id, err := cid.Decode(hash)
	if err != nil {
		return err
	}

	var graph []cid.Cid
	if len(n.PushNodes) > 0 {
		graph, err = ipfs.FetchGraph(n.IpfsNode, &id)
		if err != nil {
			return err
		}
		pointers, err := n.Datastore.Pointers().GetByPurpose(ipfs.MESSAGE)
		if err != nil {
			return err
		}
		// Check if we're seeding any outgoing messages and add their CIDs to the graph
		for _, p := range pointers {
			if len(p.Value.Addrs) > 0 {
				s, err := p.Value.Addrs[0].ValueForProtocol(ma.P_IPFS)
				if err != nil {
					continue
				}
				c, err := cid.Decode(s)
				if err != nil {
					continue
				}
				graph = append(graph, c)
			}
		}
	}
	for _, p := range n.PushNodes {
		go n.retryableSeedStoreToPeer(p, hash, graph)
	}

	return nil
}

func (n *OpenBazaarNode) retryableSeedStoreToPeer(pid peer.ID, graphHash string, graph []cid.Cid) {
	var retryTimeout = 2 * time.Second
	for {
		if graphHash != n.RootHash {
			log.Errorf("root hash has changed, aborting push to %s", pid.Pretty())
			return
		}
		err := n.SendStore(pid.Pretty(), graph)
		if err != nil {
			if retryTimeout > 8*time.Second {
				log.Errorf("error pushing to peer %s: %s", pid.Pretty(), err.Error())
				return
			}
			log.Errorf("error pushing to peer %s...backing off: %s", pid.Pretty(), err.Error())
			time.Sleep(retryTimeout)
			retryTimeout *= 2
			continue
		}
		return
	}
}

// SetUpRepublisher - periodic publishing to IPNS
func (n *OpenBazaarNode) SetUpRepublisher(interval time.Duration) {
	if interval == 0 {
		return
	}
	ticker := time.NewTicker(interval)
	go func() {
		for range ticker.C {
			err := n.UpdateFollow()
			if err != nil {
				log.Error(err)
			}
			err = n.SeedNode()
			if err != nil {
				log.Error(err)
			}
		}
	}()
}

/*EncryptMessage This is a placeholder until the libsignal is operational.
  For now we will just encrypt outgoing offline messages with the long lived identity key.
  Optionally you may provide a public key, to avoid doing an IPFS lookup */
func (n *OpenBazaarNode) EncryptMessage(peerID peer.ID, peerKey *libp2p.PubKey, message []byte) (ct []byte, rerr error) {
	ctx, cancel := context.WithTimeout(context.Background(), n.OfflineMessageFailoverTimeout)
	defer cancel()
	if peerKey == nil {
		var (
			pubKey libp2p.PubKey
			store  = n.IpfsNode.Repo.Datastore()
		)
		keyval, err := ipfs.GetCachedPubkey(store, peerID.Pretty())
		if err != nil {
			pubKey, err = routing.GetPublicKey(n.IpfsNode.Routing, ctx, peerID)
			if err != nil {
				log.Errorf("Failed to find public key for %s", peerID.Pretty())
				return nil, err
			}
		} else {
			pubKey, err = libp2p.UnmarshalPublicKey(keyval)
			if err != nil {
				log.Errorf("Failed to find public key for %s", peerID.Pretty())
				return nil, err
			}
		}
		peerKey = &pubKey
	}
	if peerID.MatchesPublicKey(*peerKey) {
		ciphertext, err := net.Encrypt(*peerKey, message)
		if err != nil {
			return nil, err
		}
		return ciphertext, nil
	}
	log.Errorf("peer public key and id do not match for peer: %s", peerID.Pretty())
	return nil, errors.New("peer public key and id do not match")
}

// IPFSIdentityString - IPFS identifier
func (n *OpenBazaarNode) IPFSIdentityString() string {
	return n.IpfsNode.Identity.Pretty()
}

// GetNodeID returns the protobuf representing the node's identity and crypto
// keys with the peer ID
func (n *OpenBazaarNode) GetNodeID() (*pb.ID, error) {
	var id = new(pb.ID)
	id.PeerID = n.IpfsNode.Identity.Pretty()

	if p, err := n.GetProfile(); err == nil {
		id.Handle = p.Handle
	}

	p := new(pb.ID_Pubkeys)
	pubkey, err := n.IpfsNode.PrivateKey.GetPublic().Bytes()
	if err != nil {
		return nil, fmt.Errorf("ipfs pubkey bytes: %s", err.Error())
	}
	p.Identity = pubkey
	coinPubkey, err := n.MasterPrivateKey.ECPubKey()
	if err != nil {
		return nil, fmt.Errorf("master pubkey: %s", err.Error())
	}
	p.Bitcoin = coinPubkey.SerializeCompressed()
	id.Pubkeys = p

	coinPrivKey, err := n.MasterPrivateKey.ECPrivKey()
	if err != nil {
		return nil, fmt.Errorf("master privkey: %s", err.Error())
	}
	coinSig, err := coinPrivKey.Sign([]byte(id.PeerID))
	if err != nil {
		return nil, fmt.Errorf("sign id: %s", err.Error())
	}
	id.BitcoinSig = coinSig.Serialize()

	return id, nil
}

// Sign returns a signature for the payload signed by the IPFS private key
func (n *OpenBazaarNode) Sign(payload []byte) ([]byte, error) {
	return n.IpfsNode.PrivateKey.Sign(payload)
}
<|endoftext|>


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

package nagger

import (
	"istio.io/bots/policybot/pkg/config"
)

const recordType = "nag"

// Nag expresses some matching conditions against a PR, along with a message to inject into a PR
// when it matches.
//
// The model is for a given PR:
//
//	if (PR title matches any of NatchTitle) || (PR body matches any of MatchBody) {
// 		if (PR files match any of MatchFiles) {
//			if (PR does not match any of AbsentFiles) {
//				produce a nag message in the PR
//			}
//		}
//	}
type nagRecord struct {
	config.RecordBase

	// MatchTitle represents content that must be in the PR's title
	MatchTitle []string // regexes

	// MatchBody represents content that must be in the PR's body
	MatchBody []string // regexes

	// MatchFiles represents files that must be in the PR
	MatchFiles []string // regexes

	// AbsentFiles represents files that must not be in the PR
	AbsentFiles []string // regexes

	// The message to inject when any of the Match* expressions match and none of the Absent* expressions do.
	Message string
}

func init() {
	config.RegisterType(recordType, config.MultiplePerRepo, func() config.Record {
		return new(nagRecord)
	})
}
<commit_msg>Run `make gen` with Go 1.19 to format files (#539)<commit_after>// Copyright 2019 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package nagger

import (
	"istio.io/bots/policybot/pkg/config"
)

const recordType = "nag"

// Nag expresses some matching conditions against a PR, along with a message to inject into a PR
// when it matches.
//
// The model is for a given PR:
//
//	if (PR title matches any of NatchTitle) || (PR body matches any of MatchBody) {
//		if (PR files match any of MatchFiles) {
//			if (PR does not match any of AbsentFiles) {
//				produce a nag message in the PR
//			}
//		}
//	}
type nagRecord struct {
	config.RecordBase

	// MatchTitle represents content that must be in the PR's title
	MatchTitle []string // regexes

	// MatchBody represents content that must be in the PR's body
	MatchBody []string // regexes

	// MatchFiles represents files that must be in the PR
	MatchFiles []string // regexes

	// AbsentFiles represents files that must not be in the PR
	AbsentFiles []string // regexes

	// The message to inject when any of the Match* expressions match and none of the Absent* expressions do.
	Message string
}

func init() {
	config.RegisterType(recordType, config.MultiplePerRepo, func() config.Record {
		return new(nagRecord)
	})
}
<|endoftext|>


<commit_before>package genflowtypes

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
)

type config struct {
	alwaysQualifyTypeNames bool
	embedEnums             bool
}

type FlowType interface {
	FlowType() string
}
type NamedFlowType interface {
	FlowType
	FlowTypeName() string
}

type simpleFlowType string

func (s simpleFlowType) FlowType() string { return string(s) }

type repeatedFlowType struct {
	t FlowType
}

func (r repeatedFlowType) FlowType() string { return r.t.FlowType() + "[]" }

type namedFlowType struct {
	Name string
	Type FlowType
}

func (t *namedFlowType) FlowType() string {
	return t.Type.FlowType()
	return fmt.Sprintf("%s = %s", t.Name, t.Type.FlowType())
}
func (t *namedFlowType) FlowTypeName() string {
	return t.Name
}

type objectFlowType struct {
	Fields []NamedFlowType
}

func (t *objectFlowType) FlowType() string {
	fields := []string{}
	for _, f := range t.Fields {
		fields = append(fields, fmt.Sprintf("  %s?: %s", f.FlowTypeName(), f.FlowType()))
	}
	return fmt.Sprintf("{\n%s\n}", strings.Join(fields, ",\n"))
}

func (cfg config) fqmnToType(fqmn string, registry *descriptor.Registry) (FlowType, error) {
	m, err := registry.LookupMsg("", fqmn)
	if err != nil {
		return nil, err
	}
	return cfg.messageToFlowType(m, registry)
}

func (cfg config) fieldToType(f *descriptor.Field, reg *descriptor.Registry) (NamedFlowType, error) {
	// FieldMessage
	var fieldType FlowType = simpleFlowType("any")
	switch f.GetType() {
	case pbdescriptor.FieldDescriptorProto_TYPE_DOUBLE:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_FLOAT:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_INT64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_UINT64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_INT32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_FIXED64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_FIXED32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_UINT32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SINT32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SINT64:
		fieldType = simpleFlowType("number")
	case pbdescriptor.FieldDescriptorProto_TYPE_BOOL:
		fieldType = simpleFlowType("boolean")
	case pbdescriptor.FieldDescriptorProto_TYPE_STRING:
		fieldType = simpleFlowType("string")
	case pbdescriptor.FieldDescriptorProto_TYPE_GROUP:
		fieldType = simpleFlowType("any") // ?
	case pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE:
		// TODO: should resolve type name relative to this type
		ft, err := reg.LookupMsg("", f.GetTypeName())
		if err != nil {
			return nil, err
		}
		fieldType = simpleFlowType(cfg.messageTypeName(ft))
	case pbdescriptor.FieldDescriptorProto_TYPE_BYTES:
		fieldType = simpleFlowType("string") // could be more correct
	case pbdescriptor.FieldDescriptorProto_TYPE_ENUM:
		e, err := reg.LookupEnum("", f.GetTypeName())
		if err != nil {
			return nil, err
		}

		if cfg.embedEnums {
			fieldType, err = cfg.enumToFlowType(e, reg)
			if err != nil {
				return nil, err
			}
		} else {
			name := cfg.enumTypeName(e)
			fieldType = simpleFlowType(name)
		}
	}
	if f.GetLabel() == pbdescriptor.FieldDescriptorProto_LABEL_REPEATED {
		fieldType = repeatedFlowType{fieldType}
	}
	return &namedFlowType{Name: f.GetName(), Type: fieldType}, nil
}

func (cfg config) messageToFlowType(m *descriptor.Message, reg *descriptor.Registry) (FlowType, error) {
	t := &objectFlowType{Fields: []NamedFlowType{}}
	for _, f := range m.Fields {
		field, err := cfg.fieldToType(f, reg)
		if err != nil {
			return nil, err
		}
		t.Fields = append(t.Fields, field)
	}
	return &namedFlowType{Name: cfg.messageTypeName(m), Type: t}, nil
}

func (cfg config) enumTypeName(e *descriptor.Enum) string {
	name := strings.Replace(e.FQEN(), ".", "", -1)
	if !cfg.alwaysQualifyTypeNames {
		if strings.HasPrefix(name, e.File.GoPkg.Name) {
			name = name[len(e.File.GoPkg.Name):]
		}
	}
	return name
}

func (cfg config) messageTypeName(m *descriptor.Message) string {
	name := strings.Replace(m.FQMN(), ".", "", -1)
	if !cfg.alwaysQualifyTypeNames {
		if strings.HasPrefix(name, m.File.GoPkg.Name) {
			name = name[len(m.File.GoPkg.Name):]
		}
	}
	return name
}

func (cfg config) enumToFlowType(e *descriptor.Enum, reg *descriptor.Registry) (FlowType, error) {
	options := []string{}
	for _, v := range e.Value {
		options = append(options, fmt.Sprintf(`"%s"`, v.GetName()))
	}
	name := cfg.enumTypeName(e)
	return &namedFlowType{
		Name: name,
		Type: simpleFlowType(strings.Join(options, " | ")),
	}, nil
}

func generateFlowTypes(file *descriptor.File, registry *descriptor.Registry, qualifyTypes bool, embedEnums bool) (string, error) {
	result := []FlowType{}
	f, err := registry.LookupFile(file.GetName())
	if err != nil {
		return "", err
	}
	cfg := config{
		alwaysQualifyTypeNames: qualifyTypes,
		embedEnums:             embedEnums,
	}
	for _, enum := range f.Enums {
		t, err := cfg.enumToFlowType(enum, registry)
		if err != nil {
			return "", err
		}
		result = append(result, t)
	}
	for _, message := range f.Messages {
		t, err := cfg.messageToFlowType(message, registry)
		if err != nil {
			return "", err
		}
		result = append(result, t)
	}

	buf := new(bytes.Buffer)
	tmpl, err := template.New("").Parse("/* @flow */\n{{range .}}export type {{.FlowTypeName}} = {{.FlowType}};\n\n{{end}}")
	if err != nil {
		return "", err
	}
	err = tmpl.Execute(buf, result)
	if err != nil {
		return "", err
	}
	return string(buf.Bytes()), nil
}
<commit_msg>use Array style<commit_after>package genflowtypes

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
)

type config struct {
	alwaysQualifyTypeNames bool
	embedEnums             bool
}

type FlowType interface {
	FlowType() string
}
type NamedFlowType interface {
	FlowType
	FlowTypeName() string
}

type simpleFlowType string

func (s simpleFlowType) FlowType() string { return string(s) }

type repeatedFlowType struct {
	t FlowType
}

func (r repeatedFlowType) FlowType() string { return fmt.Sprintf("Array<%s>", r.t.FlowType()) }

type namedFlowType struct {
	Name string
	Type FlowType
}

func (t *namedFlowType) FlowType() string {
	return t.Type.FlowType()
	return fmt.Sprintf("%s = %s", t.Name, t.Type.FlowType())
}
func (t *namedFlowType) FlowTypeName() string {
	return t.Name
}

type objectFlowType struct {
	Fields []NamedFlowType
}

func (t *objectFlowType) FlowType() string {
	fields := []string{}
	for _, f := range t.Fields {
		fields = append(fields, fmt.Sprintf("  %s?: %s", f.FlowTypeName(), f.FlowType()))
	}
	return fmt.Sprintf("{\n%s\n}", strings.Join(fields, ",\n"))
}

func (cfg config) fqmnToType(fqmn string, registry *descriptor.Registry) (FlowType, error) {
	m, err := registry.LookupMsg("", fqmn)
	if err != nil {
		return nil, err
	}
	return cfg.messageToFlowType(m, registry)
}

func (cfg config) fieldToType(f *descriptor.Field, reg *descriptor.Registry) (NamedFlowType, error) {
	// FieldMessage
	var fieldType FlowType = simpleFlowType("any")
	switch f.GetType() {
	case pbdescriptor.FieldDescriptorProto_TYPE_DOUBLE:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_FLOAT:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_INT64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_UINT64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_INT32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_FIXED64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_FIXED32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_UINT32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED64:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SINT32:
		fallthrough
	case pbdescriptor.FieldDescriptorProto_TYPE_SINT64:
		fieldType = simpleFlowType("number")
	case pbdescriptor.FieldDescriptorProto_TYPE_BOOL:
		fieldType = simpleFlowType("boolean")
	case pbdescriptor.FieldDescriptorProto_TYPE_STRING:
		fieldType = simpleFlowType("string")
	case pbdescriptor.FieldDescriptorProto_TYPE_GROUP:
		fieldType = simpleFlowType("any") // ?
	case pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE:
		// TODO: should resolve type name relative to this type
		ft, err := reg.LookupMsg("", f.GetTypeName())
		if err != nil {
			return nil, err
		}
		fieldType = simpleFlowType(cfg.messageTypeName(ft))
	case pbdescriptor.FieldDescriptorProto_TYPE_BYTES:
		fieldType = simpleFlowType("string") // could be more correct
	case pbdescriptor.FieldDescriptorProto_TYPE_ENUM:
		e, err := reg.LookupEnum("", f.GetTypeName())
		if err != nil {
			return nil, err
		}

		if cfg.embedEnums {
			fieldType, err = cfg.enumToFlowType(e, reg)
			if err != nil {
				return nil, err
			}
		} else {
			name := cfg.enumTypeName(e)
			fieldType = simpleFlowType(name)
		}
	}
	if f.GetLabel() == pbdescriptor.FieldDescriptorProto_LABEL_REPEATED {
		fieldType = repeatedFlowType{fieldType}
	}
	return &namedFlowType{Name: f.GetName(), Type: fieldType}, nil
}

func (cfg config) messageToFlowType(m *descriptor.Message, reg *descriptor.Registry) (FlowType, error) {
	t := &objectFlowType{Fields: []NamedFlowType{}}
	for _, f := range m.Fields {
		field, err := cfg.fieldToType(f, reg)
		if err != nil {
			return nil, err
		}
		t.Fields = append(t.Fields, field)
	}
	return &namedFlowType{Name: cfg.messageTypeName(m), Type: t}, nil
}

func (cfg config) enumTypeName(e *descriptor.Enum) string {
	name := strings.Replace(e.FQEN(), ".", "", -1)
	if !cfg.alwaysQualifyTypeNames {
		if strings.HasPrefix(name, e.File.GoPkg.Name) {
			name = name[len(e.File.GoPkg.Name):]
		}
	}
	return name
}

func (cfg config) messageTypeName(m *descriptor.Message) string {
	name := strings.Replace(m.FQMN(), ".", "", -1)
	if !cfg.alwaysQualifyTypeNames {
		if strings.HasPrefix(name, m.File.GoPkg.Name) {
			name = name[len(m.File.GoPkg.Name):]
		}
	}
	return name
}

func (cfg config) enumToFlowType(e *descriptor.Enum, reg *descriptor.Registry) (FlowType, error) {
	options := []string{}
	for _, v := range e.Value {
		options = append(options, fmt.Sprintf(`"%s"`, v.GetName()))
	}
	name := cfg.enumTypeName(e)
	return &namedFlowType{
		Name: name,
		Type: simpleFlowType(strings.Join(options, " | ")),
	}, nil
}

func generateFlowTypes(file *descriptor.File, registry *descriptor.Registry, qualifyTypes bool, embedEnums bool) (string, error) {
	result := []FlowType{}
	f, err := registry.LookupFile(file.GetName())
	if err != nil {
		return "", err
	}
	cfg := config{
		alwaysQualifyTypeNames: qualifyTypes,
		embedEnums:             embedEnums,
	}
	for _, enum := range f.Enums {
		t, err := cfg.enumToFlowType(enum, registry)
		if err != nil {
			return "", err
		}
		result = append(result, t)
	}
	for _, message := range f.Messages {
		t, err := cfg.messageToFlowType(message, registry)
		if err != nil {
			return "", err
		}
		result = append(result, t)
	}

	buf := new(bytes.Buffer)
	tmpl, err := template.New("").Parse("/* @flow */\n{{range .}}export type {{.FlowTypeName}} = {{.FlowType}};\n\n{{end}}")
	if err != nil {
		return "", err
	}
	err = tmpl.Execute(buf, result)
	if err != nil {
		return "", err
	}
	return string(buf.Bytes()), nil
}
<|endoftext|>


<commit_before>/*

Copyright 2017 Continusec Pty Ltd

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 safeadmin

import (
	"encoding/hex"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/net/context"
)

// FilesystemPersistence write key/values to the specified directory.
type FilesystemPersistence struct {
	// Dir is a path to the directory that contains the data
	Dir string

	// Immutable means be read-only
	Immutable bool
}

// Load returns value if found, nil otherwise
func (f *FilesystemPersistence) Load(ctx context.Context, key []byte) ([]byte, error) {
	path := filepath.Join(f.Dir, hex.EncodeToString(key))
	log.Printf("Loading from disk: %s...", path)
	rv, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, ErrStorageKeyNotFound
		}
		return nil, err
	}
	return rv, nil
}

// Save sets value, as atomically as we can. File will be saved with default umask
// This persistence layer ignores the TTL, meaning that keys will be retained indefinitely to allow out of band decryption via breakglass tool
func (f *FilesystemPersistence) Save(ctx context.Context, key, value []byte, ttl time.Time) error {
	if f.Immutable {
		log.Println("Persistence layer is configured as immutable, yet tool is trying to write to it")
		return ErrInvalidConfig
	}

	path := filepath.Join(f.Dir, hex.EncodeToString(key))
	log.Printf("Saving to disk: %s...", path)

	// Create dir if not exists
	_, err := os.Stat(f.Dir)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(f.Dir, 0700)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	// Create temp file
	tf, err := ioutil.TempFile(f.Dir, "temp-")
	if err != nil {
		return err
	}

	// Write it out
	_, err = tf.Write(value)
	if err != nil {
		tf.Close()           // ignore failure
		os.Remove(tf.Name()) // ignore failure
		return err
	}

	// Close it
	err = tf.Close()
	if err != nil {
		os.Remove(tf.Name()) // ignore failure
		return err
	}

	// Rename to correct filename - while not guaranteed by Golang to be atomic, apparently POSIX does on *nix systems
	err = os.Rename(tf.Name(), path)
	if err != nil {
		os.Remove(tf.Name()) // ignore failure
		return err
	}

	return nil
}
<commit_msg>Remove left over debug statements<commit_after>/*

Copyright 2017 Continusec Pty Ltd

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 safeadmin

import (
	"encoding/hex"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/net/context"
)

// FilesystemPersistence write key/values to the specified directory.
type FilesystemPersistence struct {
	// Dir is a path to the directory that contains the data
	Dir string

	// Immutable means be read-only
	Immutable bool
}

// Load returns value if found, nil otherwise
func (f *FilesystemPersistence) Load(ctx context.Context, key []byte) ([]byte, error) {
	rv, err := ioutil.ReadFile(filepath.Join(f.Dir, hex.EncodeToString(key)))
	if err != nil {
		if os.IsNotExist(err) {
			return nil, ErrStorageKeyNotFound
		}
		return nil, err
	}
	return rv, nil
}

// Save sets value, as atomically as we can. File will be saved with default umask
// This persistence layer ignores the TTL, meaning that keys will be retained indefinitely to allow out of band decryption via breakglass tool
func (f *FilesystemPersistence) Save(ctx context.Context, key, value []byte, ttl time.Time) error {
	if f.Immutable {
		log.Println("Persistence layer is configured as immutable, yet tool is trying to write to it")
		return ErrInvalidConfig
	}

	// Create dir if not exists
	_, err := os.Stat(f.Dir)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(f.Dir, 0700)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	// Create temp file
	tf, err := ioutil.TempFile(f.Dir, "temp-")
	if err != nil {
		return err
	}

	// Write it out
	_, err = tf.Write(value)
	if err != nil {
		tf.Close()           // ignore failure
		os.Remove(tf.Name()) // ignore failure
		return err
	}

	// Close it
	err = tf.Close()
	if err != nil {
		os.Remove(tf.Name()) // ignore failure
		return err
	}

	// Rename to correct filename - while not guaranteed by Golang to be atomic, apparently POSIX does on *nix systems
	err = os.Rename(tf.Name(), filepath.Join(f.Dir, hex.EncodeToString(key)))
	if err != nil {
		os.Remove(tf.Name()) // ignore failure
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/signal"
	"fmt"
	"github.com/garyburd/redigo/redis"
)

const (
	 redisAddress string = "127.0.0.1:6379"
)

var (
	bot CoreBot
	redisConn redis.Conn
	redisSub redis.PubSubConn
)

func main() {

	bot = NewCoreBot()
	defer bot.Close()

	// catch interrupts so we close GPIO on Ctrl-C
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	go func() {
		for _ = range signalChan {
			fmt.Println("Received an interrupt, stopping...")
			bot.Close()
			os.Exit(0)
		}
	}()

	redisConn, err := redis.Dial("tcp", redisAddress)
	if err != nil {
		panic(err)
	}
	defer redisConn.Close()

	redisSubConn, err := redis.Dial("tcp", redisAddress)
	if err != nil {
		panic(err)
	}
	redisSub = redis.PubSubConn{Conn: redisSubConn}
	defer redisSub.Close()

	fmt.Println("Arduino status: ", string(bot.ArduinoStatus()))

	redisSub.Subscribe("testchan")

	fmt.Println("Entering main loop")
	for {
		switch msg := redisSub.Receive().(type) {
		case redis.Message:
			fmt.Println("got redis message: ", string(msg.Data))
		case error:
			panic(msg)
		default:
			fmt.Println("default")
		}
	}
}
<commit_msg>goroutine redis subscription and main go loop<commit_after>package main

import (
	"os"
	"os/signal"
	"fmt"
	"github.com/garyburd/redigo/redis"
)

const (
	 redisAddress string = "127.0.0.1:6379"
)

var (
	bot CoreBot
)

func signalHandler(signalChan chan os.Signal) {
	for _ = range signalChan {
		fmt.Println("Received an interrupt, stopping...")
		bot.Close()
		os.Exit(0)
	}
}

func newRedisConnection(redisAddress string) redis.Conn {
	c, err := redis.Dial("tcp", redisAddress)
	if err != nil {
		panic(err)
	}
	return c
}

func listenToSubscriptions(subChan chan string, redisSubConn redis.PubSubConn) {
	for {
		switch msg := redisSubConn.Receive().(type) {
		case redis.Message:
			subChan <- string(msg.Data)
		case error:
			panic(msg)
		}
	}
}

func main() {

	bot = NewCoreBot()
	defer bot.Close()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	go signalHandler(signalChan)

	redisConn := newRedisConnection(redisAddress)
	defer redisConn.Close()

	redisConn2 := newRedisConnection(redisAddress)
	redisSubConn := redis.PubSubConn{Conn: redisConn2}
	defer redisSubConn.Close()

	fmt.Println("Arduino status: ", string(bot.ArduinoStatus()))

	redisSubConn.Subscribe("testchan")

	fmt.Println("Entering main loop")

	subChan := make(chan string)
	go listenToSubscriptions(subChan, redisSubConn)
	for {
		select {
		case data := <-subChan:
			fmt.Println("received message on subchan: ", data)
		}
	}
}
<|endoftext|>


<commit_before>package core

import (
	"bytes"

	"log"
	"net/http"

	"gopkg.in/simversity/gottp.v2/utils"

	"gopkg.in/bulletind/khabar.v1/db"
	"gopkg.in/bulletind/khabar.v1/dbapi/saved_item"
)

const PARSE_URL = "https://api.parse.com/1/push"

const PUSH_SOUND = "default"

func pushHandler(
	item *db.PendingItem,
	text string,
	settings map[string]interface{},
) {
	log.Println("Sending Push Notification...")

	application_id, ok := settings["parse_application_id"].(string)
	if !ok {
		log.Println("parse_application_id is a required parameter.")
		return
	}

	api_key, ok := settings["parse_rest_api_key"].(string)
	if !ok {
		log.Println("parse_rest_api_key is a required parameter.")
		return
	}

	subject, ok := item.Context["subject"].(string)
	if !ok || subject == "" {
		subject = item.Topic
	}

	body := map[string]interface{}{}
	body["alert"] = subject
	body["message"] = text
	body["entity"] = item.Entity
	body["organization"] = item.Organization
	body["app_name"] = item.AppName
	body["topic"] = item.Topic
	body["created_on"] = item.CreatedOn
	body["sound"] = PUSH_SOUND

	data := map[string]interface{}{}
	data["data"] = body
	data["channels"] = []string{"USER_" + item.User}

	var jsonStr = utils.Encoder(&data)

	req, err := http.NewRequest("POST", PARSE_URL, bytes.NewBuffer(jsonStr))

	req.Header.Set("X-Parse-Application-Id", application_id)
	req.Header.Set("X-Parse-REST-API-Key", api_key)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	saved_item.Insert(db.SavedPushCollection, &db.SavedItem{Data: data, Details: *item})

	log.Println("Push notification status:", resp.Status)
}
<commit_msg>incremenet badge on iOS<commit_after>package core

import (
	"bytes"

	"log"
	"net/http"

	"gopkg.in/simversity/gottp.v2/utils"

	"gopkg.in/bulletind/khabar.v1/db"
	"gopkg.in/bulletind/khabar.v1/dbapi/saved_item"
)

const PARSE_URL = "https://api.parse.com/1/push"

const PUSH_SOUND = "default"

func pushHandler(
	item *db.PendingItem,
	text string,
	settings map[string]interface{},
) {
	log.Println("Sending Push Notification...")

	application_id, ok := settings["parse_application_id"].(string)
	if !ok {
		log.Println("parse_application_id is a required parameter.")
		return
	}

	api_key, ok := settings["parse_rest_api_key"].(string)
	if !ok {
		log.Println("parse_rest_api_key is a required parameter.")
		return
	}

	subject, ok := item.Context["subject"].(string)
	if !ok || subject == "" {
		subject = item.Topic
	}

	body := map[string]interface{}{}
	body["alert"] = subject
	body["message"] = text
	body["entity"] = item.Entity
	body["organization"] = item.Organization
	body["app_name"] = item.AppName
	body["topic"] = item.Topic
	body["created_on"] = item.CreatedOn
	body["sound"] = PUSH_SOUND
	body["badge"] = "Increment"

	data := map[string]interface{}{}
	data["data"] = body
	data["channels"] = []string{"USER_" + item.User}

	var jsonStr = utils.Encoder(&data)

	req, err := http.NewRequest("POST", PARSE_URL, bytes.NewBuffer(jsonStr))

	req.Header.Set("X-Parse-Application-Id", application_id)
	req.Header.Set("X-Parse-REST-API-Key", api_key)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	saved_item.Insert(db.SavedPushCollection, &db.SavedItem{Data: data, Details: *item})

	log.Println("Push notification status:", resp.Status)
}
<|endoftext|>